Acrobat JavaScript 中的可信任函数

发布于:2025-06-29 ⋅ 阅读:(22) ⋅ 点赞:(0)

自从Acrobat和PDF中添加了脚本功能以来,安全性一直是一个备受关注的问题。随着互联网的日益普及,情况变得越来越糟。垃圾邮件无处不在,黑客似乎每天都在想出新的方法来入侵我们的计算机。幸运的是,Acrobat JavaScript一直是一个沙盒环境,对本地文件系统的访问受到严格控制,某些类型的潜在危险操作被限制在一个相对安全的上下文中。但即使有这些限制,早期的系统仍然松散且有些临时性。这也带来了一个问题:有些情况下确实需要执行这些潜在危险的操作。我们该如何处理这些情况?

在Acrobat DC 中,Adobe通过"特权"(Privilege)的概念正式确立了 Acrobat JavaScript 的安全性,并引入了"可信函数"(Trusted Function)作为创建特权的一种方式。

什么是特权?

Acrobat JavaScript中的所有操作分为两类:特权操作和非特权操作。例如,表单上使用的标准操作是非特权的,如计算、隐藏和显示字段、重置表单和提交表单。这些都是标准、无害的操作。然而,像访问Identity对象这样的操作就是特权的。Identity对象包含用户的姓名、电子邮件和其他专有信息。这些信息不是您希望任何脚本都能访问的。

在Acrobat JavaScript参考中,任何标记为特权的对象、函数或属性都是潜在危险的,只能在特权上下文中运行。Acrobat JavaScript参考中的所有条目在条目顶部都有一个"快速栏"(或"权限栏",取决于文档版本)。Identity对象的快速栏如下所示。

第三列中的红色"S"表示此对象只能从特权上下文中访问,并且有一个注释更明确地解释了这一点。如果权限栏的第三列为空,则该对象、函数或属性被认为是安全的,可以在Acrobat或PDF的任何上下文中运行。

特定JavaScript对象、函数或属性是否具有特权完全由Adobe决定。他们有一个安全团队日夜工作,找出安全漏洞并加以修补。

什么是特权上下文?

在Acrobat和PDF中,有许多不同的位置可以使用脚本。其中一些位置在用户的系统上,一些在PDF内部。假设脚本在用户的系统上,那么用户知道它的存在。只有能够物理访问系统的人才能将脚本放置在用户的系统上。因此,这些脚本被认为是特权上下文。这些特权位置包括文件夹级脚本、批处理脚本和JavaScript控制台窗口。

相反,PDF中的脚本可以来自任何地方。如果没有特殊机制(如数字证书),就无法知道PDF的真正来源。因此,PDF中的脚本不可信。PDF内部的所有脚本位置(如表单字段)都被视为非特权。当然,也有例外。如果PDF已经过认证,并且数字证书在用户的系统上,并且证书已经针对PDF进行了验证,那么PDF中的脚本位置可以被视为特权。但这是一个罕见的例外。一般来说,PDF内部的所有脚本都被限制为无害的非特权操作。

有时,特别是在自动化脚本中,需要从非特权上下文中运行特权操作。为了克服这一障碍,Adobe创建了可信函数。

什么是可信函数?

可信函数对自动化脚本最有用。自动化脚本通常使用所谓的文件夹级脚本文件创建。这些JavaScript文件被放置在用户系统上的特殊Acrobat文件夹中。它们在Acrobat启动时加载并运行,并且只在Acrobat启动时运行。

一般的想法是,自动化脚本设置变量、数据和函数,这些将在以后用于某些特殊的自动化任务。通常,脚本还会设置某种用户界面项,如工具栏按钮或菜单项,以运行使用由文件夹级脚本创建的函数、变量和数据的脚本。

这里的问题是,虽然文件夹级脚本是特权的(因为它在用户的系统上),但用文件夹级脚本创建的函数、变量和数据不是特权的。例如,假设我想创建一个工具栏按钮,按下时替换PDF的最后一页。为此,我创建了一个文件夹级JavaScript文件,在其中放置了替换PDF页面的普通函数代码和创建调用替换页面函数的工具栏按钮的代码。

不幸的是,这不会起作用。替换页面的JavaScript命令是特权的,而在文件夹级脚本中创建的普通函数是非特权的。为了解决这个难题,我必须使用app.trustedFunction()将普通函数变成可信函数,如下所示。

// 页面替换函数
var ReplaceLastPage = app.trustedFunction(function(cPath) {
    app.beginPriv();
    this.replacePages(this.numPages-1, cPath);
    app.endPriv();
});

让我们看看这段代码是如何工作的。app.trustedFunction()有一个输入,即一个普通函数。上面的代码使用了一个内联定义的无名函数。这只是设置可信函数的一种便捷方式。普通函数可以在其他地方定义为命名函数,然后使用函数名传递给app.trustedFunction()。app.trustedFunction()的返回值与传入的函数完全相同,但现在该函数具有特权,可以从任何执行上下文中调用,甚至是PDF内部的脚本。

