SQL 数据安全在分布式数据库应用

1次阅读

分库分表场景下sql注入更难拦截,因拆分后语句片段合法、waf/防火墙难识别;须全程参数化查询、禁用字符串拼接、严格管控${};敏感数据跨库泄露风险加剧,需脱敏存储;租户隔离易失效,须最小权限+上下文校验;审计日志碎片化,需统一trace_id注释与全局时间/id。

SQL 数据安全在分布式数据库应用

SQL 注入在分库分表场景下更难被拦截

分布式数据库本身不自带 SQL 注入防护,中间件(如 ShardingSphere、MyCat)或应用层的预编译逻辑一旦被绕过,恶意语句会直接落到某个物理库上执行——这时候 WAF 或数据库防火墙可能根本看不到完整 SQL,因为拆分后的语句片段看起来完全合法。

实操建议:

  • 所有 WHERE 条件必须用参数化查询,禁止拼接 String.format()+ 拼字符串
  • 如果用了 mybatis,确保 #{} 占位符全覆盖;${} 只允许出现在明确可控的静态枚举字段名场景(如排序字段),且需白名单校验
  • ShardingSphere 的 sql-show 配置打开后会打印重写前原始 SQL,可用于审计,但生产环境必须关掉——它不记录参数值,日志里全是问号,无法判断是否被注入

分布式事务中敏感数据跨库泄露风险被放大

当一笔订单涉及用户库 + 交易库 + 积分库,而事务日志(如 Seata 的 undo_log)或补偿任务把明文手机号、身份证号写进跨库消息体时,任意一个下游库被攻破,就等于全链路敏感字段裸奔。

实操建议:

  • 禁止在 undo_log 表、MQ 消息体、调度任务参数中存任何 PII(个人身份信息)明文,改用脱敏 ID 或哈希索引(如 HMAC-SHA256(uid, secret)
  • Seata AT 模式默认不加密日志,若业务强依赖事务一致性,需自行扩展 UndoLogManager 实现字段级加密写入
  • 分库键(sharding key)尽量避开敏感字段:别用 id_cardphone 做分片依据,否则攻击者可通过高频查询试探出分片映射关系,再定向爆破某库

权限隔离在多租户分库架构中极易失效

很多团队以为“每个租户一个库”就天然隔离了数据,结果 dba 给应用账号分配的是 tenant_1.*tenant_2.* 全库权限,而应用代码里又没做租户上下文校验——只要篡改请求头里的 X-Tenant-ID,就能越权查别人库。

实操建议:

  • 数据库账号按最小权限原则,只授予单库 select/INSERT/UPDATE,禁用 DROPSHOW DATABASES、跨库 JOIN 权限
  • 在 DAO 层强制绑定租户上下文:每次执行 SELECT 前检查 ThreadLocal.getTenantId() 是否与当前 SQL 所属库匹配,不匹配直接抛 AccessDeniedException
  • ShardingSphere 的 HintManager 支持强制路由,但若用 setDatabaseShardingValue() 动态切库,必须配合租户鉴权,否则等于开放库级跳转入口

审计日志在分库环境下难以关联还原完整行为

用户 A 修改了订单状态,这个操作可能触发主库写订单表、从库更新统计表、异步服务写日志库——三处日志时间戳不同、traceID 不一致、没有全局事务 ID 关联,安全团队查入侵路径时只能看到碎片化动作。

实操建议:

  • 所有数据库访问统一走封装好的 DBTemplate,在执行前注入 trace_id 到注释(如 /* trace_id=abc123 */ UPDATE order SET status=2),mysqlgeneral_log 或代理层能捕获该注释
  • 避免用 SELECT NOW() 记录操作时间,改用应用层生成统一 request_time 并透传到各库,否则跨机房时钟漂移会导致日志顺序错乱
  • 不要依赖 MySQL 自增 ID 做行为排序,分库后 ID 不连续也不全局有序,得靠业务侧生成单调递增的 sequence_id 或使用 TinyID 等分布式发号器

真正麻烦的不是技术方案多难实现,而是每个分库组件(JDBC 驱动、分片中间件、ORM、监控埋点)都在自己那一层悄悄绕过安全约定——得挨个对齐,不能只信文档里写的“默认安全”。

text=ZqhQzanResources