本文目录导读:

针对PHP项目的“密评”(密码应用安全性评估)以及密码应用(国密或国际算法)的合规性,核心目标是确保系统在身份鉴别、通信安全、数据存储、密钥管理等方面满足国家标准(如GM/T 0054、GB/T 39786等)。
以下是针对PHP项目进行密评改造与密码应用落地的系统性指南:
明确合规要求(标准依据)
在动手改造前,先搞清楚你的系统属于哪个等级(主要是等保二级或三级):
- 等保三级系统:强制要求使用密码技术进行身份鉴别、通信加密、数据完整性保护和数据机密性保护。
- 国密合规:部分政务、金融、医疗等行业明确要求优先使用SM系列算法(SM2/SM3/SM4)。
- 密评指标:关注“密码应用技术要求”中的物理与环境、网络与通信、设备与计算、应用与数据四个层面。
PHP项目常见的薄弱点(密评扣分项)
- HTTP明文传输:未强制HTTPS,或TLS协议版本过低(< 1.2)。
- 弱哈希存储密码:使用MD5、SHA-1直接存储用户密码。
- 硬编码密钥:数据库密码、API密钥、JWT Secret直接写在PHP文件或配置文件中。
- 缺乏完整性校验:用户请求参数、关键文件(如配置文件)未被签名或校验。
- 本地密码库缺失:未集成国密算法的PHP扩展(如
libsm、gmssl-php)。
改造实施步骤(PHP实战)
身份鉴别(密码登录/令牌)
-
密码哈希:使用
password_hash()(默认使用bcrypt),若需国密,则用SM3结合Salt,但需自定义实现。推荐做法:使用标准bcrypt/argon2,除非监管硬性要求SM3。// 标准做法(推荐) $hash = password_hash($password, PASSWORD_BCRYPT); if (password_verify($password, $hash)) { ... } // 国密做法(需扩展) $sm3Hash = sm3($password . $salt); -
会话管理:Session ID必须足够随机(
session_create_id()),且必须使用HTTPS的secure flag。
通信安全(HTTPS与TLS)
- 强制HTTPS:在Nginx/Apache端配置,PHP层面进行协议检查。
// 检查是否强制HTTPS if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') { header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); exit; } - TLS版本:确保服务器支持TLS 1.2或1.3。
数据存储加密(关键字段)
- 敏感字段(身份证、手机号):使用对称加密,推荐国密SM4或国际AES-256-GCM。
- 不要直接用固定密钥+ECB模式。
- 使用AES-256-GCM(PHP 7.1+原生支持)或SM4-GCM。
// AES-256-GCM 加密(PHP原生) $key = base64_decode($env_key); // 密钥从环境变量读取 $iv = random_bytes(12); // 初始向量 $ciphertext = openssl_encrypt($data, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag); $stored = base64_encode($iv . $tag . $ciphertext);
- 国密SM4:需安装
sm4-php扩展或调用HSM(硬件加密机)接口。
数据完整性(防篡改)
- 数据签名:适用于API接口参数、支付回调,使用HMAC(推荐SHA256)或SM2数字签名。
// HMAC-SHA256 $signature = hash_hmac('sha256', $data, $secret); - JWT令牌签名:不要用
none算法,使用HS256或RS256(非对称,更安全)。
密钥管理(最容易忽略)
- 禁止硬编码:密钥必须从环境变量(
.env)、配置中心或KMS(密钥管理系统)获取。// 不要写死:$key = '123456'; // 正确做法: $key = getenv('ENCRYPTION_KEY'); - 密钥轮换:缓存密钥版本号,支持旧密钥解密历史数据,新密钥加密新数据。
- 密钥存储:生产环境密钥务必保存在HSM或专门的密钥服务器上,PHP通过SDK调用。
PHP国密扩展集成(如需)
如果需要满足GM/T 0054等纯国密要求,你需要集成国密库,以下是成熟方案:
| 方案 | 支持算法 | 安装方式 | 适用场景 |
|---|---|---|---|
| GmSSL-php | SM2, SM3, SM4 | 编译安装 gmssl 扩展 |
最全面的国密支持,但维护较慢 |
| CFCA国密SDK | SM2, SM3, SM4, SM9 | 商业SDK,API调用 | 金融、政府项目 |
| OpenSSL 1.1.1+ (OID) | SM3, SM4(通过OID标识) | 系统级配置 | 如果OpenSSL编译时支持国密(如麒麟系统) |
| 纯PHP实现 (lizhichao/sm-crypto) | SM2, SM3, SM4 | Composer包 | 轻量、无依赖,但性能一般,适合低并发 |
推荐做法:在Linux服务器上安装GmSSL扩展,性能最优,通过Composer引入lizhichao/sm-crypto作为备选降级方案。
密评自检清单(PHP相关)
检查项目是否满足以下条件:
- [ ] 通信:全站HTTPS,TLS 1.2+,证书有效期>1年。
- [ ] 身份:密码长度>8位,复杂度混合,使用安全哈希(bcrypt/argon2/SM3),且有登录失败锁定机制(配合密码技术,如失败次数加密存储)。
- [ ] 数据:数据库中敏感字段(手机、身份证、银行卡)已加密存储(SM4或AES-256-GCM)。
- [ ] 密钥:密钥从外部配置文件或环境变量读取,不硬编码;有密钥定期轮换机制。
- [ ] 日志:日志中不打印明文密码/密钥,日志本身有完整性保护(如签名)。
- [ ] 文件:PHP配置文件权限600,禁止Web直接访问
.env。
避坑指南(常见错误)
- 自己造轮子:不要用
base64或xor做加密,不要自己实现哈希算法,用openssl_*或libsodium(PHP 7.2+内置)。 - 忽略IV随机性:每次加密必须重新生成随机IV,不要固定。
- 密钥与数据未分离:加密用的密钥不要和数据库连接配置放在同一个文件。
- 忽略“随需解密”:不要对整个数据库表加密(PHP无法做条件查询),只加密敏感字段的列。
- SM2误用:SM2签名和加密使用不同的密钥对(一般签名密钥对+加密密钥对)。
- 依赖包过时:部分GitHub上的国密Composer包可能6个月未更新,需审查代码质量。
总结流程图(改造思路)
[现状审计] --> [确定算法要求:国际 or 国密?]
|
v
[通信层] --> 全站HTTPS + TLS 1.2+ (检查证书链)
|
v
[应用层]
├── 登录: 强哈希(php password_hash) + 登录失败次数加密限定
├── API: 请求参数签名(HMAC-SM3) + 时间戳防重放
├── JWT: 非对称签名(RS256/SM2)
└── 数据: AES-256-GCM(国际) / SM4-GCM(国密) -> 随机IV
|
v
[密钥层]
├── 从Env/Config Center/HSM读取
├── 定期轮换 (key_version机制)
└── PHP进程间不能泄露密钥 (禁止日志输出)
|
v
[测试验证] --> 工具: 密评工具箱 / OpenSSL命令验证 / 渗透测试
建议结合你的系统等级保护定级(二级/三级)和行业监管要求(如银保监、网信办)来制定具体的密码应用方案,如果是纯内部系统且无硬性国密要求,优先使用国际标准算法(AES-256-GCM、SHA-256、ECDSA等),生态更成熟,PHP原生支持更好。