news 2026/10/17 17:03:13

低代码平台权限绕过漏洞剖析:从路径解析差异到token鉴权缺失

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
低代码平台权限绕过漏洞剖析:从路径解析差异到token鉴权缺失

最近圈子里又在传某开源低代码平台的权限绕过漏洞,说是“最新版”也没能挡住。我花了两个晚上把整个鉴权链路翻了一遍,除了社区里已经讨论的那些路径绕过手法之外,还真让我挖到一条不太一样的绕过路径。这条路径不是简单的“拦截器没匹配上”,而是出在“token有效但权限校验缺失”这个更隐蔽的位置。

这篇东西我会从平台鉴权架构入手,把漏洞根因拆开讲清楚,再把我发现新绕过路径的完整过程、复现步骤、修复方案和验证方法都摆出来。适合正在做代码审计的安全工程师、用低代码平台搭内部系统的开发,以及想搞明白“为什么修了拦截器还是被绕过”的运维同学。

1. 先从平台架构和鉴权设计说起

1.1 为什么低代码平台最容易出权限绕过

低代码平台的核心卖点是快。表单配一配、接口拖一拖,一套后台管理就出来了。这种快速交付模式在安全上天然会埋雷,最典型的就是把权限模型做得很“薄”。

多数这类平台的权限控制思路是这样的:前端根据用户角色控制菜单显隐,后端在接口层做URL和角色的匹配。听起来没什么问题,但实际落地时,后端的权限判断往往只做到“登录态校验”这一步,也就是说只要你带着一个有效token,后端就认为这个请求是合法的,至于这个token对应的人到底能不能访问这个接口,很多时候根本没查。

为什么会出现这种情况?因为低代码平台的接口数量太多、迭代太快,如果每个接口都精细配置权限,开发效率会急剧下降。于是平台方普遍选择“统一拦截器 + 少量放行名单”的方案:拦截器负责校验token,放行名单里的路径不用校验token。问题恰恰就出在这个“放行名单”和“统一拦截”之间的空档里。

我审计的这个平台(下称X-Platform)就是典型代表。它是标准的Spring Boot架构,鉴权用的是Shiro + JWT,核心逻辑写在一个全局拦截器里。所有请求进来先过拦截器,检查请求头是否带token、token是否有效,如果有效就放行到业务接口。看起来链路很完整,但打开放行名单之后,你会发现事情没那么简单。

1.2 目标平台的权限拦截链路

先模拟一下X-Platform的鉴权配置。登录接口、图形验证码接口、Swagger接口文档、Druid监控页、Actuator健康检查、静态资源、以及几个“内部使用”的报表导出接口,全部被加进了放行名单。关键代码如下:

@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .addPathPatterns("/**") .excludePathPatterns( "/sys/login", "/sys/captcha", "/swagger-ui/**", "/v2/api-docs", "/druid/**", "/actuator/**", "/static/**", "/report/export/**", "/inner/token" ); }

这段配置单看似乎没什么大毛病,放行的都是些常规路径。但真正的问题是:Shiro从1.x升级到2.x之后,默认的路径匹配规则变了,而平台的业务代码里还残留着大量老式写法。更麻烦的是,新版框架里有两个拦截器在同时工作——Shiro的FilterChain和SpringMVC的Interceptor,它们对“同一个请求路径”的解析结果可能完全不一样。

这个不一致,就是所有绕过手法的温床。

2. 漏洞根因:从路径规范化到鉴权不一致

2.1 同一请求,两套解析结果

先解释一下什么叫“两套解析结果”。你在浏览器里输入一个URL,这个URL不是直接交给业务代码处理的,要先经过Web容器(比如Tomcat)的解析,再经过框架的路由匹配,最后才能到Controller。问题是,容器解析URL的规则和框架匹配路径的规则并不是100%一致的。

Tomcat在解析请求路径时有一个比较特殊的机制:它会把分号(;)后面的内容当作“路径参数”剥离开。举个例子:

请求:/sys/user/list;foo=bar 容器解析后认为实际路径是:/sys/user/list

为什么容器要这么干?因为Servlet规范里允许在路径段上附加参数,最常见的场景就是/xxx;jsessionid=abc123这种老式会话跟踪写法。Tomcat老老实实地执行了规范,把分号后面的部分拆掉,然后拿着/sys/user/list去匹配Controller。

