SQL 注入攻防:绕过注释符过滤的N种方法

文章目录

引言

想象一下,你是一名网站安全工程师,刚刚部署了最新的WAF规则,过滤了所有常见的 SQL 注入关键字和符号(如 -- , #)。你觉得高枕无忧了,但攻击者依然轻松拿到了数据库权限... 问题出在哪里

很多时候,我们低估了注释符在 SQL 注入中的重要性,以及攻击者在面对过滤时表现出来的"创造力"。本文将带你深入探讨,当注释符被过滤后,攻击者是如何巧妙绕过的

本文将介绍注释符的作用、常见的过滤方式,并重点详解多种绕过注释符过滤的技巧,最终给出真正有效的防御方案


注释符是什么

注释符 是编程语言和SQL等查询语言中一种特殊的语法符号。它的核心使命是:告诉编译器或解释器,被它标记的文本内容不是需要执行的代码,而是给人看的说明、笔记或注解

你可以把它理解为代码世界里的 "便利贴"


核心作用与目的

相信学过编程语言的都不陌生

  1. 提高代码可读性

    在复杂的SQL查询中,开发者可以用注释来解释某段代码的用途、逻辑、作者或修改日期。这让其他阅读代码的人(或未来的自己)能更快地理解代码意图

  2. 调试和排除代码

    在测试或调试时,如果不想执行某段SQL代码,不需要直接删除它。只需用注释符将其"注释掉",数据库就会忽略它。这可以方便后续需要时再恢复


SQL中常见的注释符类型

SQL主要支持两种注释方式:

  1. 单行注释 (Single-line Comments)

    • 注释掉从符号开始到行尾的所有内容

    • --两个连字符 ,一般用- -+表示,ps:- -中间没有空格,因为显示问题在此加个空格):这是最通用 的标准SQL单行注释符。注意: 许多数据库(如Oracle, PostgreSQL, SQL Server)要求 -- 后面必须跟一个空格 或控制字符(如换行),否则可能无效或报错。正确写法是 -- 注释内容

    • #(井号) :主要用于 MySQL 及其分支(如MariaDB),不是所有数据库都支持

  2. 多行注释 (Block Comments)

    • 注释掉一个连续的代码块,可以跨越多行
    • / */ :这是 通用 *的多行注释符,绝大多数数据库都支持。/* 表示注释开始,*/ 表示注释结束

注释符在 SQL 注入中的使命

注释符在 SQL 注入攻击中扮演着"清道夫"和"魔术师"的角色,其使命是巧妙地操纵原始SQL查询的结构,使攻击者注入的恶意代码能够顺利执行,同时"处理"掉后续会引发语法错误的冗余部分

我们可以将它的使命分解为以下几个核心任务:

1. 主要使命:截断查询,消除语法错误

这是注释符最经典和最常见的用途。Web应用程序通常会拼接用户输入来构建SQL查询

拿 less - 1 举例

php 复制代码
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

这里 $id 是用户提交的输入

  • 攻击者的目标:绕过密码验证,以任何用户(例如管理员 admin)身份登录
  • 攻击输入
    • URl:?id=1' --+

拼接后的最终查询变为:

php 复制代码
$sql="SELECT * FROM users WHERE id='?id=1' --+' LIMIT 0,1";

现在,我们来看注释符 --+ 是如何完成它的使命的:

  1. 闭合引号 :注入的 ?id=1' --+ 中的单引号首先完成了字符串的闭合。原本的 '$id' 变成了 '?id=1' --+'
  2. 注释符生效--+ 是单行注释符,它会将其后的所有内容都标记为注释
  3. 清除冗余 :于是,--+ 之后的所有字符 ' LIMIT 0,1" 都被数据库引擎忽略不计

最终,数据库真正执行的查询只剩下:

sql 复制代码
SELECT * FROM users WHERE id='?id=1'

这条查询返回的回显就能让攻击者看出破绽

在这里,注释符的使命就是 "清理战场",将原本会导致语法错误(因为多出了一部分字符串和关键字)的无效查询,变成了一个语法完全正确且符合攻击者意图的有效查询

2. 辅助使命:绕过过滤或特定场景

注释符还有其他巧妙的用途:

a. 内联注入(Inline Injection)

有时注入点不在语句末尾,而是在查询中间。注释符可以用来终止当前子句,然后开始一个新的、完全不同的语句

b. 绕过简单过滤(Less Common)

某些非常初级的防御手段可能会检查常见的关键字如 ANDORSELECT,但可能不会严格检查注释符。攻击者可以利用注释符拆分关键字来绕过(但这种绕过于时,现代WAF很难用这招绕过)


开发者如何过滤注释符

核心思想:开发者试图通过输入过滤黑名单机制,在用户输入拼接到SQL语句之前,移除或转义掉注释符,从而"净化"输入


  1. 直接字符串替换过滤
    防御思路 :在最基础的层面,开发者会尝试直接移除或替换用户输入中的注释符

    例如:

    Less-23

  2. 正则表达式黑名单过滤
    防御思路:使用更复杂的正则表达式来识别和移除注释模式

  3. 转义特殊字符
    防御思路:不直接移除注释符,而是转义引号,使注释符失去上下文


