Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql字段级加密

在MySQL中实现字段级加密功能

作者:夜雪一千

字段级加密(列加密),就是只对表中指定敏感列做加密,普通字段保持明文,常用于手机号、身份证、银行卡、客户隐私信息,本文给大家介绍在MySQL中实现字段级加密功能,感兴趣的朋友跟随小编一起看看吧

MySQL 本身不提供字段自动加密功能,最常用且可行的方法是使用内置的 AES_ENCRYPT() / AES_DECRYPT() 函数在应用层或 SQL 层手动实现。‌‌

博客正文,Markdown格式,承接上一篇MySQL加密文章,聚焦字段级加密的3种实现方式、代码示例、优缺点、生产坑点。

前言

字段级加密(列加密),就是只对表中指定敏感列做加密,普通字段保持明文。常用于手机号、身份证、银行卡、客户隐私信息。
MySQL 字段加密一共3种主流实现方式:

  1. MySQL 内置 AES_ENCRYPT / AES_DECRYPT 函数
  2. MySQL 8.0.16+ InnoDB 原生列加密(Column-Level Encryption)
  3. 应用层加密(生产最推荐):业务代码完成加解密,数据库只存密文

重点提醒:生产环境优先应用层加密,不推荐直接裸用MySQL内置AES函数,下文会说明原因。

一、方式1:MySQL内置AES_ENCRYPT / AES_DECRYPT

MySQL 内置AES,算法为AES-128(可调整),加密返回二进制,字段类型必须用 VARBINARY / BLOB,不能用VARCHAR。

1. 建表语句

CREATE TABLE user_info (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    phone VARBINARY(256),    -- 存储手机号密文
    id_card VARBINARY(256)   -- 身份证密文
);

2. 插入加密数据

INSERT INTO user_info(username, phone, id_card)
VALUES (
    '张三',
    AES_ENCRYPT('13800138000', 'my-secret-key-123'),
    AES_ENCRYPT('500101199001011234', 'my-secret-key-123')
);

3. 查询解密

SELECT
    id,
    username,
    AES_DECRYPT(phone, 'my-secret-key-123') AS phone,
    AES_DECRYPT(id_card, 'my-secret-key-123') AS id_card
FROM user_info;

⚠️ 严重缺陷(生产慎用)

  1. 密钥写在SQL语句中:开启binlog、慢查询日志、通用日志时,SQL会被记录,密钥跟着泄露;
  2. 无法对密文字段做 LIKE 模糊查询;
  3. 密钥管理困难,密钥硬编码在SQL;
  4. 所有能执行SQL的账号都可以传入密钥解密;
  5. MySQL5.7 / 8.0 字符集差异容易出现解密乱码。

适用场景:仅本地测试、学习演示,禁止直接上生产。

二、方式2:MySQL 8.0.16+ InnoDB 原生列加密

MySQL 8.0.16 引入的原生列加密,加密密钥由MySQL密钥环(keyring)管理,密钥不会出现在SQL、binlog、慢日志,安全性远高于AES_ENCRYPT。

前置条件

  1. MySQL ≥ 8.0.16,InnoDB引擎;
  2. 提前加载密钥环插件(keyring_file / keyring_aws);
  3. 仅支持InnoDB表。

建表示例

CREATE TABLE user_secret (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50),
    phone VARCHAR(20) ENCRYPTED,
    id_card VARCHAR(18) ENCRYPTED
) ENGINE=InnoDB;

插入、查询和普通SQL完全一样,自动加解密:

INSERT INTO user_secret(username, phone, id_card)
VALUES ('张三', '13800138000', '500101199001011234');
SELECT * FROM user_secret;

数据库引擎自动完成解密,只有拥有解密权限的用户才能看到明文。

优缺点

✅ 优点

❌ 缺点

适用场景:云数据库MySQL企业版,对数据库内部访问有权限隔离需求。

三、方式3:应用层加密(生产推荐方案)

应用层加密 = 在Python/Java/Go等业务代码完成AES加解密,数据库只接收密文,密钥全程不经过MySQL。
MySQL只负责存储二进制密文,完全看不到密钥,binlog里也只有密文。

