数据库端敏感数据脱敏应区分遮蔽与加密:CONCAT+LEFT/RIGHT等函数仅用于展示层防护,不可逆;真加密存储须用AES_ENCRYPT等可逆函数并配套密钥管理。

数据库端能直接用于敏感数据脱敏的函数,不是真正意义上的“加密”,而是字符串遮蔽或混淆——它们不依赖密钥、不可逆、无解密配套,目标是展示层防护,而非存储安全。真要加密存储,必须用 AES_ENCRYPT、ENCRYPTBYPASSPHRASE、encryptbykey 这类带密钥和可逆能力的函数,且需配套密钥管理。
MySQL 用 CONCAT+LEFT/RIGHT 做手机号脱敏最常用
这是生产环境里最轻量、最可控的脱敏方式,适合查询结果直接返回给前端或报表的场景:
-
CONCAT(LEFT(mobile, 3), '****', RIGHT(mobile, 4))是标准写法,兼容 MySQL 5.7+,不依赖插件 - 别用
SUBSTRING替代LEFT/RIGHT,因为SUBSTRING(mobile, 1, 3)在字段为NULL时会返回NULL,而LEFT(NULL, 3)同样是NULL,行为一致,但可读性差 - 如果字段含空格或格式字符(如
'139-1234-5678'),先用REPLACE(mobile, '-', '')清洗再截取,否则位数错乱 - 性能无压力,但不能用于 WHERE 条件匹配——你无法用
WHERE mobile LIKE '139****5678'查原始值
SQL Server 用 STUFF 或 SUBSTRING+REPLICATE 更灵活
相比 MySQL 的拼接组合,SQL Server 的 STUFF 更适合固定位置替换,尤其当脱敏规则含动态长度(如身份证前6后4)时:
-
STUFF(id_card, 7, 10, REPLICATE('*', 10))表示从第7位开始删10个字符,插入10个星号 - 注意:
STUFF对NULL输入直接返回NULL,无需额外判空;但若字段是空字符串'',STUFF('', 7, 10, '*')会原样返回'',得加CASE WHEN LEN(id_card) > 0 THEN ... ELSE id_card END -
REPLACE不适合手机号/身份证脱敏,因为它全局替换所有匹配子串(比如把'13912345678'中所有'1'都替成'*'),容易误伤 - 视图中封装这类逻辑很安全,但记得在
CREATE VIEW后显式GRANT SELECT给业务账号,并DENY SELECT原表
PostgreSQL 优先用 REGEXP_REPLACE 处理多格式敏感字段
当同一张表里混着不同格式的身份证、护照号、统一社会信用代码时,正则比硬切片更可靠:
-
REGEXP_REPLACE(id_no, '^(\d{4}).*(\d{4})$', '\1****\2')能同时匹配 15 位和 18 位身份证,而OVERLAY必须预设长度 - 正则里用
^和$锚定首尾,避免把'ABC123456789012345'中间一段误识别为身份证 -
REGEXP_REPLACE性能略低于字符串函数,但对百万级以下表影响可忽略;超大表建议建表达式索引:CREATE INDEX idx_id_masked ON user USING btree (REGEXP_REPLACE(id_no, '^(\d{4}).*(\d{4})$', '\1****\2')) - 不推荐用
pgcrypto扩展的digest()做脱敏——它是哈希,不可逆,且相同输入永远输出相同哈希,存在被彩虹表反推风险
真正加密存储必须用 AES_ENCRYPT / ENCRYPTBYPASSPHRASE 等可逆函数
脱敏 ≠ 加密。如果你的需求是“即使 DBA 也无法看到明文”,那所有上面的字符串函数都无效,必须走加密路径:
- MySQL:
AES_ENCRYPT(data, key)返回VARBINARY,字段类型必须定义为VARBINARY(255)或更大;解密用AES_DECRYPT(ciphertext, key),失败返回NULL - SQL Server:
ENCRYPTBYPASSPHRASE('key', data)返回VARBINARY,但密钥明文硬编码极危险;更安全的是先CREATE SYMMETRIC KEY+CERTIFICATE,再用ENCRYPTBYKEY - 密钥绝不能写死在 SQL 里或配置文件中——哪怕只在 INSERT/SELECT 语句中出现一次,也会被
SHOW PROCESSLIST或审计日志捕获 - 加密字段无法索引加速查询,除非用确定性加密(如 AES-SIV)或额外建哈希列;搜索邮箱时,宁可加
email_hash CHAR(64) AS (SHA2(email, 256)) STORED,也不该加密后模糊查
最容易被忽略的一点:脱敏函数本身不防 SQL 注入,也不替代权限控制。一个能执行 SELECT * 的账号,照样能绕过视图脱敏直接查原表——所以 GRANT/DENY 配合角色隔离,比选哪个函数重要得多。


















