第 3 章:数据库内置加密函数——三剑客的实战用法

说到列级加密,最直接的办法就是利用数据库自己带的加密函数。我做过不少项目,早期团队总爱自己写加解密逻辑,结果维护起来头疼得很。后来我慢慢发现,用内置函数能省掉一大半的坑。

今天咱们就聊三个主流数据库:MySQL、PostgreSQL、SQL Server。它们各自有对应的函数,用法和坑点都不太一样。

3.1 MySQL:AES_ENCRYPT / AES_DECRYPT

MySQL 自带的 AES 加密函数,用起来极其简单。说白了就是两个函数:AES_ENCRYPT('明文', '密钥') 和 AES_DECRYPT('密文', '密钥')。

个人习惯:我一般在插入数据时直接加密,查询时实时解密。但注意,密钥别硬编码在 SQL 里,最好存到配置表或环境变量中。

先看一个例子:

-- 建表
CREATE TABLE user_credit (
    id INT PRIMARY KEY,
    card_no VARBINARY(255),
    ssn VARBINARY(255)
);

-- 插入加密数据
INSERT INTO user_credit (id, card_no, ssn)
VALUES (1, AES_ENCRYPT('6222021234567890', 'my_secret_key'),
            AES_ENCRYPT('123-45-6789', 'my_secret_key'));

-- 查询并解密
SELECT id,
       AES_DECRYPT(card_no, 'my_secret_key') AS card_no_dec,
       AES_DECRYPT(ssn, 'my_secret_key') AS ssn_dec
FROM user_credit;

有几个点你一定要注意:

  • 加密后的数据用 VARBINARY 存,别用 VARCHAR。我见过有人用 VARCHAR,结果乱码笑死人。
  • AES 默认是 AES-128-ECB,不安全。建议用 AES_ENCRYPT('明文', '密钥', '密钥长度', '加密模式') 指定 GCM 或 CBC。但注意 MySQL 8.0 才支持模式参数。
  • 密钥长度:16、24、32 字节分别对应 128、192、256 位。

我踩过的坑:曾经有一个项目,同事把密钥直接写在 SQL 脚本里,然后脚本被误传到 GitHub……嗯,从那以后我强制要求密钥从外部配置读取,绝不硬编码。

3.2 PostgreSQL:pgcrypto 扩展

PostgreSQL 默认不带加密函数,你需要安装 pgcrypto 扩展。我个人特别喜欢它的灵活性——它支持多种算法,而且有 gen_salt() 这样的辅助函数。

安装很简单:

CREATE EXTENSION IF NOT EXISTS pgcrypto;

常用的加密函数就三个:encrypt()、decrypt()、gen_salt()。我们看一个实战例子:

-- 建表
CREATE TABLE user_pii (
    id SERIAL PRIMARY KEY,
    email TEXT,
    phone_enc BYTEA,
    address_enc BYTEA
);

-- 插入加密数据
INSERT INTO user_pii (id, email, phone_enc, address_enc)
VALUES (1, 'alice@example.com',
        encrypt('13800138000', 'mykey1234567890ab', 'aes'),
        encrypt('北京市朝阳区', 'mykey1234567890ab', 'aes'));

-- 查询解密
SELECT id, email,
       decrypt(phone_enc, 'mykey1234567890ab', 'aes')::text AS phone,
       decrypt(address_enc, 'mykey1234567890ab', 'aes')::text AS address
FROM user_pii;

注意几个点:

  • encrypt() 第三个参数可以是 'aes'、'bf'(Blowfish)等。默认使用 CBC 模式。
  • 解密结果需要 ::text 转换类型,否则返回的 BYTEA 你看不懂。
  • 密钥长度:AES 要求 16 字节,否则自动填充或报错。我记得有一次密钥长了 1 字节,报错半天没找到原因。

避坑指南:如果你用 pgcrypto 做列级加密,密钥管理建议用 pgcrypto 的 pgp_sym_encrypt() 函数,它支持密码保护。但那样性能会差一些,适合低频访问的敏感字段。

3.3 SQL Server:EncryptByKey / DecryptByKey

SQL Server 的加密体系稍微复杂一些。它需要你先创建证书、创建对称密钥,然后再用 EncryptByKey() 和 DecryptByKey()。其实理解了就不难。

基本步骤是这样的:

  1. 创建主密钥(CREATE MASTER KEY)
  2. 创建证书(CREATE CERTIFICATE)
  3. 创建对称密钥(CREATE SYMMETRIC KEY)
  4. 打开密钥 → 加解密 → 关闭密钥

