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. 最小权限原则:数据库账户不应有高权限,限制其只能访问必要的表和操作
相关推荐
喝奶茶的Blair2 小时前
PHP应用&文件操作安全&上传下载&任意读取删除&目录遍历&文件包含(2024小迪安全Day32笔记)
笔记·安全·web安全·php
Run Freely9372 小时前
MySqL-day4_03(索引)
数据库·mysql
编程充电站pro2 小时前
聚合函数陷阱:AVG 和 GROUP BY 搭配使用注意点
数据库·sql
会开花的二叉树3 小时前
实战:基于 BRPC+Etcd 打造轻量级 RPC 服务 —— 从注册到调用的完整实现
网络·数据库·c++·rpc·etcd
修炼果3 小时前
为什么使用 Redis 存储Oauth的state 参数,可有效防止 CSRF 攻击
数据库·redis·csrf
cookqq4 小时前
MongoDB源码delete分析观察者getOpObserver()->onDelete
数据库·sql·mongodb·nosql
曼曼青青草4 小时前
智能体介绍及搭建实战(智能运营)
大数据·数据库·人工智能
荷兰小香猪_014 小时前
MongoDB数据类型与python操作
数据库·mongodb
qianmo20214 小时前
gpt-4o+deepseek+R生成热力图表
java·数据库·r语言