MySQL高级讲座篇之:MySQL 8.0的`Data Masking`功能:如何在数据库层面保护敏感数据?

大家好,我是老码,今天咱们来聊聊MySQL 8.0里一个很实用的功能:Data Masking(数据脱敏)。这玩意儿就像给数据库里的敏感信息戴上了一层面具,让那些不应该看到真实数据的人,只能看到一些“假数据”,以此来保护咱们的隐私,防止数据泄露。

想象一下,你的数据库里存着客户的身份证号、手机号、银行卡号等等,这些都是宝贝疙瘩,一旦泄露出去,那可就麻烦大了。Data Masking 就像一个隐身衣,让这些敏感数据在某些情况下“变身”,变成一些无意义的、但是格式又正确的数据。

为什么要用 Data Masking?

简单来说,就是为了安全!但是更细致地说,它能帮助我们:

  • 保护隐私: 这是最直接的目的,避免敏感数据被未经授权的人员访问。
  • 满足合规性要求: 很多行业都有数据保护的法规,比如 GDPR、HIPAA 等,Data Masking 可以帮助我们满足这些法规的要求。
  • 降低开发和测试风险: 在开发和测试环境中,我们通常不需要真实的敏感数据,用脱敏后的数据可以降低数据泄露的风险。
  • 方便数据分析: 有些分析场景只需要统计信息,不需要看到具体的敏感数据,Data Masking 可以让数据分析人员更安全地工作。

Data Masking 的原理

Data Masking 的原理其实很简单,就是通过一些预定义的规则,将数据库中的敏感数据替换成一些“假数据”。这些规则可以是:

  • 替换: 将整个字段替换成一个固定值,比如将所有手机号都替换成 13800000000
  • 部分替换: 只替换字段的一部分,比如只显示手机号的前三位和后四位,中间用 * 遮盖。
  • 随机替换: 用随机生成的数据替换原始数据,比如随机生成一个身份证号。
  • 加密: 使用加密算法对数据进行加密,只有拥有密钥的人才能解密。
  • 自定义函数: 使用自定义的函数来生成脱敏后的数据,可以实现更复杂的脱敏逻辑。

MySQL 8.0 Data Masking 的使用

MySQL 8.0 提供了 Data Masking 功能,让我们可以在数据库层面轻松实现数据脱敏。它主要通过以下几个步骤来实现:

  1. 定义 Masking 规则: 使用 CREATE MASK 语句来定义 Masking 规则,指定要脱敏的字段和脱敏方式。
  2. 应用 Masking 规则: 将 Masking 规则应用到指定的表或视图上。
  3. 控制访问权限: 通过 MySQL 的权限系统,控制哪些用户可以看到原始数据,哪些用户只能看到脱敏后的数据。

接下来,咱们就通过一些具体的例子,来演示一下 Data Masking 的使用方法。

例子 1:遮盖手机号

假设我们有一个 users 表,其中有一个 phone_number 字段存储用户的手机号。我们希望只显示手机号的前三位和后四位,中间用 * 遮盖。

首先,我们需要创建一个 Masking 规则:

CREATE MASK MASK_PHONE_NUMBER ON users.phone_number USING (
    CASE
        WHEN CURRENT_USER() = 'admin'@'%' THEN phone_number  -- admin用户可以看到原始数据
        ELSE CONCAT(SUBSTR(phone_number, 1, 3), '****', SUBSTR(phone_number, LENGTH(phone_number) - 3, 4))
    END
);

这个 SQL 语句的意思是:

  • CREATE MASK MASK_PHONE_NUMBER ON users.phone_number:创建一个名为 MASK_PHONE_NUMBER 的 Masking 规则,应用到 users 表的 phone_number 字段上。
  • USING ( ... ):指定脱敏的逻辑。
  • CASE WHEN CURRENT_USER() = 'admin'@'%' THEN phone_number:如果当前用户是 admin,则显示原始手机号。
  • ELSE CONCAT(SUBSTR(phone_number, 1, 3), '****', SUBSTR(phone_number, LENGTH(phone_number) - 3, 4)):否则,只显示手机号的前三位和后四位,中间用 **** 遮盖。

接下来,我们创建一个用户 developer,并授予其对 users 表的 SELECT 权限:

CREATE USER 'developer'@'%' IDENTIFIED BY 'password';
GRANT SELECT ON `your_database`.`users` TO 'developer'@'%';
FLUSH PRIVILEGES;

然后,我们分别用 admindeveloper 用户登录 MySQL,查询 users 表的数据,看看效果。

admin 用户:

SELECT * FROM users;

可以看到,admin 用户可以看到原始的手机号。

developer 用户:

SELECT * FROM users;

可以看到,developer 用户只能看到脱敏后的手机号,中间用 **** 遮盖。

