跨库查询本身不导致SQL注入,风险源于字符串拼接用户输入;权限限制无法防御注入,必须结合参数化与库/表名白名单校验,并按租户隔离数据库连接。

跨库查询本身不引入注入,拼接才危险
跨库查询(比如 SELECT * FROM db1.users JOIN db2.orders)只是语法合法,并不会自动带来 SQL 注入。真正风险来自:你用字符串拼接方式把用户输入(如库名、表名)硬塞进这个语句里。一旦攻击者控制了拼接内容,就能把 db1 替换成 db1; DROP DATABASE db2; -- 这类恶意串。
为什么不能靠账号权限“堵住”注入漏洞
限制账号只能访问 db1 和 db2,确实能阻止它去删 db3,但对注入本身没用——只要语句在允许库范围内执行成功,攻击就达成了。比如:
- 攻击者输入库名
db1; SELECT * FROM db2.sensitive_data WHERE '1'='1,如果后端不做参数化,而用"SELECT * FROM " + user_input + ".users"拼接,数据库会当作多语句执行(取决于驱动和配置) - 即使数据库禁用多语句,攻击者仍可用
db1` UNION SELECT password FROM db2.users --(利用反引号绕过)达成数据窃取 - 权限再细,也拦不住
SELECT语句从已授权库中拖走全部数据
必须用参数化 + 白名单双保险
跨库场景下,#{}(MyBatis) 或 PreparedStatement 无法绑定库名/表名,所以必须配合白名单校验:
- 所有可选的库名必须预定义在代码或配置中,例如
["db1", "db2", "reporting_db"],运行时只做in判断,绝不接受任意字符串 - 若需动态库名,应由业务逻辑映射而来(如用户类型 → 库名),而非前端直传
- MySQL 中用反引号包裹库名是必须的:
SELECT * FROM `db1`.users,但仅此不够——反引号不防注入,只防关键字冲突 - DBeaver 等工具里用
${db_name}时,务必确认变量面板已启用「安全模式」或后端做了白名单过滤,否则${}就是裸奔
最容易被忽略的一点:连接池与用户上下文隔离
很多团队以为配个只读账号就安全了,但实际生产中常共用一个连接池,不同租户请求混用同一连接。一旦某个请求因 bug 注入成功,后续语句可能复用该连接上下文,导致越权跨库访问。真正有效的做法是:
- 按租户或业务域分配独立数据库账号,账号权限精确到库+表+操作类型
- 连接池按账号维度隔离(如 HikariCP 的
DataSource实例分组),避免连接复用污染 - 禁止在应用层拼接库名后切换
USE db_name,这种显式切换极易被注入劫持

















