仅靠WSS不够安全,必须应用层AES加密;RSA因性能低、长度限、无IV等缺陷,只适合加密临时AES密钥,而非每条消息;AES-256-CBC需每次随机IV、32字节密钥、IV与密文拼接传输。

WebSocket 消息光靠 wss:// 不够安全——TLS 只保传输层,服务端解密后若直接处理明文,攻击者一旦突破证书验证或拿到内存快照,数据就裸奔了。必须在应用层加一道加密,而对称与非对称不是二选一,是分阶段配合用。
为什么不能只用 RSA 加密每条 WebSocket 消息
RSA 加密吞吐低、有长度限制(比如 2048 位密钥最多加密 245 字节),直接拿它加密聊天消息或实时指令,PHP 进程 CPU 会明显打高,延迟飙升。它适合干一件事:安全传递一个临时的对称密钥。
- 每次发消息都调用
openssl_public_encrypt(),性能开销大,不适合高频帧(如每秒 10+ 条) - RSA 密文比原文长,WebSocket 帧可能超限(尤其移动端弱网络下易触发断连)
- 没有 IV(初始化向量),相同原文加密结果固定,存在模式分析风险
AES-256-CBC 是当前最实用的 WebSocket 消息加密方式
对称加密快、可控、标准成熟,AES-256-CBC 在 PHP 中通过 openssl_encrypt() 和 openssl_decrypt() 即可完成,关键是 IV 必须随机且随密文一起传,不能硬编码或复用。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
-
$iv = openssl_random_pseudo_bytes(16)每次加密前都生成新 IV,不可缓存复用 - 密文和 IV 必须拼接传输(如
base64_encode($iv . $encrypted)),否则解密必失败 - 绝对避免
AES-128-ECB——ECB 模式无扩散性,相同原文块加密后完全一致,极易被识别结构 - 密钥长度必须严格匹配:256 位 = 32 字节,
strlen($key) !== 32会导致静默失败或乱码
如何用 RSA 安全交换 AES 密钥(混合加密起点)
客户端首次连接 WSS 后,不急着发业务消息,先走一次「密钥协商」:服务端下发公钥(如 PEM 格式),客户端用它加密一个随机生成的 32 字节 AES 密钥,再发回服务端;服务端用私钥解出该密钥,后续所有消息均用此密钥 AES 加密。
- 公钥可明文下发(
file_get_contents('public.key')),但私钥必须严格权限控制(chmod 600)且不进 Git - 客户端加密后发送的密钥需 base64 编码,服务端用
openssl_private_decrypt()解密,失败要立即断连 - 该 AES 密钥不应长期复用,建议每个会话生命周期内唯一,断连即丢弃
- 不要把 AES 密钥塞进 Cookie 或 URL——
ws://host/?key=xxx是典型反模式,会被日志/代理记录
decryptMessage() 解密失败的三个高频原因
PHP 端解密函数看似简单,但实际部署时近七成问题出在数据格式或环境配置上,不是算法写错。
- 收到的
$payload是前端未做base64_encode()的原始二进制流,base64_decode()返回 false,后续substr()全错 - 服务端 OpenSSL 版本低于 1.1.1,不支持某些 CBC 填充方式,需统一测试
openssl_get_cipher_methods()是否含AES-256-CBC - 客户端用 UTF-8 发送消息,但服务端解密后没做
mb_convert_encoding($plain, 'UTF-8', 'auto'),中文变乱码,误判为解密失败
真正难的不是写对那几行 openssl_* 调用,而是确保 IV 随机性、密钥生命周期可控、前后端编解码严格一致——这些细节不卡点测试,上线后根本看不出哪里漏了。

















