aes-gcm是唯一可落地的敏感字段加密方案,因它原子化处理加密、认证与nonce,避免cbc模式下iv复用、mac缺失等高危风险;phone和idcard必须加密且配独立nonce,passwordhash绝不可再加密,email按合规要求选择性加密。

敏感字段必须加密落盘,但不能碰 password_hash;AES-GCM 是唯一可落地的方案,其他方式要么不安全(base64),要么不可逆(md5/sha256),要么易出错(CBC 手搓)。
哪些字段该加密、哪些绝不能动
用户资料里混着 Name、Email、Phone、IDCard、PasswordHash,但加密不是全量覆盖:
-
PasswordHash已是加盐哈希值,再 AES 加密会破坏验证逻辑,且徒增风险 -
Phone和IDCard属于强敏感字段,必须加密,且需独立 nonce -
Email视合规要求而定——GDPR 下建议加密,内部系统可暂不加密 - 所有加密字段在结构体中用
encrypt:"true"标签标识,避免硬编码字段名
为什么必须用 aes.GCM,而不是 crypto/cipher.CBC
CBC 模式要自己管 IV、填充、MAC 校验,三处一错就崩:
- 复用 IV → 相同明文生成相同密文 → 身份证号等低熵字段直接暴露规律
- 漏校验 MAC → 攻击者篡改密文后仍能解出乱码,业务层无感知
-
aes.GCM把加密、认证、nonce 绑定为原子操作,标准库已通过 FIPS 验证路径 - 别碰
github.com/square/go-jose或老版jwt-go—— 它们不是为字段加密设计的,序列化耦合太重
nonce 和密钥怎么生成才安全
这两处是 panic 和静默失败的高发区:
- 密钥长度必须严格为 16 / 24 / 32 字节:
sha256.Sum256([]byte(os.Getenv("ENCRYPTION_KEY"))).Sum(nil)[:32] - 绝不能用
[]byte("my-key")—— 长度不对直接 panic:crypto/aes: invalid key size -
nonce每次加密都得新生成:crypto/rand.Read(nonce[:]),长度固定为 12 字节(aesgcm.NonceSize()) - 禁止用时间戳、自增 ID、用户 ID 当
nonce—— 它们不具备密码学随机性 - 密钥绝不硬编码,走 KMS 或至少从权限为
0600的本地加密文件读取
数据库字段和 ORM 怎么配合加解密
目标是透明:写入时自动加密,查询时自动解密,不改业务代码:
- PostgreSQL 用
BYTEA,MySQL 用BLOB—— 别用TEXT存 base64,多一层编解码开销且易被误查 - 密文长度 = 明文 UTF-8 字节数 + 12(nonce)+ 16(auth tag),手机号 11 字节 → 至少预留 39 字节
- GORM 中实现
driver.Valuer和sql.Scanner接口,把加解密塞进 ORM 流程 - 解密失败时只返回
cipher.ErrAuthentication或cipher.ErrInvalidLength,必须显式判 err,不能靠返回切片长度判断
最常被忽略的是错误处理和字段边界:解密后不检查 utf8.Valid,可能把篡改后的非法 Unicode 当正常字符串用;空字段是否加密也要统一约定,否则攻击者能靠加密与否反推字段是否存在。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