但框架这边呢?Spring的AntPathMatcher在处理/sys/user/list;foo=bar这个字符串时,它会把这个路径当成一个整体去跟/sys/user/list做匹配,结果就是不匹配。于是拦截器认为这是一个“没有匹配到受保护路径”的请求,直接放行了。而Tomcat已经把真实路径解析出来了,请求最终打到了/sys/user/list对应的业务方法上。

这就形成了经典的鉴权绕过:拦截器看到的是一串“无害”的乱码路径,Web层看到的却是真实的敏感接口。

2.2 绕过手法分类

我在实际测试中整理了一下,这类路径解析差异导致的绕过手法大概有这几类:

手法类型请求路径示例拦截器视角Web层视角杀伤力
分号参数/sys/user/list;foo=1不匹配,放行匹配 /sys/user/list高
URL编码/sys/user/%6cist不匹配解码后匹配 /list高
大小写变体/Sys/User/List不匹配(区分大小写)匹配(不区分)中
双斜杠//sys//user/list不匹配归一化后匹配中
路径穿越/sys/user/../user/list不匹配规范化后匹配高
末尾斜杠/sys/user/list/不匹配匹配中

注意,不是每种手法在每个版本上都有效,因为框架升级会影响匹配规则。但分号和URL编码这两类,在我测的X-Platform多个版本上都稳定生效。

2.3 为什么拦截器匹配不上而后端能处理

这里再往深挖一层。AntPathMatcher是一个纯字符串匹配器,它不会去理解URL的语义。它拿到原始请求路径之后,就直接跟配置的模式做比对。而容器的路径处理流程里,有一步叫做“路径规范化”,这一步会做三件事:解码百分号编码、移除.和..路径段、剥离分号参数。

也就是说,容器在“语义层”处理了URL,而拦截器在“字符串层”处理URL。这两层之间出现认知偏差,就给了攻击者操作空间。

所以我在最初审计时一眼就看出了X-Platform的问题:它把Shiro的FilterChain配置和SpringMVC的Interceptor混用了,而且两边对路径的校验规则还不一样。Shiro那边已经升级了更严格匹配机制,但SpringMVC这边还开着老式宽松匹配。一套鉴权体系里同时存在两套标准,那么攻击者只需要找到“对不上”的位置,就能钻进空档。

3. 新绕过路径的发现过程

3.1 翻代码时的怀疑点

