thinkphp敏感数据加密需在模型层用setattr/getattr实现透明加解密,推荐aes-cbc配合固定iv和base64编码,字段设varchar/text+utf8mb4_bin,查询须先加密条件值或双写明文索引,密钥从配置读取并按环境隔离。

ThinkPHP 实现敏感数据加密,核心不是选哪个函数,而是让加密逻辑稳定嵌入数据生命周期——写入时自动加密、读取时自动解密、查询时不踩坑。TP6 不提供开箱即用的字段级加密,必须靠模型层手动控制,否则极易漏掉批量更新、原生 SQL 或 Db::table() 场景。
用 setAttr / getAttr 做透明加解密
这是最轻量也最可靠的入口。对手机号、身份证号等字段,重写模型中的 setPhoneAttr 和 getPhoneAttr 方法:
- 加密必须用可逆算法,禁用 md5、password_hash、sha1 等不可逆方式
- 推荐 AES-128-CBC 或 AES-256-CBC,配合 think\facade\Crypt(TP6.1+ 内置)更安全可控
- 值为空时需主动返回空字符串或 null,避免 encrypt(null) 产生异常密文
- IV 不能随机生成(否则无法解密),也不宜硬编码;建议用
substr(hash('sha256', $key), 0, 16)衍生固定 IV
数据库字段与字符集必须匹配
加密后是二进制内容,经 base64 编码会变长且含 +、/、= 字符。若字段类型或排序规则不匹配,会导致截断、乱码甚至解密失败:
- 字段类型设为 VARCHAR(255) 或 TEXT,避免长度不足
- 排序规则必须用 utf8mb4_bin,禁用 utf8mb4_general_ci 或 utf8mb4_unicode_ci
- 不要用 INT、CHAR(11) 存手机号——加密后根本存不下
查询加密字段的正确姿势
加密后字段值每次不同(哪怕 IV 固定),where('phone', '13812345678') 永远查不到。强行用明文条件去比密文,等于大海捞针:
- 查单条:先调用相同加密逻辑处理查询值,再传入 where,如
where('phone', Crypt::encrypt(base64_encode('13812345678'))) - 高频查询:采用「明文索引 + 密文存储」双写,例如存
phone_md5(建索引,用于 where)和phone_enc(只读展示) - 禁止对加密字段做 LIKE、ORDER BY、GROUP BY —— 加密破坏数据规律,这些操作无意义
密钥管理与环境隔离
密钥一旦写死在模型里,上线换密钥就会全量数据失效。安全做法是:
- 密钥从配置读取:
config('app.aes_key'),且确保长度合规(16 字节对应 AES-128,32 字节对应 AES-256) - 开发、测试、生产环境使用不同密钥,通过 .env 文件隔离
- 切勿用 time()、rand()、IP 地址等动态值当密钥或 IV,稳定性优先
- Redis 缓存加速建议存
md5(phone) => user_id,而非加密后的 phone 字符串
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











