最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
HTML表单数据在客户端加密之后Payload构建与传输安全
时间:2026-06-18 09:53:08 编辑:袖梨 来源:一聚教程网
前端加密不能替代HTTPS,因为HTTP页面加载时JS可被篡改、公钥可被替换、明文可被截获,而SubtleCrypto等API依赖HTTPS提供的可信上下文和证书验证,仅当全链路HTTPS有效时密钥生成与加密才安全可靠。
为什么前端加密不能替代 HTTPS
客户端 JavaScript 加密(比如用 SubtleCrypto.encrypt())本身不解决传输通道安全问题。如果页面是 http:// 加载的,攻击者可以篡改 JS 文件、替换公钥、或直接拦截加密前的明文——加密逻辑和密钥都暴露在前端,形同虚设。
真正起作用的是 TLS 层加密:只有整个页面走 https://,浏览器才信任加载的脚本、证书才有效、SubtleCrypto 的密钥生成才不会被降级劫持。
- 证书必须有效且非自签名,否则 iOS Safari 或 Chrome 会阻止
SubtleCryptoAPI 调用 - 若后端用 Nginx 反代但 upstream 是
http://localhost:3000,那只是“前端加密 + 半程 HTTPS”,中间段仍是明文 - 别把公钥硬编码在 JS 里;应由后端在页面渲染时动态注入,避免被爬虫批量提取
如何用 Web Crypto API 构建加密 Payload
加密不是拼接字符串,而是按服务端约定的格式序列化后再加密。常见错误是先 JSON.stringify() 再加密,但没处理字段顺序、空值、特殊字符转义,导致解密后 JSON 解析失败。
推荐做法:构造标准对象 → 按 key 字典序排序 → JSON.stringify() → UTF-8 编码为 Uint8Array → encrypt()。
立即学习“前端免费学习笔记(深入)”;
- 密钥必须用
importKey()导入,类型设为"public",格式为"spki"(RSA 公钥)或"raw"(AES 共享密钥) - 加密输出是
ArrayBuffer,需转成 Base64(用btoa(String.fromCharCode(...))或Buffer.from(...).toString('base64'))再塞进表单字段 - 务必在加密前校验字段值:空字符串、
null、undefined都可能让后端解析出错
POST 请求中如何携带加密数据
加密后的 payload 不能直接当 form 的普通字段提交,因为 enctype="application/x-www-form-urlencoded" 会破坏二进制数据。必须用 fetch() 手动构造请求体。
- 设置
method: 'POST'和headers: {'Content-Type': 'application/json'} - 将加密结果包进标准 JSON 结构,如
{ "encrypted": "BASE64_STRING", "iv": "BASE64_IV", "timestamp": Date.now() } - 不要用
FormData提交加密数据——它默认用multipart/form-data,后端收不到原始 payload - 若后端只认
application/x-www-form-urlencoded,则需将加密字段名设为data,值为 Base64 字符串,并确保 URL 编码正确(encodeURIComponent())
容易被忽略的密钥与上下文管理
Web Crypto 的密钥对象(CryptoKey)无法序列化,也不能跨 iframe 或 service worker 共享。每次页面加载都得重新导入,且密钥生命周期必须严格控制。
- 别在
localStorage存私钥——哪怕加密了,也违背密钥隔离原则 - RSA 公钥可缓存,但建议每次页面加载时从后端
/api/public-key接口获取,附带ETag避免重复请求 - 加密前检查
navigator.credentials是否可用,某些隐私模式下SubtleCrypto会被禁用 - 服务端解密失败的常见原因是 IV 不一致、padding 方式不匹配(如后端用 PKCS#7,前端用了默认 padding)、或时间戳过期(用于防重放)