看代码:

-- 1. 创建主密钥
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'MasterKeyPass123!';

-- 2. 创建证书
CREATE CERTIFICATE MyCert
WITH SUBJECT = 'Column Encryption Cert';

-- 3. 创建对称密钥
CREATE SYMMETRIC KEY MySymKey
WITH ALGORITHM = AES_256
ENCRYPTION BY CERTIFICATE MyCert;

-- 4. 建表
CREATE TABLE Employee (
    id INT PRIMARY KEY,
    name NVARCHAR(100),
    salary_enc VARBINARY(256),
    ssn_enc VARBINARY(256)
);

-- 5. 插入加密数据
OPEN SYMMETRIC KEY MySymKey
   DECRYPTION BY CERTIFICATE MyCert;
INSERT INTO Employee (id, name, salary_enc, ssn_enc)
VALUES (1, '张三',
        EncryptByKey(Key_GUID('MySymKey'), '85000'),
        EncryptByKey(Key_GUID('MySymKey'), '123-45-6789'));
CLOSE SYMMETRIC KEY MySymKey;

-- 6. 查询解密
OPEN SYMMETRIC KEY MySymKey
   DECRYPTION BY CERTIFICATE MyCert;
SELECT id, name,
       CONVERT(NVARCHAR, DecryptByKey(salary_enc)) AS salary,
       CONVERT(NVARCHAR, DecryptByKey(ssn_enc)) AS ssn
FROM Employee;
CLOSE SYMMETRIC KEY MySymKey;

你看,步骤多了点,但安全性更高。我的建议是:

  • 每个加密字段用不同的对称密钥?没必要,同一个密钥足够了,除非你需要做列级权限隔离。
  • 记得每次用完要 CLOSE SYMMETRIC KEY,否则会话里一直打开,有安全隐患。
  • EncryptByKey() 的返回值是 VARBINARY,最大 256 字节。如果明文太长会报错。

我推荐的做法:在存储过程里封装加解密逻辑,应用层只调用存储过程,不直接操作密钥。这样密钥管理起来方便,也更容易做审计。

3.4 三者的对比

我整理了一张表,方便你快速选型:

对比项 MySQL PostgreSQL SQL Server
扩展/函数 内置函数(需注意版本) pgcrypto 扩展 内置,需创建证书+密钥
算法支持 AES-128/192/256 (ECB/CBC/GCM) AES, Blowfish, PGP 等 AES (128/192/256), TRIPLE_DES
密钥管理 手动传密钥 手动传密钥或 pgp 密码 证书+对称密钥体系
性能 较高(纯函数调用) 中(扩展加载开销) 较低(需开闭密钥)
适用场景 简单应用,快速加密 灵活多算法,安全要求中等 企业级,合规要求高
数据库内置加密函数架构对比 MySQL AES_ENCRYPT AES_DECRYPT PostgreSQL pgcrypto 扩展 encrypt / decrypt SQL Server EncryptByKey DecryptByKey MySQL 特点 函数级调用,无需额外扩展 密钥由应用传入,方便但风险高 PostgreSQL 特点 安装扩展后支持多种算法 可配合 pgp_sym_encrypt() 管理密码 SQL Server 特点 证书+对称密钥体系,安全性最高 需显示打开/关闭密钥 选型建议 简单场景用 MySQL → 中等安全用 PostgreSQL → 企业合规用 SQL Server

3.5 我的选型建议

你可能会问:到底选哪个?我根据项目经验总结几点:

  • 原型或小项目:直接用 MySQL 的 AES_ENCRYPT,最快。但记得用 CBC 模式,且密钥别写死在代码里。
  • 需要跨平台或算法灵活:PostgreSQL + pgcrypto。我上一个项目就用这个,因为要同时兼容 AES 和 PGP 两种加密方案给不同合规部门。
  • 大型企业、有审计要求:SQL Server 的证书体系。虽然步骤多,但它的权限控制和密钥轮换做得最好。我曾在金融项目里用它加密身份证号,过了 SOC2 审计。

注意:不管用哪个数据库,千万别把加密密钥和明文数据放在同一张表或同一个备份文件里。我见过有人把密钥存到数据库的配置表里,然后整个库被人拖走……你想想看,加密还有啥意义?

好了,这一章我们讲了三个数据库内置加密函数的具体用法。你在实际项目中只要按这个思路选,基本不会跑偏。