流程

  1. 用户提交手机号/身份证 → 应用拿到明文;
  2. 应用使用AES-GCM(推荐,带完整性校验)加密明文;
  3. 将密文存入MySQL VARBINARY;
  4. 查询时:取出密文 → 应用解密 → 返回前端;
  5. 密钥保存在KMS/密钥管理系统,禁止硬编码在代码配置文件。

Python极简示例(AES-GCM)

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
# 密钥从KMS获取,禁止写死代码
key = os.urandom(32)
def encrypt(plain_text: str):
    iv = os.urandom(12)
    cipher = Cipher(algorithms.AES(key), modes.GCM(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(plain_text.encode("utf-8")) + encryptor.finalize()
    return iv + ciphertext + encryptor.tag
def decrypt(raw_data):
    iv = raw_data[:12]
    tag = raw_data[-16:]
    ciphertext = raw_data[12:-16]
    cipher = Cipher(algorithms.AES(key), modes.GCM(iv), tag, backend=default_backend())
    decryptor = cipher.decryptor()
    return decryptor.update(ciphertext) + decryptor.finalize()
# 加密后bytes直接存入MySQL VARBINARY字段
cipher_data = encrypt("13800138000")

SQL建表:

CREATE TABLE user_app_enc (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50),
    phone VARBINARY(512),
    id_card VARBINARY(512),
    phone_hash CHAR(64) -- SHA256哈希,用于等值查询
);

等值查询配套方案:
新增 phone_hash 字段,存储手机号SHA256哈希。
查询时前端传入手机号,应用计算哈希,WHERE phone_hash = ? 匹配,不解密全表。

✅ 优点

  1. 密钥不进入数据库,binlog、慢日志只有密文;
  2. DBA即使拿到数据库全部数据,没有密钥无法解密;
  3. 算法自主可控,可以选择AES-GCM、SM4国密;
  4. 不受MySQL版本限制,5.7 / 8.0都可以使用。

❌ 缺点

这是互联网业务最主流方案。

四、字段加密后的检索问题(重点)

无论哪种字段加密,密文本身不能like模糊查询

  1. 等值查询:新增哈希摘要列 SHA256,仅用于匹配;不可逆,不能解密原文;
  2. 模糊查询:
    • 方案1:业务层解密全量数据过滤(大数据量性能灾难,慎用)
    • 方案2:单独存储脱敏片段(手机号前3后4),用于检索,不存储完整明文

五、三种方案对比

方案密钥是否进SQLDBA能否看到明文MySQL版本开发成本推荐度
AES_ENCRYPT函数是,日志会记录能5.7+低⭐ 仅测试
InnoDB原生列加密否有权限就能看到8.0.16+中⭐⭐ 企业云库
应用层加密完全不进数据库无密钥看不到无限制中高⭐⭐⭐⭐⭐ 生产首选

六、生产落地最佳实践

  1. 优先应用层AES-GCM/SM4加密,敏感字段存VARBINARY;
  2. 数据库连接强制开启SSL传输加密;
  3. 密钥统一KMS管理,禁止硬编码、禁止提交到代码仓库;
  4. 增加哈希字段,用于等值查询;
  5. 数据库账号最小权限;
  6. 备份文件也要加密,备份导出的密文文件,如果密钥泄露依然无法解密;
  7. 不要使用MD5做敏感数据加密(MD5不可逆,只能做哈希摘要)。

七、常见踩坑点

  1. 加密二进制数据存入VARCHAR:会发生字符集转义损坏,解密失败;必须VARBINARY;
  2. 密钥长度不符合AES规范:AES-128=16字节,AES-256=32字节;
  3. 只加密数据库,备份mysqldump导出密文但密钥和备份放一起,等于没加密;
  4. 混淆TDE透明加密和字段加密:TDE是整表空间磁盘加密,DBA查询依然返回明文,不能保护敏感字段防内部人员查看。

总结

MySQL字段级加密有三条路线:内置AES函数适合学习,InnoDB原生列加密适合企业云MySQL;绝大多数业务系统首选应用层字段加密。
字段加密的核心不是“让MySQL帮你加密”,而是控制密钥的流转范围,不让密钥进入数据库链路。

到此这篇关于在MySQL中实现字段级加密功能的文章就介绍到这了,更多相关mysql字段级加密内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文