Skip to content

数据脱敏测试覆盖案例

1. 背景与挑战

在 SQL 审计平台中,数据脱敏是核心安全功能之一。市面上不少方案的实现思路是「结果集列名匹配」:

  1. 执行 SQL 查询。
  2. 获取结果集的列名(Column Names)。
  3. 将列名与脱敏规则(如 user_account_id)进行正则匹配。
  4. 如果匹配,对该列数据进行脱敏处理。

这套思路识别的是「结果列名」,而不是「字段的血缘来源」,因此只要把敏感字段改头换面,就能轻松绕过:

绕过手法列名匹配方案NextMeta
函数包裹(MD5 / CONCAT / CAST 等)可绕过拦截
别名(AS xxx可绕过拦截
子查询 / 多层嵌套可绕过拦截
UNION 合并查询可绕过拦截
大小写变换可能失效拦截

NextMeta 采用字段血缘追踪,直接从 SQL 语义层面识别敏感字段的真实来源。下方测试素材覆盖的函数包裹、别名、子查询、CTE、UNION、通配符、注释、大小写、多行换行等全部绕过方式,均已实测拦截。

边界加固(v2.0.5):本轮实测修复了三处可通过字段血缘绕过的边界漏洞——

  1. GROUP_CONCAT 等多值拼接聚合:mask_middle 只遮中间 1/3,头尾明文泄露,已改为对聚合结果强制 mask_all
  2. UNION 合并查询:结果列由最左分支决定,已递归分析最左分支的血缘;
  3. 子查询改名 + SELECT *:外层 * 展开子查询列,已按子查询输出列顺序传播脱敏规则。

另:查询窗口按业务要求只允许 SELECT/EXPLAINWITH(CTE)语句在查询窗口被正常拦截,不视为绕过。

sql
-- 以下方式绕过脱敏全部可以安全防护
-- 数据库审核平台脱敏规则测试集
-- 目标表: orders
-- 敏感字段: user_account_id
-- 测试目的: 验证审核逻辑是否能识别经过变形、别名或函数包裹后的敏感字段引用


SELECT user_account_id FROM orders LIMIT 1;
SELECT `user_account_id` FROM `orders` LIMIT 1;
SELECT orders.user_account_id FROM orders LIMIT 1;
SELECT MD5(user_account_id) FROM orders LIMIT 1;
SELECT HEX(user_account_id) FROM orders LIMIT 1;
SELECT TO_BASE64(user_account_id) FROM orders LIMIT 1;

SELECT CONCAT(user_account_id, '_test') FROM orders LIMIT 1;
SELECT CONCAT_WS('-', user_account_id, 'prefix') FROM orders LIMIT 1;
SELECT SUBSTR(user_account_id, 1, 5) FROM orders LIMIT 1;
SELECT LEFT(user_account_id, 5) FROM orders LIMIT 1;
SELECT TRIM(user_account_id) FROM orders LIMIT 1;
SELECT REVERSE(user_account_id) FROM orders LIMIT 1;

SELECT CAST(user_account_id AS CHAR) FROM orders LIMIT 1;
SELECT CONVERT(user_account_id, CHAR) FROM orders LIMIT 1;

SELECT GROUP_CONCAT(user_account_id) FROM orders LIMIT 1;
SELECT MAX(user_account_id) FROM orders;

SELECT CASE WHEN 1=1 THEN user_account_id ELSE '0' END FROM orders LIMIT 1;
SELECT IF(TRUE, user_account_id, 'hidden') FROM orders LIMIT 1;
SELECT COALESCE(NULL, user_account_id) FROM orders LIMIT 1;

SELECT user_account_id + 0 FROM orders LIMIT 1;
SELECT user_account_id * 1 FROM orders LIMIT 1;

SELECT user_account_id AS harmless_id FROM orders LIMIT 1;
SELECT user_account_id "uid" FROM orders LIMIT 1;

SELECT t.safe_col FROM (SELECT user_account_id AS safe_col FROM orders) t LIMIT 1;

SELECT final_col FROM (SELECT temp_col AS final_col FROM (SELECT user_account_id AS temp_col FROM orders) a) b LIMIT 1;

WITH sensitive_data AS (
    SELECT user_account_id FROM orders
)
SELECT * FROM sensitive_data LIMIT 1;

SELECT user_account_id/**/ FROM orders LIMIT 1;
SELECT user_account_id /*!50000 */ FROM orders LIMIT 1;

SELECT 
    user_account_id 
FROM 
    orders 
LIMIT 1;

SELECT t.* FROM orders t LIMIT 1;

-- 边界加固用例(v2.0.5 修复后全部拦截)
SELECT user_account_id AS safe_col FROM orders
UNION
SELECT user_account_id AS safe_col FROM orders
LIMIT 1;

SELECT * FROM (SELECT user_account_id AS safe_col FROM orders LIMIT 1) t;