九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

admin 2023年11月20日20:45:08评论10 views字数 8996阅读29分59秒阅读模式

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

·前言·

Java Spring是目前企业开发中使用较多的一种java开发框架,因此基于该框架的安全内容尤为重要。Secure Code Warrior编码实验室的Java Spring涉及到的安全问题有9个,分别为缺少功能级别的访问控制、不恰当的身份验证、日志记录和监控不足、SQL注入、明文存储密码、路径遍历、服务器请求伪造、XML外部实体(XXE)、任意文件上传。


因涉及内容较多,完整内容将会在本公众号拆分为多篇内容分别发出。本文为该系列的第六篇——安全问题六:路径遍历。


往期内容请查看Java Spring编码安全系列

Path traversal mitigation in Java Spring.

Java Spring 中的路径遍历缓解。

Prevent path traversal by rebuilding the path through extracting the filename from the file path.

通过从文件路径中提取文件名来重建路径,从而防止路径遍历。


安全问题六:路径遍历

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历




题目





1、介绍

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

Path traversal, also known as directory traversal, occurs when a user has control over the construction of a path that is used to identify a file and uses this to gain access to arbitrary files on the system. This is the case in VikingBank's downloadBrochure functionality. 


Open up the InfoController in the assignment folder and have a look at said method: the filename is passed as a parameter and used to create the full path. 


An attacker controlling the value of the filename could change it to ../../../../../../../../etc/passwd. 


When looking up the file, the application would traverse out of the brochures folder (due to each dot dot slash) and retrieve the /etc/passwd file. 


This lab will tackle path traversal by constructing the path to the file through filename extraction.

当用户可以控制用于标识文件的路径的构造并使用该路径来访问系统上的任意文件时,就会发生路径遍历,也称为目录遍历。VikingBank 的下载手册功能就是这种情况。


打开分配文件夹中的 InfoController 并查看所述方法:文件名作为参数传递并用于创建完整路径。


控制文件名值的攻击者可以将其更改为../../../../../../../../etc/passwd。


查找文件时,应用程序将遍历小册子文件夹(由于每个点点斜杠)并检索 /etc/passwd 文件。


本实验将通过文件名提取构建文件路径来解决路径遍历问题。




2、源码

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历


