websocket默认明文传输,wss://仅保传输管道安全,无法防服务端日志泄露、xss跨tab窃取及角色化消息控制;必须在应用层用rsa+aes混合加密,前端每次会话随机生成aes密钥并用服务端公钥加密传输。

WebSocket 默认是明文传输,ws:// 连接下用 Fiddler 或 Charles 抓包就能直接看到原始消息——哪怕用了 TLS(即 wss://),只要服务端或客户端没做应用层加密,中间代理、内网嗅探、甚至恶意浏览器扩展仍可能拿到解密后的数据。所以“前端 RSA/AES 加密 WebSocket 消息”不是锦上添花,而是生产环境里对敏感字段(如用户身份、支付指令、私聊内容)的刚性要求。
为什么不能只靠 wss://?
wss:// 确实能防网络层窃听,但它只保护「传输管道」,不保护「消息内容本身」。一旦连接落地到服务端内存、被日志误打、或被同源页面其他 JS 读取,明文就暴露了。更关键的是:如果服务端要广播消息给多个客户端,而各客户端持有的解密能力不同(比如有的只该看脱敏数据),仅靠 TLS 完全无法实现细粒度控制。
- 服务端日志里记录的是解密后的内容 → 明文泄露风险
- 前端多个 tab 共享 WebSocket 实例 → 一个 tab 被 XSS 攻击,其他 tab 的消息也危险
- 服务端需向不同角色转发同一消息 → 无法按角色动态加密(比如管理员看全量,普通用户看掩码)
前端用 RSA + AES 混合加密的实操要点
核心逻辑是:用服务端公钥加密临时生成的 AES 密钥,再用该 AES 密钥加密消息体。这样既避免了对称密钥硬编码,又规避了纯 RSA 加密长消息的性能问题。
- 前端每次建立 WebSocket 连接前,先通过 HTTP 接口获取服务端 RSA 公钥(建议带签名防篡改),存入
sessionStorage,不要存在全局变量或window上 - AES 密钥必须每次会话随机生成,长度严格为 32 字节(对应 AES-256),IV 同样随机且每次不同,不可复用
- RSA 加密时,用
jsrsasign或 Web Crypto API 的importKey+encrypt,注意格式:服务端给的 PEM 公钥需去掉头尾并 base64 解码后再导入 - 发送消息结构建议统一为:
{ "encryptedKey": "...", "iv": "...", "data": "..." },后端据此选择解密路径
示例片段(Web Crypto):
const aesKey = await crypto.subtle.generateKey('AES-CBC', true, ['encrypt', 'decrypt']);
const exported = await crypto.subtle.exportKey('raw', aesKey.key);
const keyBytes = new Uint8Array(exported); // 32-byte key
// 后续用 keyBytes 和随机 iv 加密消息体
后端解密时最容易踩的坑
前端加密再漂亮,后端一步解错,整条链路就断。常见失败点集中在密钥处理和填充逻辑上。
- RSA 解密得到的 AES 密钥字节数不对 → 检查前端是否用了 PKCS#1 v1.5 填充(PHP
openssl_private_decrypt默认用这个),而非 OAEP;Node.js 的crypto.privateDecrypt需显式传constants.RSA_PKCS1_PADDING - AES 解密报错
data not multiple of block length→ 前端拼接iv + cipherText后做了 base64 编码,后端 base64 解码后必须严格取前 16 字节为 IV,剩余为密文,不能直接传 whole buffer 给解密函数 - 中文乱码或 JSON parse 失败 → 解密后得到的是 Uint8Array,需用
new TextDecoder().decode()(JS)或mb_convert_encoding($str, 'UTF-8', 'UTF-8')(PHP)确保 UTF-8 正常解析 - 同一个用户多设备登录 → 后端 Map 存储的「前端公钥」必须绑定 sessionId 或 socketId,不能只按 userId 覆盖,否则新连接会把旧连接的密钥冲掉
别忽略密钥生命周期管理
混合加密的安全性,70% 取决于密钥怎么管,而不是算法多炫酷。
- 服务端 RSA 私钥绝不能出现在前端代码、HTML 注释、或未授权接口响应里;PHP 中应放在
/var/www/private/这类 Webroot 外路径 - 前端生成的 AES 密钥,只在当前 WebSocket 实例生命周期内有效;连接关闭或重连时必须丢弃并重建,不能缓存复用
- 如果业务允许,可考虑每 N 条消息轮换一次 AES 密钥(类似 TLS 的 key update),但需前后端严格同步计数器,复杂度陡增,中小项目慎用
真正难的从来不是调通 openssl_encrypt 或写对 SubtleCrypto.decrypt,而是让密钥不落地、不跨域、不复用、不日志化——这些细节没有银弹,只有逐层卡死。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











