直接传filestream或memorystream易崩溃,因pdf库不验证输入,遇恶意xref、无限嵌套等畸形结构会触发栈溢出或内存耗尽;须校验文件大小、头部、startxref位置等再解析。

PDF解析库加载远程或用户上传文件时,为什么直接用FileStream或MemoryStream传入就可能崩溃?
因为很多C# PDF库(比如iTextSharp、PDFsharp、甚至SkiaSharp的PDF导出模块)在解析阶段不做输入验证,遇到构造恶意xref表、无限嵌套对象、超大间接引用等畸形结构时,会触发栈溢出、内存耗尽或未处理异常。不是“不安全”,而是它们本就设计为解析合规PDF——而攻击者专挑不合规的边界。
- 别用
File.OpenRead("user.pdf")直传给PdfReader,先做流长度和头部校验 - 限制单个PDF文件大小(比如
if (file.Length > 10 * 1024 * 1024)直接拒绝) - 用
try/catch捕获StackOverflowException以外的常见异常,如OutOfMemoryException、ArgumentException、InvalidDataException - 对ASP.NET Core上传场景,务必在
IFormFile读取前检查ContentLength,而不是依赖MIME类型
如何用System.IO.Pipelines做轻量预扫描,拦截明显恶意PDF头?
不用解析全文,只读前几KB就能筛掉大量攻击样本。PDF规范要求开头是%PDF-,结尾有%%EOF,且xref位置必须合理。手动跳过注释行、定位startxref偏移,再验证该偏移是否落在文件末尾合理范围内(比如距末尾
- 用
PipeReader读取前8KB,避免一次性ToArray()吃光内存 - 搜索
"%PDF-"后紧跟数字(如%PDF-1.7),拒绝无版本号或版本过高(如%PDF-9.9)的文件 - 找到
startxref后,提取其后的数字,检查是否为纯数字且 - 发现多个
startxref、或%%EOF出现在文件开头,直接丢弃
iText7启用沙箱模式后,为什么还是可能被JavaScript或Launch动作绕过?
iText7的SafeParse或SecurityManager只管解析层,不拦执行层。PDF里的/JS动作、/Launch启动外部程序、甚至/EmbeddedFile带EXE附件,只要解析成功,后续若调用GetJavaScript()或GetFileSpec()就可能触发。
- 初始化
PdfReader时强制传入new ReaderProperties().SetIsUseFullCompression(false),禁用压缩流解码(减少攻击面) - 遍历
document.GetCatalog().GetPdfObject().GetAsStream(PdfName.Names)等关键字典,主动跳过含/JS、/Launch、/EmbeddedFile的条目 - 不要调用
pdfDoc.GetPage(i).GetAnnotations()后直接遍历.GetPdfObject(),先检查annotation.GetSubtype()是否为PdfName.Widget或PdfName.Link - 生产环境彻底禁用JavaScript:在Web服务器层(如Nginx)返回PDF时加
Content-Disposition: attachment,防止浏览器内联执行
为什么把PDF转成图像再OCR,反而放大了风险?
用Ghostscript或ImageMagick调用命令行转图时,如果拼接参数没过滤空格、引号、分号,攻击者上传文件名含test.pdf; rm -rf /就能RCE;更隐蔽的是PDF里嵌PostScript代码,某些渲染器会执行它。
- 永远不用
Process.Start("gs", $"-dNOPAUSE -dBATCH -sDEVICE=png16m -r300 -sOutputFile={outputPath} {inputPath}")这种字符串拼接 - 改用
ProcessStartInfo并显式设置Arguments为字符串数组,让系统自动转义 - 限制Ghostscript版本(
9.50+才默认禁用exec类PS指令),并在启动参数加-dSAFER - 转换后立刻校验输出图像尺寸:若宽高超过
10000px或面积超100MP,说明可能触发了恶意重采样逻辑,丢弃该任务
真正难防的不是格式错乱,而是合法PDF里藏的/OpenAction + /RichMedia组合,这类需要深度AST分析,普通业务代码不该自己写解析器——交给专业沙箱服务更实际。









