外部化字节码数组编码密钥
使用 vmBytecodeArrayEncodingKey 提供你自己的 VM 字节码加密密钥,并通过密钥 getter 在运行时将其交还 - 保存在包之外、从客户端存储读取,或从你的后端获取。
这些选项的作用
vmBytecodeArrayEncoding 会加密 VM 字节码数组,使其不会以明文形式出现在
输出中。默认情况下,加密密钥从环境派生并在客户端重建,因此你无需亲自处理它。这很方便,但密钥材料仍然存在于
包中。
有两个选项可以让你把密钥从包中取出并由自己掌控:
vmBytecodeArrayEncodingKey- 你在编译时提供的密钥。设置后,它会取代默认的环境派生密钥使用,并且不会 嵌入到混淆后的输出中。vmBytecodeArrayEncodingKeyGetter- 一个在运行时返回该相同密钥的 JavaScript 表达式。它会被原样嵌入, 并在混淆后的代码加载时于浏览器中求值。
关键在于分离:由于密钥不在代码中,对包进行纯粹的静态扫描无法恢复它。为了让代码运行,密钥仍然必须在运行时 存在,因此它并非真正保密 - 但你可以决定它来自何处以及谁能看到它。
这两个选项是一对。没有 getter 的 vmBytecodeArrayEncodingKey 会让混淆后的代码在运行时无法获取密钥,而没有
对应编译时密钥的 getter 则没有可与之匹配的对象。请连同 vmBytecodeArrayEncoding: true 一起设置这两者。
两个密钥如何结合
你的密钥从不单独使用 - 在两端它都会与混淆器控制的内部密钥混合:
- 编译时。
vmBytecodeArrayEncodingKey会与混淆器派生的内部密钥结合,然后用得到的混合密钥对字节码数组进行 编码。 - 运行时。 你的
vmBytecodeArrayEncodingKeyGetter解析出的值会与相同的内部密钥结合(该内部密钥根据各种运行时 因素在客户端重建),以此解码字节码。
由于两端都会将你的密钥与内部密钥混合,getter 必须解析为你作为 vmBytecodeArrayEncodingKey 传入的完全相同的
字符串。任何一部分单独都不够:没有内部密钥,你的密钥无法解码字节码;没有你的密钥,内部密钥也毫无用处 -
这正是为什么控制谁能收到你的密钥才是真正保护代码的关键。
在运行时提供密钥
默认情况下,getter 是同步的:当混淆后的代码加载时,表达式必须立即返回密钥。可以从任何已经存在于客户端的来源
读取 - cookie、localStorage、全局变量,或服务器注入的 DOM 元素。
密钥必须在混淆后的代码运行之前就存在:
其他同步来源的工作方式相同 - 选择你的应用已经填充好的那一种:
不要把密钥和混淆后的代码放在同一个文件或脚本中。将其内联在那里会彻底破坏整个目的 - 对包进行一次静态扫描
就能同时恢复代码及其密钥。请将密钥存放在单独的来源中,并从环境变量或密文中注入编译时的
vmBytecodeArrayEncodingKey,而不要将其提交到代码库。
从你的后端获取密钥(异步)
需要 vmAsyncExecutor · v7.3.0+同步 getter 只能读取已经在客户端上的内容。若要从你的服务器获取密钥 - 以便将其置于身份验证之后进行门控并
可撤销 - getter 就必须是异步的,而这需要 vmAsyncExecutor。启用异步
执行器后,getter 可以返回一个 Promise,VM 会在运行前等待它。
返回 Promise 的 getter 需要 vmAsyncExecutor。这无法在构建时检查,因此在 vmAsyncExecutor 关闭时使用
Promise getter 会在运行时失败 - 解码器收到的是 Promise 对象而不是密钥字符串。
根据已验证的会话和所需的许可证检查授权密钥交付。仅凭 Origin 或 Referer 不能验证调用者身份,同源 GET 请求可能没有 Origin。禁用响应缓存。将密钥绑定到正确的构建版本,并与包一起部署。接收密钥的客户端可以在运行时查看它。
当密钥不匹配时
只有当 getter 返回与混淆期间所用完全相同的密钥时,混淆后的代码才能工作。如果密钥不同 - 或者 getter 返回
undefined、null 或空字符串 - 解密就会产生错误的密钥流,代码会在运行时以乱码输出或普通的运行时错误而失败。
这里有意不提供任何独特的、针对密钥的错误消息:一个失败的密钥与任何其他运行时故障无法区分。因此,当一个受 VM 保护的包只在启用此选项后才抛出异常时,请先检查密钥路径 - 确认 getter 在页面上能够解析、返回非空字符串,并且 返回与你构建时所用相同的值。
