应使用 concat + left/right 进行确定性截取,避免 replace 引发的非定位替换风险;需清洗空格、短横线并校验长度,身份证号须按15/18位分支处理或用 repeat 动态掩码,所有字段外层必须包 ifnull 防 null 透出。

用 CONCAT + LEFT/RIGHT 做确定性截取,别碰 REPLACE
直接用 REPLACE 对手机号、身份证号做“中间替换”极不可靠——它按内容匹配,不是按位置。比如字段值是 '13812121212',REPLACE(mobile, SUBSTRING(mobile,4,4), '****') 会把所有连续的 '1212' 都替掉,结果变成 '138********',完全失真。
真正可控的做法是定位截取:
-
CONCAT(LEFT(mobile, 3), '****', RIGHT(mobile, 4))—— 严格取前3位+后4位,中间硬填4星 - 必须包
IFNULL(..., ''),否则任一参数为NULL,整列返回NULL,下游易崩 - 若字段含空格或短横线(如
'138-1234-5678'),先清洗:REPLACE(REPLACE(TRIM(mobile), '-', ''), ' ', '') - 长度非11位时,
RIGHT(..., 4)可能返回空字符串,建议加校验:IF(CHAR_LENGTH(cleaned) = 11, CONCAT(...), cleaned)
身份证号要按长度分支或用 REPEAT 动态掩码
15位和18位身份证结构不同:15位末3位是顺序码,18位末位可能是 'X',且校验位逻辑不同。写死 '********' 会错位、漏位、误掩。
安全写法有两种:
- 显式分支:
CASE WHEN LENGTH(id_card) = 18 THEN CONCAT(LEFT(id_card, 6), '********', RIGHT(id_card, 4)) WHEN LENGTH(id_card) = 15 THEN CONCAT(LEFT(id_card, 6), '******', RIGHT(id_card, 3)) ELSE 'INVALID' END - 更自适应:
CONCAT(LEFT(id_card, 6), REPEAT('*', CHAR_LENGTH(id_card) - 10), RIGHT(id_card, 4))—— 自动适配两种长度,但需前置清洗:TRIM(BOTH '\r\n ' FROM id_card),否则换行符会让CHAR_LENGTH失准 - 禁用
SUBSTRING(id_card, -4):MySQL 5.0+ 兼容性差,RIGHT(id_card, 4)更稳
视图是落地最小单元,但权限隔离必须同步锁死
只建脱敏视图不收基表权限,等于裸奔。用户只要对原表有 SELECT 权限,就能绕过视图直接查出明文。
关键操作必须一步到位:
CREATE VIEW v_users_masked AS SELECT id, name, CONCAT(LEFT(phone,3), '****', RIGHT(phone,4)) AS phone_masked FROM users;- 显式声明定义者与安全上下文:
CREATE VIEW ... DEFINER = 'admin'@'%' SQL SECURITY DEFINER - 判断调用者身份要用
USER()(返回连接用户),不是CURRENT_USER()(返回定义者) - 若原始字段是
BIGINT类型(如未加引号存的手机号),必须先转字符串:CAST(phone AS CHAR),再套LEFT
姓名和IP脱敏要防长度坍塌与多字节干扰
中文姓名常含2–4字,英文名可能带空格或连字符;IP地址格式不统一(IPv4/IPv6混存、带端口、含协议头)。硬写固定长度会出错。
稳妥做法:
- 姓名保留首尾各1字:
CONCAT(LEFT(name, 1), REPEAT('*', GREATEST(0, CHAR_LENGTH(name) - 2)), RIGHT(name, 1)),GREATEST(0, ...)防止2字名变负数 - IPv4脱敏优先用
SUBSTRING_INDEX(ip_address, '.', 3)提取前三段,再拼'.***',比正则更轻量、兼容性更好 - 所有脱敏字段外层一律包
IFNULL(..., ''),避免NULL透出导致前端空指针或报表聚合异常 - 注意 MySQL 的
CHAR_LENGTH按字符计数(非字节),对 UTF-8 中文安全;但若字段用了latin1排序规则,需确认是否影响截取逻辑
脱敏表达式看着简单,但每处 LEFT、RIGHT、REPEAT 的参数都依赖长度判断,而长度又受清洗、编码、空格、换行、NULL 等因素扰动——最容易被忽略的,其实是清洗和空值兜底这两步。











