java解决PDF中的XSS攻击
1、依赖
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.26</version> </dependency>
2、
public String uploadFile(MultipartFile file) throws Exception { Boolean result; final ExecutorService exec = Executors.newFixedThreadPool(1); Callable<Boolean> isXss = () -> { //开始执行耗时操作,需要加上pdf后缀,否则也可以通过抓包修改ContentType来绕过检查 if ((("pdf".equalsIgnoreCase(FileTypeUtils.getFileType(file.getOriginalFilename()))) || MediaType.APPLICATION_PDF_VALUE.equals(file.getContentType())) && FileUtils.containsJavaScript(FileUtils.multipartFileToFile(file))) { return true; } else { return false; } }; try { Future<Boolean> future = exec.submit(isXss); //这一步是为了防止那些比较大的PDF导致系统挂死的问题,如果超过3s则默认认为文件安全::>_<:: result = future.get(1000 * 3, TimeUnit.MILLISECONDS); //任务处理超时时间设为 3 秒 log.info("result:{}",result); } catch (TimeoutException ex) { log.info("调用接口,处理3s超时......"); result = false; ex.printStackTrace(); } catch (Exception e) { log.info("调用接口,处理失败......"); result = false; e.printStackTrace(); } // 关闭线程池 exec.shutdown(); if(result){ throw new CustomException("所上传文件可能具有XSS攻击,请重新上传安全文件!"); } String name = FileUploadUtils.upload(localFilePath, file); return name; } /** * File转MultipartFile * @param file 文件对象 * @return Multipart文件对象 */ public static File multipartFileToFile(MultipartFile mulFile) throws IOException { InputStream ins = mulFile.getInputStream(); String fileName = mulFile.getOriginalFilename(); String prefix = getFileNameNoSuffix(fileName) + UUID.randomUUID().toString(); String suffix = "." + getSuffixNameName(fileName); File toFile = File.createTempFile(prefix, suffix); OutputStream os = new FileOutputStream(toFile); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); ins.close(); return toFile; } /** * 获取不带扩展名的文件名 */ public static String getFileNameNoSuffix(String filename) { if ((filename != null) && (filename.length() > 0)) { int dot = filename.lastIndexOf('.'); if ((dot > -1) && (dot < (filename.length()))) { return filename.substring(0, dot); } } return filename; } /** * 获取文件扩展名 */ public static String getSuffixNameName(String filename) { if ((filename != null) && (filename.length() > 0)) { int dot = filename.lastIndexOf('.'); if ((dot > -1) && (dot < (filename.length() - 1))) { return filename.substring(dot + 1); } } return filename; } /** * 校验pdf文件是否包含js脚本 **/ public static boolean containsJavaScript(File file) throws IOException { RandomAccessFile is = new RandomAccessFile(file, "r"); try{ PDFParser parser = new PDFParser(is); parser.parse(); PDDocument doc = parser.getPDDocument(); String CosName = doc.getDocument().getTrailer().toString(); if(CosName.contains("COSName{JS}")){ return true; } }catch (Exception e){ System.out.println("PDF效验异常:"+e.getMessage()); }finally { is.close(); } return false; } /** * File转MultipartFile * @param file 文件对象 * @return Multipart文件对象 */ public static MultipartFile getMultipartFile(File file) { FileItem item = new DiskFileItemFactory().createItem("file" , MediaType.MULTIPART_FORM_DATA_VALUE , true , file.getName()); try (InputStream input = new FileInputStream(file); OutputStream os = item.getOutputStream()) { // 流转移 IOUtils.copy(input, os); } catch (Exception e) { throw new IllegalArgumentException("Invalid file: " + e, e); } return new CommonsMultipartFile(item); }