社区里已经公开的漏洞分析大多集中在路径规范化绕过上,也就是上文说的那些手法。但我审计的时候发现,X-Platform在最新版里做了一个改动:他们把旧版/**统一拦截改成了“细化白名单”模式,并且新增了一个叫“内部鉴权开关”的配置项。

这个配置项的用途官方说法是“为了单元测试和内部服务间调用方便”,默认是关闭的。但代码里有一条分支让我非常在意:

if (authSwitchConfig.isInnerEnabled()) { // 直接从请求头获取内部调用凭证,不再走完整鉴权 String innerToken = request.getHeader("X-Inner-Token"); if (StringUtils.hasText(innerToken) && validateInnerToken(innerToken)) { return true; // 直接放行 } }

这段代码在/inner/token这个接口的调用链路上,看起来是专门给“内部服务”用的捷径。如果配置项被打开,那么只要请求头里带上一个内部token,就能跳过完整鉴权。

这里就出现了一个值得深挖的点:获取内部token的接口/inner/token本身就在放行名单里。那我是不是可以用它先换一个合法的内部token,然后带着这个token去访问任何敏感接口,而平台后端根本不校验这个token的访问范围?

3.2 绕过链:甲接口泄露token,乙接口接受另一个来源

我顺着这个思路继续追,又发现了一个更隐蔽的问题。即便不考虑那个“内部鉴权开关”,X-Platform的token校验逻辑也有个大毛病:它在解析JWT之后,只校验了签名和过期时间,完全没有校验这个token的“使用场景”。

说得再具体点,X-Platform其实存在两种token。一种是用户登录后拿到的登录token,权限范围是当前用户的角色权限;另一种是从/inner/token获取的内部凭证,本来只应该用于调用那几个报表导出接口。但在后端Controller里,很多敏感接口(比如用户列表、角色配置、字典管理)的权限校验注解都只写了@RequiresAuthentication,意思就是“只要登录了就行”。内部token同样满足“已登录”的条件。

于是绕过链就形成了:

  • 未登录的用户先访问公开的/inner/token接口,获取一个合法的内部token。
  • 拿着这个内部token去访问/sys/user/list等敏感接口。
  • 后端只校验token是否有效、是否过期,不校验token的使用范围和角色的接口权限。
  • 请求返回200,数据“顺利”流出。

这一步做完之后,我意识到这已经不是单纯的路径绕过了。它是一个“任意token即可访问任意接口”的越权漏洞,危害等级比路径绕过要高得多,因为攻击者甚至不需要去凑路径变体,只需要一个合法凭证。

3.3 具体触发流程

下面是在本地授权测试环境复现的完整流程。为了不提供可直接打真实系统的完整利用链,这里只描述关键触发逻辑,接口名和参数都做了脱敏处理。

第一步,调用公开的获取内部凭证接口:

POST /inner/token HTTP/1.1 Host: target.local Content-Type: application/json {"appId":"x-inner-app","timestamp":1720000000000}

服务端返回一个JWT格式的token:

{ "code": 200, "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbm5lcl9hcHAiLCJzY29wZSI6ImlubmVyIiw...", "expire": 7200 }

第二步,携带这个token访问敏感接口:

GET /sys/user/list HTTP/1.1 Host: target.local Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

正常情况下,这个请求应该被拒绝,因为内部token根本没有访问用户列表的权限。但实际返回的是:

{ "code": 200, "data": { "total": 128, "records": [...] } }

也就是说,服务端只认了“token有效”这个事实,完全没有去核对token的scope和接口的权限要求。拿到这个结果之后,我又顺手测了角色配置、部门管理、操作日志等接口,全部命中。单这一个点,就能把平台内部的组织架构、账号列表、角色权限配置全量拖走。

3.4 新绕过与传统绕过的差异

为了让读者更好理解这条新路径“新”在哪里,我把它和传统路径绕过放在一起对比:

对比维度传统路径绕过新发现的绕过
触发点拦截器与容器路径解析不一致鉴权逻辑缺少token作用域校验
利用难度需要构造路径变体,部分场景还依赖中间件版本只需调用公开接口换一个合法token
所需权限未登录未登录
隐蔽性中(日志里能看到畸形路径)高(所有请求看起来都“正常”走完鉴权)
影响面单个接口或单类接口被绕过所有“只校验登录态”的接口全部暴露
修复难度需统一路径解析规则需在鉴权逻辑中补齐权限范围校验

对比下来很直观:传统绕过本质上是在打“鉴权系统认不出这个请求”的漏洞,而新绕过是在打“鉴权系统认出了请求,但没判断这个人有没有资格做这件事”的漏洞。很多团队在修复前者时,会集中精力去调过滤规则、规范URL编码,却忽略了后者——因为后者的代码位置更深,藏在业务逻辑里,不跟请求进去根本看不到。

4. 修复方案与验证

4.1 后端修复:统一鉴权过滤器

先说结论:修复这类问题,最有效的手段是把鉴权逻辑往前移,放到Spring的Filter层完成,并且对路径做统一的规范化处理,不要在Interceptor和Shiro两边各做一套。

推荐的做法是新增一个OncePerRequestFilter,在这个过滤器里先做三件事:

  1. 拿到原始请求路径,做一次规范化处理。
  2. 统一处理分号参数、URL编码、路径穿越、大小写归一化。
  3. 把规范化后的路径传给后续的权限校验逻辑。

规范化路径的工具方法示例如下:

public static String normalizeUri(HttpServletRequest request) { String uri = request.getRequestURI(); try { // 1. 解码URL编码 uri = URLDecoder.decode(uri, StandardCharsets.UTF_8.name()); // 2. 移除分号及后面的路径参数 uri = uri.replaceAll(";.*?(?=/|$)", ""); // 3. 处理路径穿越 Path path = Paths.get(uri).normalize(); uri = path.toString().replace("\\", "/"); } catch (Exception e) { // 解析失败直接返回原始uri,后续鉴权流程会拒绝 } return uri; }

然后在同一个过滤器里统一做三块校验:

  • 当前请求路径是否在白名单里。
  • token是否存在、签名是否有效、是否过期。
  • token携带的角色/scope是否覆盖当前接口的权限要求。

只有三层校验全部通过,请求才允许继续。这样就不会再出现“Shiro放行了、Interceptor没拦住”之类的空档,因为路径规范化和权限判断都集中在了一个执行链路上。

另外,那个“内部鉴权开关”必须单独处理。我给出的修复建议是:去掉“通过请求头传内部token直接放行”的分支,改为必须从注册中心获取调用方身份,并且调用方身份必须精确匹配目标接口的allow列表。任何情况下都不能因为开关打开就跳过权限范围校验。

4.2 验证方式

修复完之后,我一般会拿之前所有的绕过样本做回归测试。验证清单大概长这样:

验证项预期结果实际结果
未登录访问 /sys/user/list401通过
分号绕过 /sys/user/list;foo=1401通过
URL编码绕过 /sys/user/%6cist401通过
大小写绕过 /Sys/User/List401通过
内部token直接访问用户列表403通过
内部token访问报表导出接口200通过
正常用户访问自己有权限的接口200通过
正常用户访问无权限的接口403通过

我把这组测试用例做成了一个自动化脚本,用的就是常见的网络请求工具,跑一遍大概两分钟,能直接把所有已知绕过手法全部覆盖住。脚本逻辑并不复杂:枚举路径变形、枚举放行名单、检查响应码,如果发现预期要返回401/403的请求返回了200,就说明有绕过。

4.3 代码审计自检清单

最后给出一份代码审计自检清单,做完这些还不够,但至少能把大部分同类漏洞排掉:

  • [ ] 全局搜索addInterceptors和FilterRegistrationBean,检查所有放行路径。
  • [ ] 对所有放行路径逐一验证:是否真的不需要鉴权?是否可能泄露敏感数据?
  • [ ] 搜索excludePathPatterns、permitAll、antMatchers().permitAll(),全部列出并人工复核。
  • [ ] 搜索@RequiresAuthentication、@RequiresUser等注解,检查是否只校验了登录态,没有校验具体权限。
  • [ ] 检查JWT解析逻辑,确认解析之后是否校验了token的使用场景、角色、scope。
  • [ ] 检查路径解析相关代码,确认是否存在分号参数、URL编码、大小写导致的两套解析结果。

这份清单是我做Java Web代码审计时固定的起手式,每次都能捞出点东西。X-Platform这次的问题,就是清单第三项和第五项踩了雷。

5. 审计方法论:下次怎么快速找到这类漏洞

5.1 从配置和放行名单入手

很多人在代码审计时会犯一个错误:一头扎进业务代码里跟Controller,结果看了半天看不出问题。我的习惯是反着来,先看配置,再看过滤器和拦截器,最后才看业务逻辑。

为什么先看配置?因为配置是最容易留后门的地方。低代码平台的作者往往会在配置里加一些“测试入口”或者“内部接口”,初衷是方便自己调试,但上了生产环境忘了删,就成了最肥的突破口。放行名单里的每一个路径,都应该视为潜在攻击面,逐个过一遍“如果我不带token访问这个路径,会发生什么”。

5.2 关注“两套解析器”的差异

这是这类漏洞的核心命门。只要发现一套系统里同时存在两套路径匹配逻辑,就要立刻警觉。最常见的组合是Shiro/Spring Security的外层Filter加SpringMVC的Interceptor,两套规则对URL编码、分号、双斜杠的理解不同,就会产生认知偏差。

我自己的排查方法是:写一个简单的路径变形枚举脚本,把Java Web常见的容器路径解析差异都跑一遍,看哪些变形能突破拦截器。这个脚本我维护了好几年,里面已经积累了三十多种变形方式,基本覆盖Tomcat、Undertow、Jetty的常见差异。

5.3 关注“内网白名单”和“临时凭证”

从这次审计经验来看,新式绕过特别喜欢藏在两个地方:一个是“内部服务专用接口”,另一个是“临时凭证”。它们有一个共同点,都是为了图省事,在完整的鉴权链路上开了一口“后门井”。

审计时遇到这类代码,要追问三个问题:

  • 这个内部token是从哪个接口发出来的?那个接口是不是任何人都能访问?
  • token的有效期是多长?过期了之后是否有刷新机制?
  • 某个接口校验了token,但校验的是“token是谁签发的”,还是“token对应的人有没有权限做这件事”?这两者差别巨大。

我在X-Platform上挖到的新绕过,本质上就是在追第三个问题时发现的。代码里写了scope: inner的字段,但校验逻辑里根本没有读取这个字段,等于写了个装饰。

5.4 工具加持

手工测当然可以,但效率太低。我一般会配合Burp Suite做两件事:

一是把常用绕过payload做成Intruder字典,直接遍历所有关心的接口,看哪些路径变体能绕过去。

二是用响应对比功能,把“正常访问”和“绕过访问”的响应长度、状态码做对比。如果两个响应一样,就说明拦截器已经被打穿了。

另外有一个小技巧:如果平台用了JWT,可以把token解出来看它的payload里有什么字段。很多开发会把角色、权限范围写进token里,但后端解析出来之后完全没校验,这种问题用肉眼一眼就能看出来。

最后分享一点实际体会

这次审计让我最有感触的一点是:修复路径绕过神速,但修复权限范围缺失很慢。原因在于路径绕过是“一个点”的问题,改改过滤器、规范一下解析逻辑就行;而权限范围缺失是“一片”的问题,你得把所有标记了@RequiresAuthentication的接口全部过一遍,逐个补上真实的角色和scope校验。这个工程量在低代码平台上尤其大,因为接口太多了。

所以在跟开发团队沟通修复方案的时候,我的建议是分两步走:第一步,立刻在全局过滤器里加上路径规范化逻辑,把已知的绕过路径先堵死;第二步,梳理敏感接口清单,分批把“只要登录就能访问”的接口改成“必须有指定角色才能访问”。第一步治标,能快速止血;第二步治本,需要排期慢慢推。

另外还有一个小建议:审计低代码平台时,别只盯着框架本身的漏洞公告,一定要把平台自研的那层“二次封装”也翻一遍。很多平台为了方便业务方调用,会在框架之上再包一层鉴权逻辑,我最常遇到的问题就是:框架层已经校验过了,平台层又校验一次,而且两层的校验标准不一样。多一层封装就多一层差异,多一层差异就多一个绕过空间。

X-Platform这个案例如果后续扩展,我大概率会再去翻它暴露出来的接口文档,看看还有多少接口是“声明了权限但没真正校验”的状态。低代码平台里这类“纸面权限”远比想象中多,而且每个都可能是下一个安全公告的主角。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 17:02:48

从零基础到精通:20款网络安全工具全拆解

如果你正在了解网络安全这个方向,一定见过各种写着“XX个工具”的文章。看的时候觉得大开眼界,关掉页面之后真正会用的,可能一个都没有。我做了这么多年安全相关的项目,最深的感受是:工具不是越多越好,而是…

作者头像 李华
网站建设 2026/10/17 17:02:08

Postman接口自动化:Tests断言校验返回结果实战指南

1. 为什么我要把接口校验交给Tests断言2019年底到2020年初那段时间,前后端分离的项目越来越多,接口联调和回归测试的量一下子压了上来。当时我手里维护着十几个业务接口,每次后端改完代码,我都要打开Postman重新点一遍请求&#x…

作者头像 李华
网站建设 2026/10/17 17:01:17

crontab定时任务不生效?三步排查法+日志定位实战

先说个我亲历的场景:某天早上一到工位,运维群里就炸了——昨晚的数据库备份没跑,备份目录空空如也。我第一反应是执行 crontab -l 看配置,任务明明还在,语法也对,手动执行脚本也正常,但定时就是…

作者头像 李华
网站建设 2026/10/17 17:00:21

小新Yoga预装软件卸不掉?从备份到注册表的彻底清理指南

小新 / Yoga 系列应用卸不掉?异常软件强力卸载教程,附数据备份提醒!碰到小新或 Yoga 系列笔记本预装软件卸不掉的问题,确实挺闹心的。明明在应用列表里点了“卸载”,结果提示正在运行、需要权限,或者干脆点…

作者头像 李华
网站建设 2026/10/17 16:59:51

MIT-BIH心电数据解析与MATLAB绘图:从二进制格式到波形可视化全流程

简介:MIT-BIH ECG心电数据集与MATLAB绘图详解是一份面向心电图研究初学者与进阶用户的入门实操资源,聚焦临床心律失常数据读取与可视化分析。资源包内共4个文件,涵盖MAT格式心电数据、MATLAB绘图脚本、辅助脚本及数据说明文件,能帮…

作者头像 李华
网站建设 2026/10/17 16:59:49

wxid转二维码:用Python生成微信联系人二维码的完整方案

简介:工具包采用纯前端方案,可将微信用户个人标识wxid转换为带weixin://contacts/前缀的好友链接二维码,主要面向需要找回误删好友、或已知对方wxid但未保存聊天的普通用户,也适合客服人员和社群运营者临时应急使用。它借助qrcode…

作者头像 李华