UNION注入能绕过简单过滤,因UNION是合法SQL操作,不依赖分号或危险关键字;列数必须匹配否则报错;类型不匹配易致失效,宜用字符串兜底;$queryRawUnsafe无转义机制,等同于开后门。

UNION注入为什么总能绕过简单过滤
因为攻击者根本不需要执行任意语句,只要让数据库把两个结果集“拼起来”返回就行——而UNION本身是合法SQL操作,很多基础过滤规则(比如只拦SELECT、INSERT或分号)对它完全无效。
常见误区是以为“只要不出现;或EXEC就安全”,但UNION SELECT 1,2,3这种纯查询组合,连语法检查都过得了,WAF和正则黑白名单经常漏放。
列数限制为什么是UNION注入的第一道门槛
UNION要求左右两边的SELECT语句字段数严格一致,否则直接报错:ERROR 1222 (21000): The used SELECT statements have a different number of columns。这个错误会暴露后端是否启用详细错误提示,也决定了后续能否继续推进。
- 用
ORDER BY N试探:输入id=1 ORDER BY 1--+→ORDER BY 2--+→直到报错,最大不报错的N就是列数 - 用
UNION SELECT验证:构造id=-1 UNION SELECT 1,2,3,...,N--+,看页面是否正常渲染数字,确认回显位置 - 若后端关闭错误提示,就得靠布尔盲注或时间盲注来推断列数,效率大幅下降
数据类型不匹配会导致什么后果
MySQL在UNION时会对字段做隐式类型转换,但前提是能转;如果左边是字符串、右边是BLOB或JSON,可能触发Illegal mix of collations或直接截断/报错,导致payload失效。
实战中更常遇到的是字段顺序与类型错位:
- 原始查询选的是
id, name, price, image_url, created_at,5个字段,其中price是DECIMAL,created_at是DATETIME - 你用
UNION SELECT id, email, password, role, 'x'--+,第4个字段role如果是ENUM或TINYINT,而原始第4位是image_url(VARCHAR),多数情况能过;但若换成UNION SELECT ..., NULL, NOW()--+,第5位类型冲突就容易崩 - 稳妥做法:全部用字符串兜底,如
CONCAT('a',email)、CAST(password AS CHAR),避免类型强校验场景
Prisma的$queryRawUnsafe为什么等于开后门
Prisma文档明确警告:$queryRawUnsafe不做任何SQL转义,传入什么就执行什么。这意味着哪怕你只拼接一个q参数进WHERE name LIKE '%${q}%',攻击者输' UNION SELECT ...--+,就会原样进数据库执行。
它和手写mysql2或pg的字符串拼接没有本质区别,只是换了个名字包装:
-
$queryRaw支持参数化,安全;$queryRawUnsafe不支持,危险 - 团队常误以为“用了Prisma就天然防注入”,结果在搜索、报表导出等动态查询场景里,悄悄用上
$queryRawUnsafe,还配了高权限DB账号 - 真正难察觉的点:错误日志里看不到
UNION痕迹,只有业务层返回异常数据或空列表,拖库发生时已经晚了

















