Java 任意文件下载漏洞
我们在开发 Web 应用时,经常会提供文件下载功能。对外暴露类似如下的URL
http://demo.com/download?fileName=foo.txt
这样的确很方便,但是,大家有没有想过,这样的功能可能会出现什么样的安全隐患或者漏洞呢?
下面是一段提供文件下载的spring mvc的java代码,使用fileName来指定要下载的文件。
@GetMapping("/download") public ResponseEntity<Resource> download(@RequestParam("fileName") String fileName) throws IOException { Path filePath = Paths.get(PATH_PREFIX).toAbsolutePath().normalize() .resolve(fileName).normalize(); Resource resource = new UrlResource(filePath.toUri()); if (!resource.exists()) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok() .contentType(MediaType.parseMediaType("application/octet-stream")) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + resource.getFilename()) .body(resource); }
这里定了一个 PATH_PREFIX 常量,意图把访问限制在 PATH_PREFIX下。当fileName=../path/to/file 就跳出了限制,可以任意下载服务器上的问题,这是很严重的问题。
如果你的 DB 和业务代码配置在同一个服务器就可能被人脱库。
那么怎么解决这个问题呢?方案也有很多,例如:把文件路径存储到DB,客户端通过 fileId 下载文件。这里咱们讨论另一种简单的方案:
@GetMapping("/download") public ResponseEntity<Resource> download(@RequestParam("fileName") String fileName) throws IOException { Path filePath = Paths.get(PATH_PREFIX).toAbsolutePath().normalize() .resolve(fileName).normalize(); final String parentPathString = filePath.getParent().toAbsolutePath().toString(); if (!parentPathString.startsWith(PATH_PREFIX)) { logger.error("illegal access!"); return ResponseEntity.notFound().build(); } Resource resource = new UrlResource(filePath.toUri()); if (!resource.exists()) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok() .contentType(MediaType.parseMediaType("application/octet-stream")) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + resource.getFilename()) .body(resource); }
上面红色标记部分,判断需要下载的文件的路径是否包含了PATH_PREFIX,包含则合法。注意前面经过了 normalize 处理。