浅谈SpringWeb URI中的任意文件下载
字数 2557 2025-08-06 12:20:54
Spring Web URI中的任意文件下载漏洞分析与防护
0x00 前言
文件下载是Web应用中常见的业务功能。当文件名参数可控且系统未进行充分过滤时,可能导致任意文件下载漏洞。本文深入分析Spring Web框架中URI路径处理的机制,探讨不同场景下的漏洞利用方式及防护措施。
0x01 Spring Web中获取URI Path的常见方式
1.1 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE属性
在Spring中,请求处理流程会通过getHandlerInternal方法从request对象中获取请求path并找到对应的handlerMethod。关键步骤如下:
- 调用
initLookupPath方法初始化请求映射路径 - 调用
lookupHandlerMethod方法匹配RequestMappingInfo - 将最佳匹配方法放入request对象的
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE属性中
示例代码:
@RequestMapping("/file/download/**")
public void fileDownload(HttpServletResponse response, HttpServletRequest request) throws IOException {
String reqPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
String path = new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, reqPath);
// ...
}
当请求/file/download/../../etc/passwd时,path参数值为../../etc/passwd。
1.2 {pathVariable:正则表达式(可选)}
通过路径变量方式获取URI Path:
@RequestMapping("/file/download/{path:.*}")
public void fileDownload(@PathVariable("path") String path, HttpServletResponse response) throws IOException {
// ...
}
1.2.1 AntPathMatcher解析
AntPathMatcher通过doMatch方法解析:
- 使用
tokenizePattern()将pattern分割成String数组 - 使用
tokenizePath()处理请求path,基于/进行分割 - 无法直接获取到
/字符
1.2.2 PathPattern解析
PathPattern解析流程:
- 根据
/将URL拆分成多个PathElement对象 - 使用
CaptureVariablePathElement的matches方法匹配 - 同样无法直接获取到
/字符
1.3 PathPattern新增的{*spring}语法
{*path}可以匹配剩余所有path并获取完整内容:
@RequestMapping("/file/download/{*path}")
public void fileDownload(@PathVariable("path") String path, HttpServletResponse response) throws IOException {
// ...
}
解析过程:
- 使用
CaptureTheRestPathElement处理 - 通过
pathToString方法组合各个PathElement内容 - 可以获取到
/字符,如请求/file/../etc/passwd时path参数为../etc/passwd
0x02 利用限制分析
2.1 容器对特殊字符的处理
2.1.1 Tomcat对%2f和%2F的处理
Tomcat默认会拒绝URL编码的斜杠:
- 通过
UDecoder.convert方法检查 encodedSolidusHandling默认为REJECT- 需要设置
org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true才能允许
2.1.2 Tomcat对/../的跨目录处理
normalize()方法处理逻辑:
- 检查路径是否以
/或\开头 - 处理连续的
/ - 处理
./和../:- 回溯目录直到找不到路径穿越符
- 当index==0(即处理后的url为
/../)时返回400状态码 - 限制路径穿越符个数,与请求目录层数相关
2.2 Spring自身的处理
2.2.1 initLookupPath处理差异
- PathPattern解析:
- 不进行URL解码
- 通过URL编码可绕过
/获取限制
- AntPathMatcher解析:
- 会进行URL解码
- 无法通过编码方式获取
/
2.2.2 alwaysUseFullPath的影响
- Spring Boot ≤2.3.0:会对路径进行规范化处理
- Spring Boot >2.3.0:不进行规范化处理
0x03 漏洞利用方式
3.1 Tomcat环境利用
{pathVariable:正则表达式(可选)}(PathPattern解析)
前提条件:
- 设置
ALLOW_ENCODED_SLASH=true - 使用URL编码的
/(%2f) - 路径穿越符数量不超过目录层数限制
示例:
/file/download/..%2fetc%2fpasswd
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
可直接获取../../,但受限于Tomcat的跨目录处理:
- 需计算合理数量的
../ - 示例:
/file/download/../../etc/passwd
{*path}语法
同样可直接获取路径穿越符,受限于Tomcat处理:
/file/download/../../etc/passwd
3.2 Jetty环境利用
绕过限制的技巧
Jetty的canonicalPath处理特点:
- 处理
../时会截断路径 - 如果路径以
/结尾,可避免返回null - 使用
..代替../可绕过限制
示例:
/file/download/..//..//etc/passwd
{pathVariable:正则表达式(可选)}
需URL编码/:
/file/download/..%2f..%2fetc%2fpasswd
3.3 Undertow环境利用
Undertow限制较少:
- 直接使用
../即可 - 示例:
/file/download/../../etc/passwd
对于PathPattern解析,仍需URL编码/。
3.4 其他利用场景
当Controller层对文件名进行URL解码时,可能绕过更多限制:
String decodedPath = URLDecoder.decode(path, "UTF-8");
0x04 防护建议
-
输入验证:
- 严格校验文件名参数,限制为预期字符集
- 使用白名单方式验证文件路径
-
路径处理:
- 使用
Paths.get().normalize()规范化路径 - 检查最终路径是否在允许的目录范围内
- 使用
-
容器配置:
- 避免设置
ALLOW_ENCODED_SLASH=true - 保持容器默认的安全配置
- 避免设置
-
编码处理:
- 避免多次URL解码
- 在验证后再进行解码操作
-
Spring配置:
- 使用最新版本Spring Boot
- 考虑使用
PathPattern替代AntPathMatcher
示例安全代码:
@RequestMapping("/file/download/{*path}")
public void fileDownload(@PathVariable("path") String path, HttpServletResponse response) throws IOException {
// 规范化路径
Path requestedPath = Paths.get("/base/dir").resolve(path).normalize();
// 验证是否在允许的目录内
if (!requestedPath.startsWith("/base/dir")) {
throw new IllegalArgumentException("Invalid file path");
}
// 后续文件操作...
}
通过深入理解Spring Web的路径处理机制和容器特性,可以有效防御任意文件下载漏洞,确保文件下载功能的安全性。