问题:
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)