【PDF-XSS攻击】springboot项目-上传文件-解决PDF文件XSS攻击

发布于:2025-07-02 ⋅ 阅读:(24) ⋅ 点赞:(0)

问题:

 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文件
            }

            // 检查是否包含JavaScript
            if (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

 @Override
    public 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)


网站公告

今日签到

点亮在社区的每一天
去签到