AAA 模型在数据库中的应用
在信息安全领域,AAA 模型(认证、授权、审计)是基本支柱。您可以在 RADIUS、TACACS+、防火墙、VPN 中看到它……但它很少被严格应用于关系型数据库。
然而 MariaDB / MySQL 提供了原生机制,可以在敏感数据和应用程序用户之间实现真正的物理隔离。无需额外的中间件,无需昂贵的代理。一切都已经在引擎中了。
核心思想很简单:应用程序用户永远不应该直接访问包含敏感数据的表。他们只应该与精心设计的视图和存储过程交互,这些视图和存储过程仅暴露最少量的必要数据。
为什么需要物理隔离?
经典模型是给应用程序用户授予 GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.*。这设置起来很快,但在安全方面是灾难性的:
- 用户可以访问所有表的所有列
- 应用程序中的 SQL 注入会暴露整个数据库
- 无法对敏感数据的访问进行细粒度追踪
- 无法屏蔽某些列(卡号、邮箱、哈希密码)
物理隔离通过在应用程序和数据之间插入一个 SQL 抽象层 来解决这些问题。
第一步:创建接口 Schema
CREATE DATABASE app_interface;
此 schema 不包含任何表。只有视图和存储过程。它是应用程序可见的"攻击面"。
第二步:使用 ALGORITHM=TEMPTABLE 创建视图
物理隔离的关键在于视图算法的选择:
CREATE
ALGORITHM = TEMPTABLE
DEFINER = 'admin_views'@'localhost'
SQL SECURITY DEFINER
VIEW app_interface.v_customers AS
SELECT
customer_id,
first_name,
last_name,
city,
country
FROM production.customers;
这里有三个关键要素:
- ALGORITHM=TEMPTABLE:MariaDB 将视图物化为临时表。用户无法通过
SHOW CREATE VIEW"回溯"到底层表来构建直接查询。 - DEFINER:视图以
admin_views账户的权限执行,而不是应用程序用户的权限。 - SQL SECURITY DEFINER:权限检查基于 DEFINER 而非 INVOKER 进行。应用程序用户只需要视图上的权限,而不需要源表上的权限。
注意视图中缺少的内容:没有邮箱、没有电话号码、没有完整地址。数据脱敏是视图设计的固有特性。
第三步:使用存储过程进行写操作
对于写操作,存储过程提供了更精细的控制:
DELIMITER //
CREATE PROCEDURE app_interface.sp_update_customer_city(
IN p_customer_id INT,
IN p_city VARCHAR(100)
)
SQL SECURITY DEFINER
BEGIN
-- 业务验证
IF p_city IS NULL OR LENGTH(TRIM(p_city)) = 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'City cannot be empty';
END IF;
UPDATE production.customers
SET city = p_city,
updated_at = NOW()
WHERE customer_id = p_customer_id;
-- 审计记录
INSERT INTO production.audit_log(
table_name, record_id, field_name,
action, performed_by, performed_at
)
VALUES (
'customers', p_customer_id, 'city',
'UPDATE', CURRENT_USER(), NOW()
);
END //
DELIMITER ;
应用程序用户只能修改城市。不能修改姓名、邮箱或账户状态。而且每次修改都会被自动审计。
第四步:锁定的管理员账户
视图和存储过程的 DEFINER 账户不应该被用于连接:
CREATE USER 'admin_views'@'localhost'
IDENTIFIED BY 'impossible_to_guess_random_string';
GRANT SELECT, INSERT, UPDATE ON production.* TO 'admin_views'@'localhost';
ALTER USER 'admin_views'@'localhost' ACCOUNT LOCK;
锁定的账户(ACCOUNT LOCK)无法连接,但其权限在 SQL SECURITY DEFINER 模式下的视图和存储过程中仍然有效。这是该架构的关键点:拥有权限的账户无法连接,而连接的账户没有直接权限。
第五步:最小权限的应用程序用户
CREATE USER 'app_user'@'10.0.%'
IDENTIFIED BY 'strong_password_here';
GRANT SELECT ON app_interface.v_customers TO 'app_user'@'10.0.%';
GRANT EXECUTE ON PROCEDURE app_interface.sp_update_customer_city
TO 'app_user'@'10.0.%';
-- 不授予 production.* 上的任何权限
应用程序用户无法访问 production schema 中的任何内容。即使 SQL 注入成功,攻击者也只能看到视图暴露的数据,只能执行授权的存储过程。
高级数据脱敏
视图还支持复杂的数据脱敏技术:
CREATE VIEW app_interface.v_customer_contacts AS
SELECT
customer_id,
CONCAT(LEFT(email, 3), '***@***.',
SUBSTRING_INDEX(email, '.', -1)) AS masked_email,
CONCAT('***-***-', RIGHT(phone, 4)) AS masked_phone
FROM production.customers;
客服人员可以通过电话号码的后 4 位来识别客户,而无需看到完整号码。
查询频率限制
一种经常被忽视的技术:使用存储过程在数据库层面实现频率限制:
CREATE PROCEDURE app_interface.sp_search_customers(
IN p_search_term VARCHAR(100)
)
SQL SECURITY DEFINER
BEGIN
DECLARE v_count INT;
SELECT COUNT(*) INTO v_count
FROM production.rate_limit
WHERE user = CURRENT_USER()
AND action = 'search'
AND created_at > NOW() - INTERVAL 1 MINUTE;
IF v_count > 10 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Rate limit exceeded: max 10 searches/minute';
END IF;
INSERT INTO production.rate_limit(user, action, created_at)
VALUES (CURRENT_USER(), 'search', NOW());
SELECT customer_id, first_name, last_name, city
FROM production.customers
WHERE last_name LIKE CONCAT(p_search_term, '%')
LIMIT 50;
END;
架构总结
| 层级 | 组件 | 职责 |
|---|---|---|
| 应用层 | app_user |
连接、执行视图/存储过程 |
| 接口层 | app_interface(schema) |
仅暴露必要数据 |
| 安全层 | admin_views(已锁定) |
拥有权限,无法连接 |
| 生产层 | production(schema) |
实际数据表,不可直接访问 |
局限性
这种方法并非完美:
- 性能:
ALGORITHM=TEMPTABLE会创建临时副本。对于大表,开销可能较大。 - 复杂性:每个新的应用功能可能都需要新的视图或存储过程。
- 维护:视图必须随底层表的 schema 变化而演进。
但这些约束是安全的代价。在数据泄露平均每次事件造成 450 万美元损失的背景下,这是一项合理的投资。
结论
通过 TEMPTABLE 视图和 DEFINER 存储过程实现的物理隔离不是 MariaDB / MySQL 的晦涩功能。它是一种强大的原生安全架构,但往往未被充分利用。
五个步骤就足够了:一个接口 schema、使用正确算法的视图、用于写操作的存储过程、一个锁定的 DEFINER 账户和一个最小权限的应用程序用户。结果是一个即使 SQL 注入成功也只能访问受控数据子集的数据库。
本文最初发表于 Medium。
评论 (0)
暂无评论。
发表评论