Spring 控制器从文件系统下载文件限制



我有一个带有/file映射的 Spring 控制器,它可以从用户那里获取文件名并将文件内容流式传输给用户

@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下载该文件,这是一个安全漏洞。 检查我从用户那里给出的名称的最佳方法是文件名?(不是directory/filename../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'

"tmpPath"将等于"fileParent",如果您提供有效的文件名,如"bar.xls"。

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

最新更新