【PDF-XSS攻击】springboot项目-上传文件-解决PDF文件XSS攻击
- 解决
-
- pdfbox依赖
- PdfUtils工具类
问题:
1、pom.xml
<dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.34</version></dependency>
2、PdfUtils.java
package com.hf.common.utils;import com.hf.common.core.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPageTree;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.stream.IntStream;@Slf4j
public class PdfUtils {/*** 校验pdf文件是否包含js脚本**/public static boolean containsJavaScript(File file) {PDDocument document = null;try {document = PDDocument.load(file);} catch (IOException e) {throw new BusinessException("检测pdf文件脚本失败");}return containsJavaScript(document);}/*** 通过流的方式校验pdf文件是否包含js脚本* 校验pdf文件是否包含js脚本**/public static boolean containsJavaScript(InputStream input) {PDDocument document = null;try {document = PDDocument.load(input);} catch (IOException e) {throw new BusinessException("检测pdf文件脚本失败");}return containsJavaScript(document);}/*** 文档的方式校验pdf文件是否包含js脚本*/private static boolean containsJavaScript(PDDocument document) {if (document.getDocument().getTrailer().toString().contains("COSName{JS}")|| document.getDocument().getTrailer().toString().contains("COSName{JavaScript}")) {return true;}PDPageTree pages = document.getPages();return IntStream.range(0, pages.getCount()).anyMatch(i -> {String pageContent = pages.get(i).getCOSObject().toString();return pageContent.contains("COSName{JS}") || pageContent.contains("COSName{JavaScript}");});}/*** 增强版PDF安全检查,检测空PDF和可能的恶意内容** @param file PDF文件* @return 是否安全*/public static boolean isSafePdf(File file) {PDDocument document = null;try {document = PDDocument.load(file);// 检查文件大小是否异常小
// if (file.length() < 1024) { // 小于1KB的PDF文件可能有问题
// log.error("PDF文件大小异常小: {} bytes", file.length());
// return false;
// }// 检查页面数量if (document.getNumberOfPages() == 0) {log.error("PDF文件没有页面");return false; // 空PDF文件}// 检查是否包含JavaScriptif (containsJavaScript(document)) {log.error("PDF文件包含JavaScript");return false;}// 检查文档结构中的可疑元素String trailerString = document.getDocument().getTrailer().toString().toLowerCase();if (trailerString.contains("/js") ||trailerString.contains("/javascript") ||trailerString.contains("/action") ||trailerString.contains("/launch") ||trailerString.contains("/submitform") ||trailerString.contains("/openaction")) {log.error("PDF文件包含可疑元素: {}", trailerString);return false;}return true;} catch (Exception e) {// 如果解析出错,认为文件不安全log.error("PDF文件解析失败", e);return false;} finally {if (document != null) {try {document.close();} catch (IOException e) {// 忽略关闭错误}}}}
}
3、serviceimpl
@Overridepublic ObjectFile upload(MultipartFile file, String user) throws Exception {if (file == null || file.isEmpty()) {throw new BusinessException("上传文件为空");}String fileName = file.getOriginalFilename();String contentTypes = file.getContentType();// 检查PDF文件安全性if (fileName != null && fileName.toLowerCase().endsWith(".pdf")) {File tempFile = File.createTempFile("upload_", ".pdf");try {FileCopyUtils.copy(file.getInputStream(), new FileOutputStream(tempFile));// 使用增强的PDF安全检查if (!PdfUtils.isSafePdf(tempFile)) {throw new BusinessException("检测到不安全的PDF文件,上传失败");}} catch (IOException e) {throw new BusinessException("PDF文件安全检查失败: " + e.getMessage());} finally {tempFile.delete();}}
//上传minio服务器Map<String, String> map = minioService.uploadFile(file.getInputStream(), file.getSize(), fileName, contentTypes);if (MapUtil.isEmpty(map)) {throw new BusinessException("上传文件失败");}ObjectFile objectFile = new ObjectFile ();objectFile .setFileType(map.get("fileType"));objectFile .setNameOld(map.get("nameOld"));objectFile .setNameNew(map.get("nameNew"));objectFile .setPath(map.get("path"));objectFile .setInternalPath(map.get("internalPath"));if (StrUtil.isNotEmpty(user)) {objectFile .setCreateId(user);}iObjectFileService.save(objectFile );return objectFile ;}
使用python 生成的xss 空白文件
from PyPDF2 import PdfReader, PdfWriter
# 创建一个新的 PDF 文档
output_pdf = PdfWriter()
# 添加一个新页面
page = output_pdf.add_blank_page(width=72, height=72)
# 添加js代码
output_pdf.add_js("app.alert('xss');")
# 将新页面写入到新 PDF 文档中
with open("xss.pdf", "wb") as f:output_pdf.write(f)
文件2:生成带有内容的xss文件
from PyPDF2 import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from io import BytesIO# 第一步:用 reportlab 创建带有文本的 PDF 页面
packet = BytesIO()
can = canvas.Canvas(packet, pagesize=letter)
can.drawString(100, 750, "123") # 添加你要写入的文本
can.save()
packet.seek(0)# 第二步:读取该页面并创建新的 PDF
new_pdf = PdfReader(packet)
output_pdf = PdfWriter()# 添加页面(带文字)
output_pdf.add_page(new_pdf.pages[0])# 添加 JavaScript(XSS 示例)
output_pdf.add_js("app.alert('xss');")# 第三步:写入最终 PDF 文件
with open("xss23.pdf", "wb") as f:output_pdf.write(f)