例子 2:随机替换身份证号

假设我们还有一个 users 表,其中有一个 id_card 字段存储用户的身份证号。我们希望用随机生成的身份证号来替换原始数据。

首先,我们需要创建一个自定义函数,用于生成随机身份证号:

DELIMITER $$
CREATE FUNCTION generate_random_id_card()
RETURNS VARCHAR(18)
DETERMINISTIC
BEGIN
  DECLARE area_code VARCHAR(6) DEFAULT LPAD(FLOOR(RAND() * 999999), 6, '0');
  DECLARE year VARCHAR(4) DEFAULT CAST(FLOOR(RAND() * 100 + 1920) AS CHAR);
  DECLARE month VARCHAR(2) DEFAULT LPAD(FLOOR(RAND() * 12 + 1), 2, '0');
  DECLARE day VARCHAR(2) DEFAULT LPAD(FLOOR(RAND() * 28 + 1), 2, '0');
  DECLARE seq VARCHAR(3) DEFAULT LPAD(FLOOR(RAND() * 999), 3, '0');
  DECLARE checksum INT;
  DECLARE id_card VARCHAR(17);

  SET id_card = CONCAT(area_code, year, month, day, seq);

  -- 计算校验位 (简化版本)
  SET checksum = MOD(
    (CAST(SUBSTRING(id_card, 1, 1) AS UNSIGNED) * 7 +
     CAST(SUBSTRING(id_card, 2, 1) AS UNSIGNED) * 9 +
     CAST(SUBSTRING(id_card, 3, 1) AS UNSIGNED) * 10 +
     CAST(SUBSTRING(id_card, 4, 1) AS UNSIGNED) * 5 +
     CAST(SUBSTRING(id_card, 5, 1) AS UNSIGNED) * 8 +
     CAST(SUBSTRING(id_card, 6, 1) AS UNSIGNED) * 4 +
     CAST(SUBSTRING(id_card, 7, 1) AS UNSIGNED) * 2 +
     CAST(SUBSTRING(id_card, 8, 1) AS UNSIGNED) * 1 +
     CAST(SUBSTRING(id_card, 9, 1) AS UNSIGNED) * 6 +
     CAST(SUBSTRING(id_card, 10, 1) AS UNSIGNED) * 3 +
     CAST(SUBSTRING(id_card, 11, 1) AS UNSIGNED) * 7 +
     CAST(SUBSTRING(id_card, 12, 1) AS UNSIGNED) * 9 +
     CAST(SUBSTRING(id_card, 13, 1) AS UNSIGNED) * 10 +
     CAST(SUBSTRING(id_card, 14, 1) AS UNSIGNED) * 5 +
     CAST(SUBSTRING(id_card, 15, 1) AS UNSIGNED) * 8 +
     CAST(SUBSTRING(id_card, 16, 1) AS UNSIGNED) * 4 +
     CAST(SUBSTRING(id_card, 17, 1) AS UNSIGNED) * 2),
    11
  );

  CASE checksum
    WHEN 0 THEN RETURN CONCAT(id_card, '1');
    WHEN 1 THEN RETURN CONCAT(id_card, '0');
    WHEN 2 THEN RETURN CONCAT(id_card, 'X');
    WHEN 3 THEN RETURN CONCAT(id_card, '9');
    WHEN 4 THEN RETURN CONCAT(id_card, '8');
    WHEN 5 THEN RETURN CONCAT(id_card, '7');
    WHEN 6 THEN RETURN CONCAT(id_card, '6');
    WHEN 7 THEN RETURN CONCAT(id_card, '5');
    WHEN 8 THEN RETURN CONCAT(id_card, '4');
    WHEN 9 THEN RETURN CONCAT(id_card, '3');
    WHEN 10 THEN RETURN CONCAT(id_card, '2');
  END CASE;

END$$
DELIMITER ;

这个函数的功能是:

  • generate_random_id_card():函数名。
  • RETURNS VARCHAR(18):返回一个长度为 18 的字符串,也就是身份证号的长度。
  • DETERMINISTIC:表示这个函数是确定性的,也就是说,对于相同的输入,总是返回相同的输出。
  • BEGIN ... END:函数体,生成随机身份证号的逻辑。

注意: 这个函数只是一个简化版本,生成的身份证号并不一定完全符合国家标准,仅仅用于演示 Data Masking 的功能。在实际应用中,你需要使用更严谨的算法来生成随机身份证号。

然后,我们创建一个 Masking 规则:

CREATE MASK MASK_ID_CARD ON users.id_card USING (
    CASE
        WHEN CURRENT_USER() = 'admin'@'%' THEN id_card  -- admin用户可以看到原始数据
        ELSE generate_random_id_card()
    END
);

