news 2026/9/7 9:43:43

sql二次注入攻击原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
sql二次注入攻击原理

免责声明本文全部实验操作均在本人完全可控的自建本地靶场环境完成,文章仅用于网络安全原理学习与技术研究。根据《中华人民共和国网络安全法》,未经授权对任何第三方系统进行探测、命令执行、文件写入等操作属于违法行为。

严禁复制、使用本文中的 Payload、代码应用到未获得授权的设备上。若读者将文中内容用于非法用途,一切法律责任由行为人本人承担,与本文作者无关。

个人认知有限,文中难免存在疏漏与冗余,如有不妥之处,欢迎大家交流指正,还望海涵。
————————————————

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

一、什么是二次注入

二次注入,又称二阶 SQL 注入(Second-Order SQL Injection),是一种特殊的 SQL 注入形式。它和传统的(一阶)SQL 注入最大的区别在于:

  • 一阶注入:攻击者提交的恶意数据,在当前请求中就被直接拼接到 SQL 语句并执行,攻击效果立即显现。

  • 二次注入:攻击者提交的恶意数据,在写入时被正确地转义、过滤或使用参数化处理,安全地存储进数据库;但当这些数据在后续的某个请求中被读取出来,并再次被拼接进新的 SQL 语句执行时,由于未做二次过滤,注入漏洞才真正被触发。

通俗地说:"第一次存储是安全的,第二次使用时是危险的。"

二次注入的关键在于,攻击载荷经过了"存储 → 读取 → 拼接执行"这一完整链条,攻击效果具有延迟性和隐蔽性,因而往往能绕过仅针对"输入"进行过滤的常规防御。


二、产生原理

二次注入产生的根本原因可以概括为一句话:

应用程序对写入数据库的数据做了过滤/转义,但对从数据库读取出来的数据却盲目信任,直接拼接到 SQL 语句中。

其完整流程如下:

为什么常见防御会失效?

以"对单引号进行转义"为例(如使用addslashes()或反斜杠转义):

  1. 攻击者提交用户名admin' --

  2. 应用层使用addslashes()转义后变为admin\' --,此时拼接进 INSERT 语句是安全的。

  3. 但数据库实际存储的字符串是admin' --(转义字符只在拼接 SQL 时起作用,并不写入库)。

  4. 后续某功能(如修改密码、查询资料)执行:

    UPDATE users SET password='新密码' WHERE username='admin' --'

    此时从库里读出的admin' --被原样拼接,'闭合了前面的引号,--注释掉了后续内容,注入由此触发。

关键认知:转义/过滤只能保证"这一次"拼接安全,并不能改变数据本身的"危险内容"。一旦这些内容被再次使用,就必须重新处理。


三、利用条件

要成功实施二次注入,通常需要满足以下条件:

  1. 存在"先存储后使用"的数据流:某功能将用户可控的数据写入数据库,另一功能又从数据库读取该数据。

  2. 写入时经过处理(可选但常见):写入端可能做了转义/过滤/参数化,导致一阶注入不可行,攻击者被迫走二次注入路径。

  3. 读取后的数据被拼接进 SQL:后续使用该数据时,没有使用参数化查询或再次过滤,而是直接拼接字符串。

  4. 攻击者可构造并观察到差异:通常需要借助布尔盲注、时间盲注或回显差异来判断注入结果。


四、利用过程示例

4.1 场景假设

一个网站有两个功能:

  • 注册功能:用户填写用户名、密码,注册账号(写入数据库)。

  • 修改密码功能:登录后输入新密码,系统根据当前登录用户名更新密码(读取用户名并拼接 SQL)。

4.2 攻击步骤

第一步:注册一个恶意用户名

攻击者注册用户名:admin' #

(假设注册时后端使用了转义处理,该用户名被安全地写入数据库,注册成功。)

数据库users表此时存储:

idusernamepassword
1admin' #123456

第二步:触发二次注入

攻击者以admin' #身份登录,进入修改密码功能,提交新密码hacked

后端修改密码的核心 SQL 大致为:

UPDATE users SET password = 'hacked' WHERE username = '$username';

其中$username是从数据库读出的当前登录用户名,即admin' #,拼接后变成:

UPDATE users SET password = 'hacked' WHERE username = 'admin' #';

由于#(或--)将其后内容注释,实际执行的语句等价于:

UPDATE users SET password = 'hacked' WHERE username = 'admin';

结果:攻击者成功将真正管理员账号admin的密码修改为hacked,从而接管管理员账号。

4.3 常见攻击目标

  • 垂直越权:普通用户通过注入影响高权限用户的数据(如上例)。

  • 获取敏感数据:借助布尔盲注/时间盲注逐字符读取其他表数据。

  • 删除/篡改数据:构造DELETEUPDATE等破坏性载荷。


五、常见应用场景

二次注入多发于以下功能点:

  1. 注册/登录 + 个人资料修改:恶意用户名在修改资料、改密时被再次拼接。

  2. 评论/留言 + 后台管理:评论内容被存储后,在后台的搜索、删除、筛选等 SQL 中被再次使用。

  3. 收货地址/联系人信息:存储的地址、姓名在订单查询、统计功能中被拼接。

  4. 用户昵称 + 论坛引用/回复:昵称在展示或引用他人帖子时被再次拼入查询。

  5. 导入的数据文件:批量导入的数据(CSV 等)在后续报表、检索中被拼接。

  6. 配置项/自定义字段:用户可配置的字段值在系统内部 SQL 中复用。


六、练习

一道恒学练习的ctf题目

在靶场发表一篇文章

总共有5个字段

拼接语句,再次查看文章

注入语句在页面回显

尝试查看当前数据库

成功得到当前数据库

获取数据库列名

1',2,(select group_concat(table_name) from information_schema.tables where table_schema='php_test'),4,NOW())--

获取数据库字段名

1',2,(select group_concat(column_name) from information_schema.columns where table_schema='php_test' and table_name='articles'),4,NOW())--

获取flag

1',2,(select group_concat(password) from php_test.users),4,NOW())--

sqli第24关

注册账户:admin'#

使用注册的账户登录,登录后可以修改密码

修改密码的逻辑:

UPDATE users SET password='新密码' WHERE username='admin'#' AND password='旧密码'

#注释后面的信息,从而变成修改管理员的账户admin密码为1234

最终生效语句:

UPDATE users SET password='新密码' WHERE username='admin'

使用修改后的密码成功登录admin账户

七、防御措施

二次注入的防御核心是:不信任任何进入 SQL 的数据,无论它来自用户输入还是数据库读取。

7.1 使用参数化查询(预编译语句)——最根本手段

无论数据来自哪里,一律使用占位符绑定参数,从根本上杜绝拼接:

// Java JDBC PreparedStatement String sql = "UPDATE users SET password = ? WHERE username = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, newPassword); ps.setString(2, username); ps.executeUpdate();
# Python cursor.execute( "UPDATE users SET password = %s WHERE username = %s", (new_password, username) )

参数化查询将"数据"与"代码"分离,任何来自数据库的字符串都只被当作数据,不可能被解析为 SQL 语法。

7.2 使用 ORM 框架