可信函数不能在任何地方创建。它们只能在特权上下文中创建。但一旦创建,它们可以在任何地方使用。在上面的例子中,替换函数是在文件夹级脚本中创建的,该脚本在Acrobat启动时加载并运行,此时是特权的。

创建可信函数的最后一部分是注意app.beginPriv()和app.endPriv()代码行。这些行将函数中需要特权的部分括起来。它们必须存在才能使代码具有特权。

创建可信函数真的就这么简单。最重要的部分是它必须从已经具有特权的上下文中完成,并且函数内部的特权部分必须用app.beginPriv()和app.endPriv()函数括起来。

您可以在下面的链接中的Acrobat JavaScript指南和Acrobat JavaScript参考中找到关于特权、可信函数以及本文中使用的所有各种Acrobat JavaScript功能的更多信息。请查看"文档"选项卡。

扩展内容与示例代码

可信函数的工作原理

可信函数本质上是一个包装器,它将普通函数提升为可以在任何上下文中执行特权操作的函数。这种机制类似于操作系统中的权限提升(privilege escalation)。

调用
执行
非特权上下文
可信函数
特权上下文
特权操作
返回结果

完整示例代码

// 文件夹级脚本 (privileged-context.js)
// 这个脚本在Acrobat启动时自动加载

// 创建可信函数来访问用户身份信息
var GetUserInfo = app.trustedFunction(function() {
    app.beginPriv();
    try {
        // 访问特权对象 - Identity
        var identity = {
            name: identity.name,
            email: identity.email,
            company: identity.company
        };
        app.endPriv();
        return identity;
    } catch(e) {
        app.endPriv();
        console.println("Error accessing identity: " + e);
        return null;
    }
});

// 创建工具栏按钮
app.addToolButton({
    cName: "User Info",
    cLabel: "显示用户信息",
    cEnable: "event.rc= (event.target != null);",
    cExec: "showUserInfo();"
});

// 普通函数 - 将被PDF中的脚本调用
function showUserInfo() {
    var info = GetUserInfo();
    if(info) {
        app.alert("用户信息:\n姓名: " + info.name + 
                 "\n邮箱: " + info.email + 
                 "\n公司: " + info.company);
    } else {
        app.alert("无法获取用户信息");
    }
}

代码备注

  1. app.beginPriv()/app.endPriv():这对函数必须成对出现,它们之间的代码块将获得特权执行权限
  2. 错误处理:在特权代码块中使用try-catch确保app.endPriv()始终被执行
  3. 工具栏按钮:演示了如何将可信函数与用户界面集成
  4. 身份信息访问:Identity对象是典型的特权对象,包含敏感用户数据

在PDF中使用可信函数

// PDF文档中的脚本 (非特权上下文)

// 尝试直接访问Identity对象会失败
try {
    var name = identity.name; // 这将抛出安全异常
} catch(e) {
    console.println("直接访问失败: " + e);
}

// 但可以调用之前创建的可信函数
if(typeof(GetUserInfo) == "function") {
    var userData = GetUserInfo();
    if(userData) {
        this.getField("UserName").value = userData.name;
        this.getField("UserEmail").value = userData.email;
    }
}

安全模型图解

  1. 最小特权原则:只在必要时使用可信函数,且特权代码块应尽可能小
  2. 输入验证:可信函数应对所有输入参数进行严格验证
  3. 错误处理:妥善处理特权代码中的错误,确保始终调用app.endPriv()
  4. 文档记录:清晰记录哪些函数是可信的及其安全影响
  5. 代码签名:对包含可信函数的脚本进行数字签名
«enumeration»
ExecutionContext
Privileged
NonPrivileged
JavaScriptObject
+isPrivileged: Boolean
+checkPrivilege()
TrustedFunction
+wrappedFunction: Function
+executeInPrivilegedContext()
SecurityManager
+beginPrivileged()
+endPrivileged()
+validateTrustedFunction()

常见问题解答

Q: 可信函数会降低PDF的安全性吗?
A: 合理使用时不会。可信函数实际上提供了一种可控的特权提升机制,比早期版本中宽松的安全模型更安全。

Q: 可以在PDF内部创建可信函数吗?
A: 不可以。可信函数只能在特权上下文中创建,如文件夹级脚本、批处理脚本或控制台。

Q: 如何调试可信函数?
A: 可以使用JavaScript控制台(privileged context)进行调试,console.println()输出在控制台可见。

生词表

单词 词性 中文解释
sandboxed adj. 沙盒化的,受限制的
ad hoc adj. 临时的,特别的
benign adj. 良性的,无害的
proprietary adj. 专有的,私有的
ubiquitous adj. 普遍存在的
converse adj. 相反的
validate v. 验证,确认
dilemma n. 困境,两难
bracket v. 用括号括起
automation n. 自动化
certificate n. 证书
explicitly adv. 明确地
mechanism n. 机制
console n. 控制台
inline adj. 内联的

网站公告

今日签到

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