SQL 数据加密存储方法与案例

1次阅读

mysql中aes_encrypt返回blob导致乱码,需用to_base64()存储、from_base64()还原后解密;密钥长度必须为16/24/32字节,否则静默返回NULL;推荐使用确定性哈希辅助查询,避免加密字段直接where。

SQL 数据加密存储方法与案例

MySQL 中用 AES_ENCRYPT 存敏感字段,但查出来是乱码?

不是加密失败,是默认返回 BLOB 类型,直接 select 会显示十六进制或乱码。必须显式转成可读格式,比如用 HEX()TO_BASE64() 存,对应用 UNHEX() / FROM_BASE64() 解。

  • 存的时候别直接 INSERT INTO user (pwd) VALUES (AES_ENCRYPT('123', 'key')),而要写成 INSERT INTO user (pwd) VALUES (TO_BASE64(AES_ENCRYPT('123', 'key')))
  • 查的时候得先还原再解密:SELECT AES_DECRYPT(FROM_BASE64(pwd), 'key') FROM user
  • 密钥长度必须是 128/192/256 位(即 16/24/32 字节),传错长度会静默失败,返回 NULL —— 这是最常踩的坑
  • AES_ENCRYPT 默认用 AES-128-CBC,不带 IV;如果需要更安全的随机 IV,得自己生成并拼接存储,MySQL 原生不支持自动管理 IV

postgresql 里 pgcrypto 的 encrypt() 总报 “wrong key Length

因为 encrypt() 要求密钥必须是二进制字节流,不是字符串。你传 'mykey',它按 UTF8 算长度是 5 字节,但 AES-128 要 16 字节,直接报错。

  • 正确做法是用 convert_to('mykey', 'utf8') + md5() 截取,或者更稳妥地用 digest('mykey', 'sha256') 得到 32 字节密钥
  • 加密函数推荐用 pgp_sym_encrypt(),它自动处理密钥派生、IV、填充和 Base64 编码,存出来就是可读字符串,不用手动转码
  • pgp_sym_encrypt('data', 'key', 'cipher-algo=aes256') 比裸用 encrypt() 更适合业务场景,但注意它依赖 pgcrypto 扩展已启用

sqlite 没内置加密函数,真要加怎么办?

官方 SQLite 不支持透明加密,sqlite3 命令行工具也无 AES_ENCRYPT。所谓“加密 SQLite”基本靠三类方案:应用层手动加解密、第三方扩展(如 SQLCipher)、或文件系统级加密。

  • 别在 SQL 里写 ENCRYPT(data, key) —— 这语法根本不存在,会报 no such function: ENCRYPT
  • SQLCipher 是最常用选择,但它是独立编译版本,普通 pip install pysqlite3 不带它;要用就得装 sqlcipher3 包,并用 PRAGMA key = 'x'xx' 开启
  • 如果只是临时保护配置字段,建议在应用代码里用 Python 的 cryptography 库做 AES-GCM 加密,存进 TEXT 字段,比折腾数据库扩展更可控

加密后 WHERE 查询变慢,甚至没法走索引?

对加密字段做 WHERE encrypted_col = AES_ENCRYPT(?, ?) 看似合理,实际等于每次查询都重新加密一次,还无法利用索引 —— 因为加密结果依赖 IV 或随机盐,相同明文加密后值不同。

  • 想按加密字段查,唯一靠谱方式是确定性加密(如 AES-SIV 或 HMAC+AES),但主流数据库都不原生支持;MySQL 8.4+ 的 AES_ENCRYPTMODE=ECB 虽然确定性,但 ECB 极不安全,别用
  • 常见折中:把关键查询字段(如手机号)额外存一个加盐哈希(SHA2(CONCAT(phone, salt))),用于等值查找,加密字段只存完整敏感内容
  • 记住:加密不是索引友好操作。一旦字段加密了,就默认它只能做「取出来再解密后处理」,别指望在数据库里高效过滤

密钥轮换、字段粒度控制、加密与业务逻辑耦合程度——这些才是上线后真正卡住手脚的地方,比选哪个函数难得多。

text=ZqhQzanResources