技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTETC

前端如何给 JavaScript 加密(不是混淆)?

所属馆藏
Other
文件格式
Markdown
原始路径
Other/10-前端如何给 JavaScript 加密(不是混淆)?
本文目录10 个章节
  1. 一、先说结论
  2. 二、加密、哈希、编码和混淆的区别
  3. 三、为什么“把 JavaScript 加密后再执行”不能保密
  4. 四、如果目标是保护 API 密钥和服务器秘密
  5. 五、HTTPS 与“前端先加密再提交”
  6. 六、需要真正加密数据时:Web Crypto API
  7. 七、不同威胁模型下的选择
  8. 八、如何降低 JavaScript 被轻易阅读的程度
  9. 九、上线前检查清单
  10. 参考资料

前端如何给 JavaScript 加密(不是混淆)?

Category(分类): Other Status: 持续更新

原文只有一个知乎问题链接:前端如何给 JavaScript 加密(不是混淆)?。这个问题最容易产生的误解是:把 JavaScript 加密后交给浏览器执行,就能让用户看不到代码或拿不到密钥。

一、先说结论

如果 JavaScript 必须在用户的浏览器中执行,就无法可靠地对用户隐藏它的源代码、逻辑和运行时秘密。

原因很简单:浏览器必须先拿到代码,再解密、解析和执行;解密所需的代码和密钥最终也会出现在用户控制的运行环境中。用户可以打开开发者工具、查看网络响应、格式化代码、调试函数、修改执行流程,或者在代码运行时读取明文和密钥。

因此应先明确目标:

真实目标更合适的方案
防止传输中被窃听或篡改HTTPS/TLS、HSTS、正确的证书配置
不让前端暴露服务器秘密秘密只放服务端,使用 BFF/API 代理和短期令牌
防止数据库泄露明文服务端使用经过认证的加密、密钥管理和访问控制
让服务端也不能读取用户数据端到端加密,密钥由用户控制;需要完整威胁模型
让普通用户不容易看懂代码压缩、Tree Shaking、混淆;只能提高阅读成本
验证脚本没有被替换HTTPS、CSP、SRI、构建签名和供应链保护
证明登录者是本人WebAuthn/Passkey、OAuth/OIDC 等标准认证方案
保护收费算法或核心规则把核心逻辑放到服务端,服务端进行授权和限流

加密、编码、哈希、混淆和压缩解决的是不同问题,不能混为一谈。

二、加密、哈希、编码和混淆的区别

1. 加密(Encryption)

加密使用密钥把明文转换成密文,拥有正确密钥的一方可以解密。它主要提供保密性,认证加密还可以提供密文完整性和篡改检测;这不等于单独证明了发送者的真实身份。

加密的安全性不在于算法代码没人看见,而在于密钥管理和经过公开审查的算法。AES-GCM、RSA-OAEP 等是算法和模式;Base64 不是加密。

2. 哈希(Hashing)

哈希是单向映射,适合完整性校验、内容寻址和密码存储中的一部分场景。密码不能用普通 SHA-256 或 MD5 直接保存,应使用 Argon2id、scrypt、bcrypt 或 PBKDF2 等专用密码哈希算法,并配合唯一盐。

3. 编码(Encoding)

Base64、URL 编码、Unicode 转义只是改变表示方式,不提供安全性。任何拿到编码字符串的人都可以还原它。

4. 混淆和压缩(Obfuscation/Minification)

混淆可以重命名变量、改变控制流、拆分字符串或增加阅读成本;压缩和 Tree Shaking 主要为了减少包体积。它们不能保护秘密,也不能阻止有经验的用户调试和还原逻辑。

即使把代码编译成 WebAssembly,模块仍然会被下载并在客户端执行,也不能把它当作不可逆的加密。

5. 完整性保护

SRI 可以让浏览器校验外部静态脚本是否匹配预先声明的哈希,CSP 可以限制允许加载脚本的来源,代码签名和锁文件可以减少供应链风险。这些措施解决的是“代码有没有被替换”,不是“用户能不能阅读代码”。

三、为什么“把 JavaScript 加密后再执行”不能保密

假设页面加载了:

<script src="/encrypted-app.js"></script>

要执行它,页面必须拥有类似以下能力:

const plainText = decrypt(cipherText, key)
eval(plainText)

无论 decryptkey 和明文放在哪里,用户都可以:

  • decrypt 返回的位置设置断点;
  • 读取网络面板中的密文和运行时数据;
  • 重写函数、Hook crypto.subtleeval
  • 直接调用业务函数,跳过界面上的限制;
  • 修改前端逻辑后继续向服务端发送请求。

因此客户端代码中的“隐藏密钥”只能阻挡非常初级的查看者,不能作为认证、授权、反作弊或商业秘密保护方案。重要的权限判断必须在服务端重复执行。

四、如果目标是保护 API 密钥和服务器秘密

