java - Spring Controller 下载文件不受文件系统限制

标签 java spring security servlets

我有一个带有 /file 映射的 Spring Controller ,它从用户获取文件名并将文件内容流式传输给用户

@RequestMapping(value = "/file" , method = RequestMethod.GET)
@ResponseBody
public void getFile(@RequestParam(value = "name", required = true) String fileName,
                    HttpServletResponse response)
{
    String fileExtension = "";
    int i = fileName.lastIndexOf('.');
    if (i > 0) {
        fileExtension = fileName.substring(i+1);
    }

    // file extension for requested file must be xls
    if(!fileExtension.equals("xls"))
    {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        return;
    }

    try {
        Path path = Paths.get("/tmp/" + fileName);
        byte[] data = Files.readAllBytes(path);
        response.setHeader("Content-Disposition", "inline; filename=" + fileName);
        response.setContentType("application/vnd.ms-excel");
        response.setContentLength(data.length);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(data);
            outputStream.flush();
        } catch (Exception e) {
        }
    } catch (Exception e) {
    }
}

用户只能下载 tmp 文件夹中扩展名为 .xls 的文件。此代码的问题是用户可以更改目录并下载其他目录中的其他 .xls 文件。例如,如果此路径中有一个文件 /tmp/tmp2/ab.xls 用户可以通过调用此 url http://myserver.mydomain:myport/mycontext/file 下载该文件?name=tmp2/ab.xls 这是一个安全漏洞。检查用户提供的名称是文件名的最佳方法是什么? (不是目录/文件名../filename或其他危险路径)

最佳答案

    Path tmpPath = Paths.get("/tmp/"); //valid directory
    String fileName = "foo/bar.xls"; //supplied fileName

    Path filePath = tmpPath.resolve(fileName); //add fileName to path
    Path fileParent = filePath.getParent(); //get parent directory
    System.out.println(fileParent);
    System.out.println(tmpPath.equals(fileParent)); //false because fileParent is '/tmp/foo'

如果您提供有效的文件名(例如“bar.xls”),“tmpPath”将等于“fileParent”。

我认为您还可以简化扩展名检查: filePath.endsWith(".xls"); 应该足够了。并且不要连接文件路径(“/tmp/”+ 文件名)。 Paths.get("/tmp", fileName) 将为您完成此操作。

关于java - Spring Controller 下载文件不受文件系统限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35113518/

相关文章:

spring - 组件扫描不扫描子包

security - 阻止 API "spoofing"或 "hacking"

rest - 如何保护 flask 上的 REST Api

apache - 2 Way SSL using Apache - 证书问题

java - 在编写 Junit 测试时,Mockito.mock 比使用 new 有何优势?

java - 新的 JUnit 4.8.1 @Category 渲染测试套件是否几乎过时了?

java - while循环只执行一次

java - com.sun.tools.javac.code(javac 编译器)包中的这种奇怪语法是什么?它是如何工作的?

java - Spring Security - 忽略请求参数规则的URL

java - @Transactional 和 @Transactional(readOnly=true) 有什么区别