文档
/

字节码数组编码密钥

外部化字节码数组编码密钥

Pro

使用 vmBytecodeArrayEncodingKey 提供你自己的 VM 字节码加密密钥,并通过密钥 getter 在运行时将其交还 - 保存在包之外、从客户端存储读取,或从你的后端获取。

这些选项的作用

vmBytecodeArrayEncoding 会加密 VM 字节码数组,使其不会以明文形式出现在 输出中。默认情况下,加密密钥从环境派生并在客户端重建,因此你无需亲自处理它。这很方便,但密钥材料仍然存在于 包中。

有两个选项可以让你把密钥从包中取出并由自己掌控:

  • vmBytecodeArrayEncodingKey - 你在编译时提供的密钥。设置后,它会取代默认的环境派生密钥使用,并且不会 嵌入到混淆后的输出中。
  • vmBytecodeArrayEncodingKeyGetter - 一个在运行时返回该相同密钥的 JavaScript 表达式。它会被原样嵌入, 并在混淆后的代码加载时于浏览器中求值。

关键在于分离:由于密钥不在代码中,对包进行纯粹的静态扫描无法恢复它。为了让代码运行,密钥仍然必须在运行时 存在,因此它并非真正保密 - 但你可以决定它来自何处以及谁能看到它。

这两个选项是一对。没有 getter 的 vmBytecodeArrayEncodingKey 会让混淆后的代码在运行时无法获取密钥,而没有 对应编译时密钥的 getter 则没有可与之匹配的对象。请连同 vmBytecodeArrayEncoding: true 一起设置这两者。

两个密钥如何结合

你的密钥从不单独使用 - 在两端它都会与混淆器控制的内部密钥混合:

  • 编译时。 vmBytecodeArrayEncodingKey 会与混淆器派生的内部密钥结合,然后用得到的混合密钥对字节码数组进行 编码。
  • 运行时。 你的 vmBytecodeArrayEncodingKeyGetter 解析出的值会与相同的内部密钥结合(该内部密钥根据各种运行时 因素在客户端重建),以此解码字节码。

由于两端都会将你的密钥与内部密钥混合,getter 必须解析为你作为 vmBytecodeArrayEncodingKey 传入的完全相同的 字符串。任何一部分单独都不够:没有内部密钥,你的密钥无法解码字节码;没有你的密钥,内部密钥也毫无用处 - 这正是为什么控制谁能收到你的密钥才是真正保护代码的关键。

在运行时提供密钥

默认情况下,getter 是同步的:当混淆后的代码加载时,表达式必须立即返回密钥。可以从任何已经存在于客户端的来源 读取 - cookie、localStorage、全局变量,或服务器注入的 DOM 元素。

JavaScript

密钥必须在混淆后的代码运行之前就存在:

JavaScript

其他同步来源的工作方式相同 - 选择你的应用已经填充好的那一种:

JavaScript

不要把密钥和混淆后的代码放在同一个文件或脚本中。将其内联在那里会彻底破坏整个目的 - 对包进行一次静态扫描 就能同时恢复代码及其密钥。请将密钥存放在单独的来源中,并从环境变量或密文中注入编译时的 vmBytecodeArrayEncodingKey,而不要将其提交到代码库。

从你的后端获取密钥(异步)

需要 vmAsyncExecutor · v7.3.0+

同步 getter 只能读取已经在客户端上的内容。若要从你的服务器获取密钥 - 以便将其置于身份验证之后进行门控并 可撤销 - getter 就必须是异步的,而这需要 vmAsyncExecutor。启用异步 执行器后,getter 可以返回一个 Promise,VM 会在运行前等待它。

JavaScript

返回 Promise 的 getter 需要 vmAsyncExecutor。这无法在构建时检查,因此在 vmAsyncExecutor 关闭时使用 Promise getter 会在运行时失败 - 解码器收到的是 Promise 对象而不是密钥字符串。

根据已验证的会话和所需的许可证检查授权密钥交付。仅凭 Origin 或 Referer 不能验证调用者身份,同源 GET 请求可能没有 Origin。禁用响应缓存。将密钥绑定到正确的构建版本,并与包一起部署。接收密钥的客户端可以在运行时查看它。

JavaScript

当密钥不匹配时

只有当 getter 返回与混淆期间所用完全相同的密钥时,混淆后的代码才能工作。如果密钥不同 - 或者 getter 返回 undefinednull 或空字符串 - 解密就会产生错误的密钥流,代码会在运行时以乱码输出或普通的运行时错误而失败。

这里有意不提供任何独特的、针对密钥的错误消息:一个失败的密钥与任何其他运行时故障无法区分。因此,当一个受 VM 保护的包只在启用此选项后才抛出异常时,请先检查密钥路径 - 确认 getter 在页面上能够解析、返回非空字符串,并且 返回与你构建时所用相同的值。