SQL注入是Web安全领域极为常见且危害较大的漏洞类型,攻击者通过在用户输入点插入恶意SQL语句,干扰原本正常的数据库查询逻辑,进而获取、篡改甚至删除数据库中的敏感数据。这类攻击的核心原因是后端未对用户输入做严格的校验和过滤,直接将输入内容拼接进SQL语句执行。

常见SQL注入手法分类
根据数据回显方式和利用逻辑的不同,SQL注入的常见手法可以分为以下几类:
- 联合查询注入:利用UNION关键字拼接多个查询结果,直接通过页面回显获取目标数据,是最容易利用的注入类型之一。
- 报错注入:借助数据库执行错误时返回的错误信息,将目标数据携带在报错内容中回显,适用于页面没有正常数据回显的场景。
- 布尔盲注:页面不返回具体数据或报错信息,仅根据查询返回结果的不同(比如页面正常/异常)判断注入条件是否成立,通过逐位猜测获取数据。
- 时间盲注:利用数据库延时函数,根据页面响应时间的差异判断注入条件是否成立,同样适用于无回显的场景。
联合查询注入原理与利用
联合查询注入的核心是利用SQL中的UNION操作符,UNION的作用是将多个SELECT语句的查询结果合并为一个结果集,前提是多个查询的列数相同,且对应列的数据类型兼容。
利用前提
使用联合查询注入需要满足两个条件:一是页面存在SQL注入点,且注入点拼接的SQL语句是查询类语句;二是页面会将查询结果的部分内容回显到前端,方便攻击者获取拼接后的查询数据。
基本利用步骤
首先需要通过ORDER BY语句判断当前查询结果的列数,假设注入点拼接的SQL语句为SELECT * FROM users WHERE id=,攻击者输入1 ORDER BY 3,如果页面返回正常,说明当前查询列数大于等于3,继续尝试直到出现报错,即可得到准确的列数。
之后使用UNION拼接查询语句,比如已知列数为3,攻击者输入1 UNION SELECT 1,2,3,观察页面中哪个位置回显了UNION后的查询结果,再将该位置替换为想要查询的内容,比如查询当前数据库名:
-- 假设第2列的位置会回显,替换为查询数据库名的函数 1 UNION SELECT 1,database(),3
上述语句执行后,页面会在原本第2列回显的位置展示当前数据库的名称,攻击者可以进一步查询表名、字段名和具体数据:
-- 查询当前数据库下的所有表名 1 UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema=database() -- 查询指定表的字段名,假设表名为users 1 UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_name='users' -- 查询用户表的账号密码数据 1 UNION SELECT 1,group_concat(username,':',password),3 FROM users
报错注入原理与利用
报错注入的核心思路是构造能让数据库抛出错误的SQL语句,同时将想要获取的数据拼接到错误信息中,借助数据库的报错回显机制拿到目标数据。这类注入适用于页面没有正常数据回显,但会展示数据库错误信息的场景。
常见报错函数原理
不同数据库有不同的报错函数可以利用,以MySQL为例,常用的报错函数包括updatexml、extractvalue、floor等。以updatexml函数为例,其语法为updatexml(xml_target, xpath_expr, new_xml),其中xpath_expr参数需要符合XPath语法规范,如果传入不符合规范的字符串,数据库会抛出错误,且错误信息中会包含传入的xpath_expr内容。
利用示例
攻击者可以构造如下语句,将查询到的数据库名拼接到xpath_expr参数中,触发报错并回显数据:
-- 利用updatexml函数触发报错,获取当前数据库名 1 AND updatexml(1,concat(0x7e,database(),0x7e),1)
执行上述语句后,MySQL会返回类似XPATH syntax error: '~test_db~'的错误信息,其中test_db就是当前数据库的名称。同理可以查询表名和字段名:
-- 获取当前数据库的表名 1 AND updatexml(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()),0x7e),1) -- 获取users表的字段名 1 AND updatexml(1,concat(0x7e,(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_name='users'),0x7e),1) -- 获取用户数据 1 AND updatexml(1,concat(0x7e,(SELECT group_concat(username,':',password) FROM users),0x7e),1)
如果查询的数据过长导致报错信息无法完整展示,可以使用limit语句逐条查询数据。
SQL注入防御建议
针对联合查询和报错注入等常见SQL注入手法,开发者可以从以下几个层面做好防御:
- 优先使用参数化查询(预编译语句),比如Java中的PreparedStatement、PHP中的PDO预编译,让SQL语句的结构和参数分离,避免用户输入被当作SQL代码执行。
- 对用户输入做严格的过滤和校验,限制输入的长度、类型,过滤掉单引号、分号、UNION、SELECT等危险字符和关键字,但要注意避免过滤规则被绕过。
- 遵循最小权限原则,数据库账号不要授予过高的权限,比如仅给查询接口分配查询权限,避免注入攻击造成数据篡改、删除的危害。
- 关闭数据库的错误信息回显,避免将详细的数据库报错展示到前端,防止攻击者利用报错注入获取数据。