这个 SQL 语句的意思是:

  • CREATE MASK MASK_ID_CARD ON users.id_card:创建一个名为 MASK_ID_CARD 的 Masking 规则,应用到 users 表的 id_card 字段上。
  • USING ( ... ):指定脱敏的逻辑。
  • CASE WHEN CURRENT_USER() = 'admin'@'%' THEN id_card:如果当前用户是 admin,则显示原始身份证号。
  • ELSE generate_random_id_card():否则,使用 generate_random_id_card() 函数生成随机身份证号。

同样,我们用 admindeveloper 用户登录 MySQL,查询 users 表的数据,看看效果。

admin 用户:

SELECT * FROM users;

可以看到,admin 用户可以看到原始的身份证号。

developer 用户:

SELECT * FROM users;

可以看到,developer 用户只能看到随机生成的身份证号。

Data Masking 的一些注意事项

  • 性能影响: Data Masking 会对数据库的性能产生一定的影响,特别是在查询大量数据时。因此,我们需要 carefully 评估 Masking 规则的复杂度,以及数据量的大小,选择合适的脱敏方式。
  • 存储空间: 如果使用加密的方式进行脱敏,会增加存储空间的占用。
  • 权限控制: 要确保只有授权的用户才能看到原始数据,避免数据泄露。
  • 测试: 在应用 Data Masking 规则之前,一定要在测试环境中进行充分的测试,确保脱敏后的数据满足业务需求,并且不会影响现有的应用程序。
  • 数据一致性: 随机生成数据时要特别注意数据一致性问题,例如某些数据之间存在关联关系,随机生成时需要保证这些关系依然成立,否则会导致业务逻辑错误。
  • 函数的确定性: 自定义函数必须是确定性的(DETERMINISTIC),否则 Data Masking 可能会出现不一致的结果。 也就是说,对于相同的输入,函数必须总是返回相同的输出。

Data Masking 的一些限制

  • 只能在 SELECT 语句中使用: Data Masking 只能在 SELECT 语句中使用,不能在 UPDATE、DELETE 等语句中使用。也就是说,Data Masking 只能控制数据的显示,不能修改数据库中的原始数据。
  • 不能用于索引: Data Masking 不能用于索引,也就是说,不能在 WHERE 子句中使用 Masking 规则来过滤数据。
  • 不支持全文索引: Data Masking 不支持全文索引。
  • 不支持 Generated Column: Data Masking 不支持 Generated Column。

Data Masking 的一些替代方案

如果 MySQL 8.0 的 Data Masking 功能不能满足你的需求,你还可以考虑以下一些替代方案:

  • 应用程序层面脱敏: 在应用程序层面进行数据脱敏,可以实现更灵活的脱敏逻辑。但是,这种方式需要修改应用程序的代码,并且可能会增加应用程序的复杂度。
  • ETL 工具: 使用 ETL 工具进行数据脱敏,可以在数据抽取、转换、加载的过程中,对敏感数据进行脱敏。这种方式适用于数据仓库等场景。
  • 第三方数据脱敏工具: 使用第三方数据脱敏工具,可以提供更丰富的脱敏功能,比如数据漂白、数据替换、数据屏蔽等。

Data Masking 的最佳实践

  • 识别敏感数据: 首先,我们需要识别数据库中的敏感数据,比如身份证号、手机号、银行卡号等等。
  • 定义脱敏策略: 根据业务需求和安全要求,定义合适的脱敏策略。比如,对于身份证号,我们可以选择随机替换;对于手机号,我们可以选择部分替换。
  • 选择合适的脱敏方式: 根据脱敏策略和性能要求,选择合适的脱敏方式。比如,如果对性能要求比较高,我们可以选择简单的替换方式;如果对安全性要求比较高,我们可以选择加密方式。
  • 控制访问权限: 通过 MySQL 的权限系统,控制哪些用户可以看到原始数据,哪些用户只能看到脱敏后的数据。
  • 定期审查: 定期审查 Data Masking 规则,确保其仍然满足业务需求和安全要求。
  • 监控: 监控 Data Masking 的执行情况,及时发现和解决问题。

总结

Data Masking 是 MySQL 8.0 中一个非常有用的功能,可以帮助我们保护数据库中的敏感数据,满足合规性要求,降低开发和测试风险。虽然 Data Masking 有一些限制,但是通过合理的配置和使用,它可以有效地提高数据的安全性。

希望今天的讲座能够帮助大家更好地理解和使用 Data Masking 功能。记住,数据安全无小事,保护好咱们的数据,才能睡个好觉!

好了,今天的分享就到这里,如果大家有什么问题,欢迎提问。下次有机会再跟大家聊聊其他好玩的技术。拜拜!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注