1. 不要把秘密放到前端

以下内容一旦被打进浏览器包,就不再是秘密:

  • 数据库密码、云服务私钥、JWT 签名密钥;
  • 第三方服务的管理密钥;
  • 用于绕过权限的固定 Token;
  • CI/CD 密钥、内部接口地址和管理员凭据;
  • 写在 .env 中但被构建工具暴露给客户端的值。

VITE_NEXT_PUBLIC_、Nuxt 的公开运行时配置等变量只表示“允许暴露给客户端”,不表示它们安全。生产构建后应检查包和 source map,确认没有秘密泄露。

2. 使用服务端代理和短期凭证

更合理的架构是:

浏览器 → 自己的后端/BFF → 第三方服务

第三方密钥只保存在后端的 Secret Manager、KMS 或环境隔离的服务中。后端根据当前用户的身份和权限发起请求,并对参数、频率、额度和响应做限制。

如果业务确实需要让浏览器直接访问第三方服务,可以申请只读、限域名、限额度的公开标识,并把它当成可泄露的标识,而不是秘密;同时配置配额、来源限制、轮换和异常监控。

3. 认证和授权不能靠前端代码

前端隐藏按钮、加密接口参数或把权限写进混淆代码都不能阻止用户直接调用接口。服务端应校验:

  • 会话或访问令牌是否有效;
  • 当前用户是否有访问该资源的权限;
  • 请求参数是否属于当前用户;
  • 是否存在重放、越权、频率或额度问题。

对于登录和授权,优先采用经过审查的 OAuth 2.1/OIDC、PKCE、WebAuthn/Passkey 和服务端会话方案,不要自己设计“加密后传密码”的协议。

五、HTTPS 与“前端先加密再提交”

用户经常问:密码在前端再加密一次,是不是比 HTTPS 更安全?通常不是。

HTTPS/TLS 已经负责保护浏览器到服务端之间的机密性、完整性和服务器身份。前端再用一个固定公钥或固定算法加密,如果协议没有解决密钥来源、重放、错误处理、认证和降级,反而可能制造新的问题。

正确的密码流程一般是:

  1. 浏览器通过 HTTPS 连接服务端;
  2. 服务端接收密码并做必要的输入限制;
  3. 服务端使用 Argon2id、scrypt、bcrypt 或 PBKDF2 等慢哈希算法保存密码验证值;
  4. 服务端通过安全会话或短期令牌维持登录状态。

不要使用 MD5、SHA-1、普通 SHA-256、Base64、简单异或或自创算法替代 TLS 和密码哈希。客户端加密也不能防止已经被 XSS、恶意扩展或被入侵的浏览器读取用户输入。

六、需要真正加密数据时:Web Crypto API

浏览器提供 Web Crypto API,包括密钥生成、导入、加密、解密、签名和派生等能力。它通常要求安全上下文(HTTPS 或 localhost)。这是底层 API,调用成功不代表方案设计正确,尤其要重视密钥生命周期和威胁模型。

1. AES-GCM 对称加密示例

下面是一个仅用于说明 API 的示例:

async function createSessionKey() {
  return crypto.subtle.generateKey(
    { name: 'AES-GCM', length: 256 },
    false,
    ['encrypt', 'decrypt']
  )
}

async function encryptText(text, key) {
  const iv = crypto.getRandomValues(new Uint8Array(12))
  const encoded = new TextEncoder().encode(text)
  const ciphertext = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    key,
    encoded
  )

  return {
    iv, // IV 不需要保密,但必须和密文一起保存
    ciphertext: new Uint8Array(ciphertext)
  }
}

AES-GCM 是带认证的加密模式,解密时会验证密文有没有被篡改。实际使用要注意:

  • 同一个密钥下,绝不能重复使用 IV;
  • IV 通常使用随机的 12 字节值,不需要加密;
  • 可以使用 additionalData 绑定用户 ID、版本或资源类型等上下文;
  • 不要直接把 Uint8Array 当成字符串,传输时使用可靠的 Base64、Base64url 或二进制协议;
  • 密文需要版本、算法、IV 和密钥标识,方便未来轮换;
  • 不要选择 AES-ECB,也不要自行给 AES-CBC/CTR 拼一个不严谨的 MAC;
  • 不能把密钥硬编码进 JavaScript,也不能把可恢复的密钥和密文放在同一个公开位置。

extractable: false 可以减少通过 Web Crypto 导出密钥的机会,但当前页面的脚本仍可能在密钥使用时调用加密操作;如果发生 XSS,不能假设它能保护整个应用。

2. 密钥从哪里来

真正困难的不是调用 encrypt(),而是回答以下问题:

  • 谁能解密?
  • 密钥如何首次建立和恢复?
  • 用户换设备或忘记密码怎么办?
  • 服务器被入侵时是否仍不能解密?
  • XSS、恶意扩展、备份和日志会不会拿到明文?
  • 密钥如何轮换、撤销和审计?