主流 ORM(如 Hibernate、MyBatis 的#{}、SQLAlchemy、Eloquent 等)默认对参数进行绑定。注意:

  • MyBatis 中务必使用#{}而非${}${}是字符串拼接,仍存在注入风险。

7.3 对"输出侧"同样进行过滤/转义

若因历史原因必须拼接 SQL,那么在从数据库读出数据并再次使用时,同样执行与输入侧一致的过滤、转义,而不是默认"库里的数据是干净的"。

7.4 最小权限原则

为应用配置最小化的数据库账号权限,限制 SQL 语句的破坏能力:

  • 不同功能使用不同权限的数据库账号。

  • 严格限制DELETEUPDATEDROP等高危操作权限。

7.5 白名单校验与输入约束

  • 对用户名、昵称等字段在写入时就限制字符集,例如只允许字母、数字、下划线,直接拒绝含特殊字符('"#--;等)的输入。

  • 从源头减少危险数据入库的可能性。

7.6 使用 Web 应用防火墙(WAF)与安全审计

  • 部署 WAF 可在一定程度上检测并拦截明显注入载荷。

  • 配合代码审计工具、静态扫描工具在开发阶段发现"数据二次使用但未过滤"的隐患点。

7.7 其他辅助措施

  • 安全编码培训与规范:建立统一的数据库访问层,强制使用参数化接口,禁止业务代码拼接 SQL。

  • 代码评审:重点审查"从数据库读取 → 拼接 SQL"的链路。

  • 定期渗透测试:二次注入隐蔽性强,需通过专业测试发现。


八、总结

维度一阶注入二次注入
触发时机当前请求立即触发后续请求延迟触发
载荷存放直接进入 SQL先入库,再取出进入 SQL
写入侧处理通常未过滤通常已过滤/转义
防御盲点输入过滤缺失对"库内数据"盲目信任
检测难度较低较高(隐蔽、延迟)

核心结论:

  1. 二次注入的根源是对"数据库中读出的数据"盲目信任,而本质上它仍是 SQL 注入。

  2. 防御的关键不是"在输入时过滤一次",而是彻底使用参数化查询,让所有数据——无论来源——都无法被当作 SQL 代码执行

  3. 只要坚持"数据与代码分离"这一原则,无论一阶还是二阶注入都能被有效防范。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 9:42:12

Java开发必备工具库:字符串、集合、加密、JDBC一站式封装

简介:这是一套面向Java中高级开发者的轻量级通用工具库,聚焦日常开发高频场景,显著降低字符串处理、日期计算、集合操作、文件IO、JDBC封装、JSON解析及HTTP调用等重复编码成本。资源共2000个文件,主体为1920个精心组织的Java工具…

作者头像 李华
网站建设 2026/9/6 13:02:09

合同管理系统开发等保 2.0 三级技术落地:加密、RBAC、审计与密钥管理的工程实现

等保 2.0 三级是金融、政务类合同系统的准入门槛。密评不是"上云 开 HTTPS"就完事,它会逐条核对口令复杂度、双因素认证、日志留存时长、密钥管理归属。本文给出一套可落地、且天然契合信创语境的数据安全架构。 一、三级到底卡什么三级核心要求可浓缩为…

作者头像 李华
网站建设 2026/9/6 11:27:54

蚁小二的视频和图文分发有什么区别,支持哪些内容类型

很多新手在使用蚁小二的时候,分不清平台视频分发和图文分发的具体区别,也不清楚工具具体支持哪些内容类型,很容易出现发文格式错误、发布失败、内容不匹配平台规则的问题。本文所有功能介绍均严格参照蚁小二官方公开功能与规则撰写&#xff0…

作者头像 李华
网站建设 2026/9/4 8:35:12

C语言字符串数组详解:二维数组与指针数组的核心区别

字符串数组是 C 语言里最容易让人绕晕的知识点之一。很多初学者学到数组时觉得没问题,学到指针时也勉强能跟上,但一碰到“字符串数组”这个概念,就开始分不清char name[3][20]和char *name[3]到底有什么区别,更不用说在写用户名单…

作者头像 李华
网站建设 2026/9/4 7:36:46

C语言字符串数组:二维数组与指针数组的本质区别与工程选择

字符串数组在C语言里看起来很简单:不就是一堆字符串装进一个数组吗?但真上手写几行代码,很多人会发现不对劲——为什么有时候能改字符串里的字符,有时候一改就崩溃?为什么char *names[]和char names[][20]看着差不多&a…

作者头像 李华
网站建设 2026/9/6 5:26:01

P4角色桌Replay完结篇:雪山密室的终局推理与叙事复盘

这个标题信息量其实很大:P4、角色桌、Replay、完结篇、雪山密室、第九话、灯推理。它既是一个创作项目的收尾,也是一个非常典型的“叙事型角色桌Replay”作品。这篇博客不写代码,但要把它当成一个完整的“创作项目”来拆解:这个系…

作者头像 李华