浅谈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。关键步骤如下:

  1. 调用initLookupPath方法初始化请求映射路径
  2. 调用lookupHandlerMethod方法匹配RequestMappingInfo
  3. 将最佳匹配方法放入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方法解析:

  1. 使用tokenizePattern()将pattern分割成String数组
  2. 使用tokenizePath()处理请求path,基于/进行分割
  3. 无法直接获取到/字符

1.2.2 PathPattern解析

PathPattern解析流程:

  1. 根据/将URL拆分成多个PathElement对象
  2. 使用CaptureVariablePathElement的matches方法匹配
  3. 同样无法直接获取到/字符

1.3 PathPattern新增的{*spring}语法

{*path}可以匹配剩余所有path并获取完整内容:

@RequestMapping("/file/download/{*path}")
public void fileDownload(@PathVariable("path") String path, HttpServletResponse response) throws IOException {
    // ...
}

解析过程:

  1. 使用CaptureTheRestPathElement处理
  2. 通过pathToString方法组合各个PathElement内容
  3. 可以获取到/字符,如请求/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()方法处理逻辑:

  1. 检查路径是否以/\开头
  2. 处理连续的/
  3. 处理./../
    • 回溯目录直到找不到路径穿越符
    • 当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解析)

前提条件:

  1. 设置ALLOW_ENCODED_SLASH=true
  2. 使用URL编码的/(%2f)
  3. 路径穿越符数量不超过目录层数限制

示例:

/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处理特点:

  1. 处理../时会截断路径
  2. 如果路径以/结尾,可避免返回null
  3. 使用..代替../可绕过限制

示例:

/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 防护建议

  1. 输入验证

    • 严格校验文件名参数,限制为预期字符集
    • 使用白名单方式验证文件路径
  2. 路径处理

    • 使用Paths.get().normalize()规范化路径
    • 检查最终路径是否在允许的目录范围内
  3. 容器配置

    • 避免设置ALLOW_ENCODED_SLASH=true
    • 保持容器默认的安全配置
  4. 编码处理

    • 避免多次URL解码
    • 在验证后再进行解码操作
  5. 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的路径处理机制和容器特性,可以有效防御任意文件下载漏洞,确保文件下载功能的安全性。

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 属性中 示例代码: 当请求 /file/download/../../etc/passwd 时, path 参数值为 ../../etc/passwd 。 1.2 {pathVariable:正则表达式(可选)} 通过路径变量方式获取URI Path: 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并获取完整内容: 解析过程: 使用 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) 路径穿越符数量不超过目录层数限制 示例: HandlerMapping.PATH_ WITHIN_ HANDLER_ MAPPING_ ATTRIBUTE 可直接获取 ../../ ,但受限于Tomcat的跨目录处理: 需计算合理数量的 ../ 示例: /file/download/../../etc/passwd {* path}语法 同样可直接获取路径穿越符,受限于Tomcat处理: 3.2 Jetty环境利用 绕过限制的技巧 Jetty的 canonicalPath 处理特点: 处理 ../ 时会截断路径 如果路径以 / 结尾,可避免返回null 使用 .. 代替 ../ 可绕过限制 示例: {pathVariable:正则表达式(可选)} 需URL编码 / : 3.3 Undertow环境利用 Undertow限制较少: 直接使用 ../ 即可 示例: /file/download/../../etc/passwd 对于PathPattern解析,仍需URL编码 / 。 3.4 其他利用场景 当Controller层对文件名进行URL解码时,可能绕过更多限制: 0x04 防护建议 输入验证 : 严格校验文件名参数,限制为预期字符集 使用白名单方式验证文件路径 路径处理 : 使用 Paths.get().normalize() 规范化路径 检查最终路径是否在允许的目录范围内 容器配置 : 避免设置 ALLOW_ENCODED_SLASH=true 保持容器默认的安全配置 编码处理 : 避免多次URL解码 在验证后再进行解码操作 Spring配置 : 使用最新版本Spring Boot 考虑使用 PathPattern 替代 AntPathMatcher 示例安全代码: 通过深入理解Spring Web的路径处理机制和容器特性,可以有效防御任意文件下载漏洞,确保文件下载功能的安全性。