HTTPS是前端加密不可绕过的前提,因HTTP下JS可被篡改、公钥可被替换;必须用fetch发送JSON格式加密payload,配合Base64编码与时间戳校验,且服务端须验证完整性。

表单数据不加密,就等于裸奔——哪怕用了 type="password" 或 autocomplete="off",只要没 HTTPS,Wireshark 抓包三秒就能看到身份证号和密码明文。
为什么 HTTPS 是唯一不可绕过的前提
所有前端加密(RSA、SubtleCrypto、JSEncrypt)都依赖 HTTPS 提供的可信上下文。HTTP 页面加载的 JS 可被中间人篡改,公钥可被替换,加密逻辑形同虚设。
- 浏览器地址栏没锁图标 → 不安全,立刻停用
- 证书过期、自签名、或 Nginx upstream 转发到
http://localhost:3000→ 属于“半程加密”,后端收的是明文 - iOS Safari 和新版 Chrome 会直接拒绝调用
SubtleCrypto.generateKey(),除非页面是有效 HTTPS 加载 - 检查方式:打开 DevTools → Security 标签页 → 看 “Connection secure” 是否显示为绿色
POST 提交 + 正确 enctype 才能承载加密 payload
用 enctype="application/x-www-form-urlencoded" 直接提交 Base64 加密字符串,会导致 +、/、= 被 URL 编码破坏,解密失败;用 FormData 更糟——它强制 multipart,后端根本收不到原始 ArrayBuffer。
- 必须用
fetch()手动构造请求,headers: {'Content-Type': 'application/json'} - 加密后输出是
ArrayBuffer,要转成 Base64:btoa(String.fromCharCode(...new Uint8Array(encryptedData)))(注意不是atob) - payload 推荐结构:
{"encrypted": "BASE64_STRING", "iv": "BASE64_IV", "timestamp": 1754425000000} - 别把加密字段塞进普通
<input name="data">再 submit()——这会触发表单默认行为,走错编码路径
Web Crypto API 构建加密 payload 的关键细节
加密不是把 JSON 字符串丢给 encrypt() 就完事。字段顺序、空值、特殊字符都会导致服务端解析失败。
立即学习“前端免费学习笔记(深入)”;
- 先构造标准对象,再按 key 字典序排序(避免不同浏览器 JSON.stringify() 顺序不一致)
- 用
new TextEncoder().encode(JSON.stringify(obj))得到Uint8Array,不是直接传字符串 - 公钥必须用
importKey('spki', publicKey, {name: 'RSA-OAEP'}, false, ['encrypt'])导入,类型写错会静默失败 - 加密后 IV 必须随 payload 一起发送,且长度需匹配算法(如 AES-GCM 常用 12 字节)
- 别在 JS 里硬编码公钥——由后端渲染时注入到
<script>window.PUBLIC_KEY = "..."</script>,防爬虫批量提取
最常被跳过的环节:服务端没校验 timestamp 和 IV 有效性,也没做 AEAD 解密后的完整性校验。加密了但没验签,等于锁了门却把钥匙焊在门把手上。



















