You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Apache PDFBox验证PDF外部引用及嵌入JavaScript?

Apache PDFBox 2.0.32 任务实现指南

一、检测PDF嵌入JavaScript的正确依赖

PDFBox 2.0.32处理JavaScript检测无需额外扩展依赖,核心pdfbox依赖即可覆盖所有JavaScript动作的解析需求。以下是主流构建工具的依赖配置:

Maven

<dependency>
    <groupId>org.apache.pdfbox</groupId>
    <artifactId>pdfbox</artifactId>
    <version>2.0.32</version>
</dependency>

Gradle

implementation 'org.apache.pdfbox:pdfbox:2.0.32'

若需处理JavaScript执行场景(非检测需求),可额外添加pdfbox-tools依赖,但仅做检测的话核心依赖足够。

二、检测PDF嵌入JavaScript的示例代码

JavaScript在PDF中通常分布在文档级动作、页面动作、表单字段动作、注释动作四类位置,以下是覆盖所有场景的检测代码:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
import org.apache.pdfbox.pdmodel.interactive.action.PDAnnotationAdditionalActions;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class PdfJavaScriptDetector {
    public static void detectJavaScript(String pdfPath) throws IOException {
        try (PDDocument document = PDDocument.load(new File(pdfPath))) {
            // 检测文档级打开动作
            checkForJavaScript(document.getDocumentCatalog().getOpenAction(), "文档打开动作");

            // 检测每页的打开/关闭动作
            List<PDPage> pages = document.getPages();
            for (int i = 0; i < pages.size(); i++) {
                PDPage page = pages.get(i);
                checkForJavaScript(page.getActions().getO(), "第" + (i+1) + "页打开动作");
                checkForJavaScript(page.getActions().getC(), "第" + (i+1) + "页关闭动作");
            }

            // 检测表单字段触发动作
            PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
            if (acroForm != null) {
                for (PDField field : acroForm.getFields()) {
                    checkForJavaScript(field.getActions().getK(), "表单字段[" + field.getFullyQualifiedName() + "]动作");
                }
            }

            // 检测注释触发动作
            for (PDPage page : pages) {
                List<PDAnnotation> annotations = page.getAnnotations();
                for (PDAnnotation annotation : annotations) {
                    PDAnnotationAdditionalActions annotActions = annotation.getActions();
                    if (annotActions != null) {
                        checkForJavaScript(annotActions.getU(), "页面注释激活动作");
                    }
                }
            }
        }
    }

    private static void checkForJavaScript(PDAction action, String location) {
        if (action instanceof PDActionJavaScript) {
            PDActionJavaScript jsAction = (PDActionJavaScript) action;
            System.out.println("找到JavaScript,位置:" + location);
            System.out.println("JS内容:" + jsAction.getAction());
        }
    }

    public static void main(String[] args) throws IOException {
        detectJavaScript("test.pdf");
    }
}

三、PDF外部引用(URL链接)检测与安全验证

检测逻辑

PDF中的外部URL链接以PDActionURI类型的动作存在,需遍历文档、页面、表单、注释等所有可能的动作位置提取URL,再执行安全校验。

安全验证核心维度

  • 协议限制:仅允许http/https,禁止file:///ftp://等风险协议
  • 域名白名单:仅允许访问预先信任的域名
  • 跳转检测:拦截含跳转参数的可疑URL

示例代码

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.interactive.action.PDAction;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

public class PdfExternalLinkDetector {
    // 自定义信任域名白名单
    private static final List<String> TRUSTED_DOMAINS = Arrays.asList("example.com", "github.com");

    public static void detectAndValidateExternalLinks(String pdfPath) throws IOException {
        try (PDDocument document = PDDocument.load(new File(pdfPath))) {
            // 检测文档级动作中的URL
            checkForUriAction(document.getDocumentCatalog().getOpenAction(), "文档打开动作");

            // 检测页面动作与注释中的URL
            List<PDPage> pages = document.getPages();
            for (int i = 0; i < pages.size(); i++) {
                PDPage page = pages.get(i);
                checkForUriAction(page.getActions().getO(), "第" + (i+1) + "页打开动作");
                checkForUriAction(page.getActions().getC(), "第" + (i+1) + "页关闭动作");

                for (PDAnnotation annotation : page.getAnnotations()) {
                    checkForUriAction(annotation.getAction(), "页面注释动作");
                }
            }

            // 检测表单字段动作中的URL
            PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
            if (acroForm != null) {
                for (PDField field : acroForm.getFields()) {
                    checkForUriAction(field.getActions().getK(), "表单字段[" + field.getFullyQualifiedName() + "]动作");
                }
            }
        }
    }

    private static void checkForUriAction(PDAction action, String location) {
        if (action instanceof PDActionURI) {
            PDActionURI uriAction = (PDActionURI) action;
            String urlStr = uriAction.getURI();
            System.out.println("找到外部URL,位置:" + location);
            System.out.println("URL:" + urlStr);

            boolean isSafe = validateUrlSafety(urlStr);
            System.out.println("URL安全状态:" + (isSafe ? "安全" : "危险"));
            System.out.println("---");
        }
    }

    private static boolean validateUrlSafety(String urlStr) {
        try {
            URL url = new URL(urlStr);
            // 验证协议合法性
            String protocol = url.getProtocol();
            if (!protocol.equalsIgnoreCase("http") && !protocol.equalsIgnoreCase("https")) {
                System.out.println("风险:使用非允许协议 " + protocol);
                return false;
            }

            // 验证域名是否在白名单
            String host = url.getHost();
            if (!TRUSTED_DOMAINS.contains(host)) {
                System.out.println("风险:域名 " + host + " 不在信任白名单中");
                return false;
            }

            // 检测跳转参数(可选)
            if (url.getQuery() != null && url.getQuery().contains("redirect")) {
                System.out.println("风险:URL包含跳转参数");
                return false;
            }

            return true;
        } catch (MalformedURLException e) {
            System.out.println("风险:无效URL格式");
            return false;
        }
    }

    public static void main(String[] args) throws IOException {
        detectAndValidateExternalLinks("test.pdf");
    }
}

内容的提问来源于stack exchange,提问作者Ravi Dobariya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 23:58:24