密钥轮换需设计版本路由与迁移路径,加密时附加版本标识,解密时按标识选择密钥和对应iv,旧数据通过懒迁移+双写逐步更新,密钥须由kms等外部服务管理,iv必须每次随机生成且与密文同存。

密钥轮换不是改个变量名就完事
直接替换 $secret_key 的值,旧数据解密会全部失败。PHP 没有内置“自动兼容多版本密钥”的加解密机制,必须在业务层设计密钥标识、版本路由和迁移路径。
用 openssl_encrypt / openssl_decrypt 时必须带密钥版本号
加解密不能只靠密钥内容,还得记录用的是哪一版密钥。常见做法是在密文前或后附加一个短标识(如 v1、v2),或者单独存字段 key_version。
- 加密时:先生成密文,再拼接版本前缀,例如
'v2'.openssl_encrypt($data, 'AES-256-CBC', $keys['v2'], 0, $iv) - 解密时:先读前2字节判断版本,再选对应密钥和 IV(注意:IV 必须与加密时一致,建议随密文存储)
- 别把版本号硬编码进算法逻辑——它得可配置、可扩展,比如从
$config['encryption']['active_key']动态读取
旧密钥数据迁移要分批做,不能停服重刷
线上已有百万条用 v1 密钥加密的用户 token 或支付凭证,不可能一次性全解密再用 v2 重写。得靠“懒迁移”+“双写”过渡:
- 新写入一律用当前活跃密钥(
v2),并记录key_version = 'v2' - 读取时,若发现
key_version === 'v1',先用v1解密,再立即用v2加密回写(异步队列更稳妥,避免拖慢主流程) - 加个统计埋点,监控
v1密文读取频次,降到趋近于 0 后才可安全下线v1密钥
密钥本身别明文写在 config.php 里
轮换密钥的前提是密钥管理可控。把 $keys = ['v1' => 'xxx', 'v2' => 'yyy'] 直接塞进 PHP 文件,等于把轮换变成改代码、发版、重启——这根本不算轮换,是发布事故预备动作。
立即学习“PHP免费学习笔记(深入)”;
- 密钥应由外部服务提供:Vault、AWS KMS、阿里云 KMS,或至少是环境变量 + secrets mount(Docker/K8s)
- PHP 中只保留密钥 ID(如
key_id: 'enc-key-prod-v2'),运行时按需拉取,支持热刷新 - 本地开发用
.env模拟,但确保.env不提交、不进镜像,且生产环境完全禁用该加载路径
最常被跳过的环节是 IV 的生命周期管理——它必须和密钥版本强绑定,每次加密都该用新 IV(random_bytes(16)),且和密文一起落库;复用 IV 等同于自废 AES。