攻击者绕过注释符过滤的奇技淫巧

  1. 编码绕过技巧
  • 原理:应用程序的过滤逻辑可能只检查明文,未进行解码或多次解码

  • URL编码绕过

    技巧:使用URL编码表示注释符

  • 双重URL编码绕过

    技巧:对已经编码的内容再次编码

  • Unicode/UTF-8编码绕过

    技巧:使用Unicode表示字符

  1. 利用空白符与制表符
  • 原理:过滤逻辑可能是简单的字符串匹配 "--",如果在中间插入空白符,可能无法匹配
    • Payload示例:

      • - - (中间有空格):admin' OR 1=1- -+
      • %0A-- (换行符后跟注释):...%0A-- ...
      • 注意:需要测试数据库对空白符的解析特性。MySQL 通常允许注释符后跟空白符
  1. 不使用注释符的替代方案
  • 原理:最高明的绕过是根本不需要它。思考:为什么要用注释符?是为了处理查询的后半部分。那有没有不用注释符也能让后半部分失效的方法?

    • 精心构造Payload平衡引号

技巧:通过精心构造使整个查询语法正确,无需注释掉后续部分

示例Payload:

sql 复制代码
' or '1'='1' and '1'='1

拼接后:

sql 复制代码
SELECT * FROM users WHERE username = '' OR '1'='1' AND '1'='1' AND password = 'xxx'

由于AND优先级高于OR,实际等价于:

sql 复制代码
SELECT * FROM users WHERE (username = '') OR ('1'='1' AND '1'='1' AND password = 'xxx')
  • 使用CASE语句

技巧:使用CASE语句构造条件注入,无需注释符

示例Payload:

sql 复制代码
' OR CASE WHEN (SELECT COUNT(*) FROM users) > 0 THEN 1 ELSE 0 END = 1 AND '1'='1
  1. 利用过滤逻辑缺陷
  • 递归过滤绕过

技巧:如果过滤只执行一次,可以构造使过滤后产生新注释符的Payload

示例Payload:

text 复制代码
admin'-- -

过滤--后变为:

text 复制代码
admin' -

在某些上下文中,-可能被解释为负号或其它操作符,但仍可能造成注入

  • 上下文区分绕过

技巧:利用过滤系统无法区分代码上下文的特点

示例Payload:

text 复制代码
admin' AND password = '' OR 1=1--

即使过滤了注释符,前面部分仍可能构成有效注入


实战演练

理论再多,不如实践一番
环境设置 : 本示例为 sqli-labs 23

提交参数

php 复制代码
?id=1

测试闭合方式,运气很好一下子就试出来了,通过输入 1' 后出现报错,且报错信息中显示 ''1'' LIMIT 0,1' ,可判断出注入点为单引号闭合

php 复制代码
?id=1'

按正常逻辑来说应该添加注释符进一步验证,但这里却出现了报错

php 复制代码
?id=1' --+

查看本题源码,发现过滤了注释符

这段代码的含义为:

通过移除 #-- 来阻止攻击者注释掉SQL查询的后续部分

所以通过构造Payload平衡引号 即可绕过

php 复制代码
?id=1' '

构建注入语句即可查询出库名

php 复制代码
?id=-1' union select 1,database(),3 '

大家也可以试一下其他方法,例如:

通过 or '1'='1 绕过

php 复制代码
?id=-1' union select 1,database(),3 or '1'='1

通过老版本的漏洞 ;%00 也行 (注:这不是通用方案,且在现代环境中大多已修复)

php 复制代码
?id=-1' union select 1,database(),3 ;%00

我在测试发现 ;%00 也能绕过的时候感到非常新奇,没想到这么经典的绕过技巧在这里也能使用

;%00 成功绕过原理如下:

由于篇幅原因,后续的注入语句就不再展示了,不会的可以看:字符型注入


防御之道:如何防止注入者绕过注释符

通过绕过方法哪里也能看出问题所在

  • 不要试图单纯地黑名单过滤:本文展示的绕过技巧已经证明这是徒劳的

  • 终极解决方案

    1. 预编译语句(Prepared Statements)绝对首选。将SQL语句结构与数据完全分离,从根本上杜绝注入,注释符只会被当作数据内容,而非指令
    2. 使用严格的输入验证:对于类型确定的输入(如数字ID),强制转换为 int 型
    3. 使用权威的安全库:如 PHP 的 PDO、Java 的 MyBatis 等
    4. 最小权限原则:数据库账户不应有高权限,限制其只能访问必要的表和操作
相关推荐
nongcunqq1 小时前
abap 操作 excel
java·数据库·excel
rain bye bye2 小时前
calibre LVS 跑不起来 就将setup 的LVS Option connect下的 connect all nets by name 打开。
服务器·数据库·lvs
旺仔Sec3 小时前
新疆维吾尔自治区第一届“丝路杯”网络安全大赛暨2026年新疆职业院校技能大赛网络安全赛项竞赛样题
安全·web安全
阿里云大数据AI技术3 小时前
云栖实录|MaxCompute全新升级:AI时代的原生数据仓库
大数据·数据库·云原生
不剪发的Tony老师4 小时前
Valentina Studio:一款跨平台的数据库管理工具
数据库·sql
重生之我要当java大帝4 小时前
java微服务-尚医通-编写医院设置接口下
java·开发语言·sql
weixin_307779134 小时前
在 Microsoft Azure 上部署 ClickHouse 数据仓库:托管服务与自行部署的全面指南
开发语言·数据库·数据仓库·云计算·azure
六元七角八分4 小时前
pom.xml
xml·数据库
虚行4 小时前
Mysql 数据同步中间件 对比
数据库·mysql·中间件
奥尔特星云大使4 小时前
mysql读写分离中间件Atlas安装部署及使用
数据库·mysql·中间件·读写分离·atlas