PHP域名加密须先标准化再AES-256-GCM加密:统一小写、去端口、可选去www;密钥用random_bytes(32),IV每次重置,密文含IV+tag+密文三部分;解密须校验tag和域名合法性,密钥轮换需兼容新旧密钥并重加密旧数据。
PHP中域名加密前必须先标准化
直接对原始 $_SERVER['HTTP_HOST'] 或 $_SERVER['SERVER_NAME'] 加密是危险的——不同访问方式(带www、不带www、含端口、HTTPS重定向)会导致同一站点产生多个“不同域名”,后续校验无法匹配。必须先做归一化处理:
- 统一转小写:
strtolower($host) - 剥离端口(如
:8080)和协议头(https://已不在HTTP_HOST中,但需注意反向代理场景) - 可选:强制去 www(
preg_replace('/^www\./i', '', $host)),但需确认业务是否允许子域共用密钥 - 最终建议格式:
$canonicalDomain = rtrim($host, '.')(防尾部点号)
用 openssl_encrypt() 替代 mcrypt 或简单 base64
mcrypt 已被废弃,base64_encode() 不是加密,只是编码,毫无安全性。真实加密必须用现代 AEAD 模式:
- 推荐算法:
'aes-256-gcm'(PHP 7.1+),兼顾安全与性能 - 密钥必须由
random_bytes(32)生成,**绝不能硬编码字符串或用 md5/SHA 拼接** - 每次加密必须用新 IV:
$iv = random_bytes(openssl_cipher_iv_length('aes-256-gcm')) - 完整密文需保存三部分:密文 + IV + 认证标签(
$tag),缺一不可,否则解密失败
示例关键片段:
$cipher = 'aes-256-gcm';
$key = hex2bin('your-32-byte-hex-key-from-config'); // 实际应从环境变量或密钥管理服务读取
$iv = random_bytes(openssl_cipher_iv_length($cipher));
$tag = '';
$ciphertext = openssl_encrypt($domain, $cipher, $key, OPENSSL_RAW_DATA, $iv, $tag);
$stored = base64_encode($iv . $tag . $ciphertext); // 合并后 Base64 存数据库字段
解密时验证完整性比解密本身更关键
攻击者可能篡改存储的密文,若只检查 openssl_decrypt() 返回 false 就报错,会暴露侧信道(如通过响应时间判断密文结构)。正确做法: