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

问题:

1、pom.xml

java 复制代码
         <dependency>
                <groupId>org.apache.pdfbox</groupId>
                <artifactId>pdfbox</artifactId>
                <version>2.0.34</version>
            </dependency>

2、PdfUtils.java

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

java 复制代码
 @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 空白文件

python 复制代码
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文件

python 复制代码
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)
相关推荐
吾疾唯君医4 分钟前
Java SpringBoot集成积木报表实操记录
java·spring boot·spring·导出excel·积木报表·数据文件下载
lifejump2 小时前
Empire(帝国)CMS 7.5 XSS注入
前端·安全·xss
正儿八经的少年4 小时前
Spring Boot 两种激活配置方式的作用与区别
java·spring boot·后端
疯狂成瘾者4 小时前
Spring Boot 项目中的 SMTP 邮件验证码服务技术解析
java·spring boot·后端
啃臭5 小时前
AOP和反射
java·spring boot
凌云若寒5 小时前
BarTender许可 | 关于PDF打印数量说明
学习·pdf·产品经理·制造·软件需求
进击切图仔6 小时前
RAG 加载 pdf 文档
linux·前端·pdf
河阿里6 小时前
SpringBoot:Spring Task定时任务完整使用教学
java·spring boot·spring
优化控制仿真模型6 小时前
30套高级毕业答辩ppt模版(免费下载)
经验分享·pdf
XMJ20028 小时前
解决包含visio图的Word文档转PDF后部分文字消失问题的一种方法
pdf·word