InfoController.java
package vikingbank.web.controllers;
import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import vikingbank.web.storage.StorageService;
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;
@Controller@RequestMapping("/info")public class InfoController { private final StorageService storageService;
    public InfoController(StorageService storageService) {        this.storageService = storageService; }
    @GetMapping("downloadBrochure")    public ResponseEntity<byte[]> downloadBrochure(@RequestParam String fileName) throws RuntimeException, IOException {          Path filePath = Path.of(fileName);
Path location = storageService.load(filePath);
byte[] content = Files.readAllBytes(location);
        HttpHeaders headers = new HttpHeaders();        headers.setContentType(MediaType.APPLICATION_PDF); headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");         return new ResponseEntity<>(contentheadersHttpStatus.OK);    }}

*左右滑动查看更多


文件结构:

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历




3、步骤一 添加扩展程序允许列表

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历


首先,添加一个简单的文件扩展名验证,以确保只能检索允许扩展名的文件。


编写逻辑,以便在使用 DownloadBrochure 方法的 fileName 参数构建完整路径之前执行该逻辑。


3.1 task1

● 创建一个新的允许扩展名的列表。使用列表的好处是可以轻松地添加其他扩展名到允许列表中。在这种情况下,只允许PDF文件。 

● 获取文件名的扩展名。 

● 创建一个检查,验证文件名的扩展名是否在允许列表中,如果不在,则返回新的ResponseEntity<>(HttpStatus.BAD_REQUEST)。

Step Solution
import java.util.Arrays;
编写一个方法来将文件扩展名与允许的扩展名列表进行比较。
private boolean isValidExtension(String fileName) {    String[] allowedExtensions = { "pdf" };    String fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1);    return Arrays.asList(allowedExtensions).contains(fileExtension.toLowerCase());}
在构造 Path 之前,调用 downloadBrochure 端点中的方法,并在验证返回 false 时返回 BadRequest。if (!isValidExtension(fileName)) {    return new ResponseEntity<>(HttpStatus.BAD_REQUEST);}

*左右滑动查看更多


根据要求修改代码:

InfoController.java
package vikingbank.web.controllers;
import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import vikingbank.web.storage.StorageService;
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.util.Arrays;
@Controller@RequestMapping("/info")public class InfoController {    private final StorageService storageService; private final String[] allowedExtensions = { "pdf" };
    public InfoController(StorageService storageService) {        this.storageService = storageService; }
/**
* 检查文件扩展名是否有效
* @param fileName 文件名
* @return 如果文件扩展名有效,则返回 true;否则返回 false
     */    private boolean isValidExtension(String fileName) {        String fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1);        return Arrays.asList(allowedExtensions).contains(fileExtension.toLowerCase()); }
/**
     * 下载宣传册的方法     * @param fileName 要下载的文件名     * @return 包含文件内容的 ResponseEntity 对象     * @throws RuntimeException 如果发生运行时异常 * @throws IOException 如果发生 IO 异常
*/
    @GetMapping("downloadBrochure")    public ResponseEntity<byte[]> downloadBrochure(@RequestParam String fileName) throws RuntimeException, IOException {        // 检查文件扩展名是否有效        if (!isValidExtension(fileName)) {            // 如果无效,返回一个包含 HttpStatus.BAD_REQUEST 的 ResponseEntity 对象            return new ResponseEntity<>(HttpStatus.BAD_REQUEST); }
        // 创建文件路径和存储位置        Path filePath = Path.of(fileName); Path location = storageService.load(filePath);
        // 读取文件内容为字节数组 byte[] content = Files.readAllBytes(location);
        // 设置响应头        HttpHeaders headers = new HttpHeaders();        headers.setContentType(MediaType.APPLICATION_PDF); headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
        // 返回一个包含文件内容、响应头和 HttpStatus.OK 的 ResponseEntity 对象        return new ResponseEntity<>(content, headers, HttpStatus.OK);    }}

*左右滑动查看更多




4、步骤二 提取文件名

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

目前,存储路径是通过存储位置的路径与文件路径组合而成的。为了防止目录遍历,不可靠的字符(例如 ../)不应进入完整路径。一种方法是从传入文件路径中提取文件名。


4.1 task1

使用 Path.of().getFileName() 提取文件名。
此方法将提取最后一个分隔符之后的路径部分。F.e. loans.pdf 和 ../../../loans.pdf 都会生成loans.pdf。


● 在文件路径上调用.getFileName() 以返回距离路径根最远的元素。

Step SolutionPath filePath = Path.of(fileName).getFileName();

*左右滑动查看更多


按照要求修改代码:

InfoController.java
package vikingbank.web.controllers;
import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import vikingbank.web.storage.StorageService;
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.util.Arrays;
@Controller@RequestMapping("/info")
public class InfoController {    private final StorageService storageService; private final String[] allowedExtensions = { "pdf" };
    public InfoController(StorageService storageService) { this.storageService = storageService;
}
/**
* 检查文件扩展名是否有效
* @param fileName 文件名
* @return 如果文件扩展名有效,则返回 true;否则返回 false
*/
    private boolean isValidExtension(String fileName) {        String fileExtension = Path.of(fileName).getFileName().toString();        fileExtension = fileExtension.substring(fileExtension.lastIndexOf('.') + 1); return Arrays.asList(allowedExtensions).contains(fileExtension.toLowerCase()); }
    /**     * 下载宣传册的方法     * @param fileName 要下载的文件名     * @return 包含文件内容的 ResponseEntity 对象     * @throws RuntimeException 如果发生运行时异常 * @throws IOException 如果发生 IO 异常
*/
    @GetMapping("downloadBrochure")    public ResponseEntity<byte[]> downloadBrochure(@RequestParam String fileName) throws RuntimeException, IOException {        // 检查文件扩展名是否有效        if (!isValidExtension(fileName)) {            // 如果无效,返回一个包含 HttpStatus.BAD_REQUEST 的 ResponseEntity 对象            return new ResponseEntity<>(HttpStatus.BAD_REQUEST); }
        // 创建文件路径和存储位置        Path filePath = Path.of(fileName); Path location = storageService.load(filePath);
        // 读取文件内容为字节数组 byte[] content = Files.readAllBytes(location);
        // 设置响应头        HttpHeaders headers = new HttpHeaders();        headers.setContentType(MediaType.APPLICATION_PDF); headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
        // 返回一个包含文件内容、响应头和 HttpStatus.OK 的 ResponseEntity 对象        return new ResponseEntity<>(content, headers, HttpStatus.OK);    }}

*左右滑动查看更多


提交通过。

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

分析一下:
在 isValidExtension 方法中,首先将 fileName 转换为 Path 对象,然后通过调用 filePath.getFileName().toString() 获取文件名的字符串表示。


接着,使用 substring 方法从文件名中提取文件扩展名,并将其转换为小写。最后,检查文件扩展名是否在允许的扩展名列表中。


在 downloadBrochure 方法中,首先调用 Path.of(fileName) 创建文件路径,并通过调用 storageService.load(filePath) 获取文件的存储位置。


然后,使用 Files.readAllBytes 方法读取文件内容为字节数组。接下来,设置响应头的内容类型为 MediaType.APPLICATION_PDF,并设置缓存控制头。


最后,返回一个包含文件内容、响应头和 HttpStatus.OK 的 ResponseEntity 对象。如果文件扩展名无效,将返回一个包含 HttpStatus.BAD_REQUEST 的 ResponseEntity 对象。


总结





1、 题目总结

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历


当攻击者可以影响文件或目录的路径时,就可能会发生路径或目录遍历。为了避免此漏洞,需要在构建路径时控制用户输入。如果在业务层面不好实现,当从路径中提取文件名时,需要对可能导致遍历目录的分隔符进行过滤排查。




2、路径遍历通用修复方案

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历


1、对输入进行验证和过滤:对用户提供的输入始终进行验证和过滤,以确保输入只包含预期的字符,而不包含特殊字符或路径分隔符。可以使用正则表达式或其他验证机制。

2、绝对路径替代相对路径:使用绝对路径而非相对路径,以确保应用程序始终在已知目录中运行,而不受用户输入或其他不受信任输入的影响。

3、白名单验证:使用白名单验证限制用户输入。通过定义允许的文件或目录列表,确保用户输入仅匹配白名单中的选项。

4、权限控制:限制应用程序对文件系统的访问权限。确保应用程序运行的用户或服务账户具有最小必需的权限,以限制攻击者能够访问的文件和目录。

5、使用安全的文件操作API:在处理文件和路径时,使用具有内建安全机制的API,以避免潜在的安全风险,这些API提供了防止路径遍历攻击的保护机制。例如,在Java中,可以使用 java.nio.file 包中的方法来执行文件和路径操作。

6、对用户输入进行转义:对用户输入进行转义。为了确保用户输入不会被解释为路径分隔符或其他特殊字符,应使用适当的转义机制,将其动态拼接到文件路径或命令中。



往期回顾

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历


关于安恒信息安全服务团队
安恒信息安全服务团队由九维安全能力专家构成,其职责分别为:红队持续突破、橙队擅于赋能、黄队致力建设、绿队跟踪改进、青队快速处置、蓝队实时防御,紫队不断优化、暗队专注情报和研究、白队运营管理,以体系化的安全人才及技术为客户赋能。

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

原文始发于微信公众号(安恒信息安全服务):九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月20日20:45:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   九维团队-绿队(改进)| Java Spring编码安全系列之路径遍历http://cn-sec.com/archives/2222968.html

发表评论

匿名网友 填写信息