如果密钥由页面固定写入,任何用户都能得到它;如果密钥由服务器直接发给浏览器,服务器和该浏览器会话通常都能得到它。只有在解密密钥由用户控制且不提交给服务端(例如由用户口令经专门 KDF 派生,或由客户端生成并受硬件保护的密钥作为解锁条件),并且客户端代码可信时,才可能实现特定意义上的端到端保护。WebAuthn 私钥对网页和 Relying Party 不可导出,主要用于签名和认证,不能直接当作通用 KDF 输入或 Web Crypto 加密密钥;同步型 Passkey 可能通过受保护机制复制到其他设备,但网页仍不能读取私钥。它最多可以参与认证后解锁客户端持有的密钥,设备迁移和恢复仍需单独设计。若登录密码已经提交给服务端,也不能直接把它当成“服务端不知道”的端到端密钥。

从用户口令派生密钥时,需要使用专门的 KDF、随机盐和合理的成本参数。Web Crypto 提供 PBKDF2;如果威胁模型要求更强的抗 GPU 能力,可以评估经过维护的 Argon2id 实现,但不要自行实现密码学算法。

3. 公钥加密和混合加密

如果只有服务端需要解密,可以把服务端的公钥放到前端,私钥只保留在服务端。浏览器使用 RSA-OAEP 等公钥加密算法加密数据,服务端使用私钥解密。

大数据通常不直接用 RSA 加密,而是采用混合方案:

  1. 浏览器随机生成一次性 AES-GCM 数据密钥;
  2. 使用 AES-GCM 加密业务数据;
  3. 使用服务端公钥加密或封装 AES 密钥;
  4. 把密文、IV、封装后的数据密钥和版本发送给服务端;
  5. 服务端用私钥解开数据密钥,再验证并解密数据。

公钥可以公开,私钥不能进入前端。公钥应通过 HTTPS、可信构建渠道或协议内置的密钥校验获得,否则攻击者替换公钥后可以实施中间人攻击。浏览器应用一般不要泛化使用自行实现的证书固定;HTTPS/TLS、可信证书体系和服务端密钥轮换是基础。即便使用公钥加密,也仍需处理认证、重放、请求绑定、大小限制和错误响应。

加密和签名也不要混淆:RSA-OAEP 主要用于加密,RSA-PSS/ECDSA 等用于签名。需要证明用户持有硬件密钥时,应优先评估 WebAuthn,而不是把一个私钥文件放在 localStorage

七、不同威胁模型下的选择

威胁需要保护的对象建议
网络窃听传输中的请求和响应全站 HTTPS/TLS、HSTS、证书和安全 Cookie
数据库泄露存储中的敏感字段服务端认证加密、KMS/HSM、密钥与数据分离
第三方 API 密钥泄露服务器凭据后端代理、Secret Manager、短期凭证和限额
服务端不应看到用户内容用户端到端数据端到端加密、用户控制密钥、明确恢复策略
普通人查看代码源码可读性压缩和适度混淆,只当作提高成本
脚本被 CDN 或依赖替换代码完整性HTTPS、CSP、SRI、锁文件、依赖审计和签名
客户端篡改业务规则授权和业务完整性服务端校验、审计、限流和风控

八、如何降低 JavaScript 被轻易阅读的程度

如果只是希望减少源码体积或提高普通用户的阅读门槛,可以:

  • 开启生产构建和压缩;
  • 删除 source map 的公开访问,改上传到受控错误监控平台;
  • 使用 Tree Shaking、代码分割和懒加载;
  • 对确实需要的代码做适度混淆,并评估调试、兼容性和性能成本;
  • 把高价值算法、授权和策略放到服务端;
  • 使用 CSP、Trusted Types、依赖锁定和供应链审计降低代码被替换风险。

不要把混淆当作:

  • 防止用户绕过权限;
  • 防止 API 被调用;
  • 隐藏云服务密钥;
  • 防止专业逆向;
  • 满足密码学或合规要求。

九、上线前检查清单

  • 前端包、source map、静态配置和错误日志中没有服务器秘密;
  • 所有权鉴权和金额/权限规则都在服务端执行;
  • 传输使用 HTTPS/TLS,不依赖自创的前端加密协议;
  • 密码在服务端使用专用慢哈希保存,不使用普通哈希或可逆加密;
  • 需要应用层加密时使用 Web Crypto 或成熟库,不自创算法;
  • AES 使用 GCM 等认证模式,同一密钥不重复使用 IV;
  • 密钥有生成、保存、轮换、撤销、备份和恢复策略;
  • 公钥、脚本和依赖有可信来源与完整性校验;
  • 已评估 XSS、恶意扩展、供应链攻击和被控制客户端的影响;
  • 对“代码保密”“数据保密”“代码完整性”分别设计方案。

参考资料

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS