我有一个带有 /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/