jjzjj

php - 绕过 mysql_real_escape_string() 的 SQL 注入(inject)

coder 2023-10-06 原文

即使使用 mysql_real_escape_string() 是否也有 SQL 注入(inject)的可能性?功能?

考虑这个示例情况。 SQL 在 PHP 中是这样构造的:

$login = mysql_real_escape_string(GetFromPost('login'));
$password = mysql_real_escape_string(GetFromPost('password'));

$sql = "SELECT * FROM table WHERE login='$login' AND password='$password'";

我听到很多人对我说这样的代码仍然很危险,即使使用 mysql_real_escape_string() 也可能被黑客入侵。使用的函数。但我想不出任何可能的漏洞?

像这样的经典注入(inject):
aaa' OR 1=1 --

不工作。

你知道任何可能的注入(inject)可以通过上面的 PHP 代码吗?

最佳答案

简短的回答是 是的,是的,有办法绕过mysql_real_escape_string() .
#对于非常模糊的边缘情况!!!
长答案并不那么容易。它基于攻击 demonstrated here .
攻击
所以,让我们从展示攻击开始……

mysql_query('SET NAMES gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
在某些情况下,这将返回超过 1 行。让我们剖析一下这里发生了什么:
  • 选择字符集
    mysql_query('SET NAMES gbk');
    
    为了使这种攻击起作用,我们需要服务器在连接上期望的编码 '如在 ASCII 中,即 0x27并有一些字符的最后一个字节是 ASCII \0x5c .事实证明,MySQL 5.6 默认支持 5 种这样的编码:big5 , cp932 , gb2312 , gbksjis .我们将选择 gbk这里。
    现在,非常重要的是要注意 SET NAMES 的使用。这里。这将设置字符集 在服务器上 .如果我们使用对 C API 函数的调用 mysql_set_charset() ,我们会很好(自 2006 年以来的 MySQL 版本)。但更多关于为什么在一分钟...
  • 有效载荷
    我们将用于此注入(inject)的有效负载以字节序列 0xbf27 开头。 .在 gbk ,这是一个无效的多字节字符;在 latin1 ,它是字符串 ¿' .请注意,在 latin1 gbk , 0x27本身就是一个字面值 '特点。
    我们选择这个有效载荷是因为,如果我们调用 addslashes()在它上面,我们插入一个 ASCII \0x5c , 在 ' 之前特点。所以我们最终会得到 0xbf5c27 ,其中在 gbk是一个两个字符的序列:0xbf5c其次是 0x27 .或者换句话说,一个有效字符后跟一个未转义的 ' .但我们没有使用 addslashes() .那么进入下一步...
  • mysql_real_escape_string()
    C API 调用 mysql_real_escape_string()不同于 addslashes()因为它知道连接字符集。因此它可以对服务器期望的字符集正确执行转义。但是,到目前为止,客户认为我们仍在使用 latin1对于连接,因为我们从来没有告诉过它。我们确实告诉服务器我们正在使用 gbk ,但客户仍认为是 latin1 .
    因此调用 mysql_real_escape_string()插入反斜杠,我们有一个空闲挂 '我们“逃脱”内容中的角色!事实上,如果我们看 $vargbk字符集,我们会看到:
    縗' OR 1=1 /*

    Which is exactly what the attack requires.

  • The Query

    This part is just a formality, but here's the rendered query:

    SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1
    

  • 恭喜,你刚刚成功攻击了一个使用 mysql_real_escape_string() 的程序...
    坏的
    它变得更糟。 PDO默认使用 MySQL 模拟准备好的语句。这意味着在客户端,它基本上通过 mysql_real_escape_string() 执行 sprintf (在 C 库中),这意味着以下将导致成功注入(inject):
    $pdo->query('SET NAMES gbk');
    $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
    $stmt->execute(array("\xbf\x27 OR 1=1 /*"));
    
    现在,值得注意的是,您可以通过禁用模拟准备好的语句来防止这种情况:
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    
    这通常会产生一个真正的准备好的语句(即数据在与查询分开的数据包中发送)。但是,请注意 PDO 会默默地fallback模拟 MySQL 无法本地准备的语句:它可以是 listed在手册中,但要注意选择合适的服务器版本)。
    丑陋的
    我一开始就说过,如果我们使用 mysql_set_charset('gbk') 就可以避免这一切。而不是 SET NAMES gbk .如果您使用的是自 2006 年以来的 MySQL 版本,那就是正确的。
    如果您使用的是较早的 MySQL 版本,则 bugmysql_real_escape_string()意味着即使客户端已被正确通知连接编码,无效的多字节字符(例如我们的有效负载中的那些字符)也被视为单字节进行转义,因此这种攻击仍然会成功。该错误已在 MySQL 中修复 4.1.20 , 5.0.225.1.11 .
    但最糟糕的是PDO没有为 mysql_set_charset() 公开 C API直到 5.3.6,所以在以前的版本中它 不能 为每个可能的命令阻止这种攻击!
    它现在公开为 DSN parameter .
    拯救的恩典
    正如我们一开始所说的,要使这种攻击起作用,必须使用易受攻击的字符集对数据库连接进行编码。 utf8mb4 不易受到攻击,但可以支持每个 Unicode 字符:因此您可以选择使用它——但它仅从 MySQL 5.5.3 开始可用。另一种选择是 utf8 ,它也不容易受到攻击,并且可以支持整个 Unicode Basic Multilingual Plane .
    或者,您可以启用 NO_BACKSLASH_ESCAPES SQL 模式,它(除其他外)改变了 mysql_real_escape_string() 的操作.启用此模式后,0x27将替换为 0x2727而不是 0x5c27因此,转义过程无法在以前不存在的任何易受攻击的编码中创建有效字符(即 0xbf27 仍然是 0xbf27 等)——因此服务器仍然会拒绝该字符串为无效。但是,请参阅 @eggyal's answer对于使用此 SQL 模式可能产生的不同漏洞。
    安全示例
    以下示例是安全的:
    mysql_query('SET NAMES utf8');
    $var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
    mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
    
    因为服务器正在等待 utf8 ...
    mysql_set_charset('gbk');
    $var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
    mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
    
    因为我们已经正确设置了字符集,所以客户端和服务器匹配。
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $pdo->query('SET NAMES gbk');
    $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
    $stmt->execute(array("\xbf\x27 OR 1=1 /*"));
    
    因为我们已经关闭了模拟准备好的语句。
    $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);
    $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
    $stmt->execute(array("\xbf\x27 OR 1=1 /*"));
    
    因为我们已经正确设置了字符集。
    $mysqli->query('SET NAMES gbk');
    $stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
    $param = "\xbf\x27 OR 1=1 /*";
    $stmt->bind_param('s', $param);
    $stmt->execute();
    
    因为 MySQLi 一直在做真正的准备好的语句。
    包起来
    如果你:
  • 使用 MySQL 的现代版本(5.1 后期,所有 5.5、5.6 等) mysql_set_charset()/$mysqli->set_charset()/PDO 的 DSN 字符集参数(在 PHP ≥ 5.3.6 中)


  • 不要使用易受攻击的字符集进行连接编码(您只使用 utf8/latin1/ascii/等)

  • 你是 100% 安全的。
    否则,您很容易受到攻击 即使您正在使用 mysql_real_escape_string() ...

    关于php - 绕过 mysql_real_escape_string() 的 SQL 注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31011339/

    有关php - 绕过 mysql_real_escape_string() 的 SQL 注入(inject)的更多相关文章

    1. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

      对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

    2. ruby - 从 String#split 返回的零长度字符串 - 2

      在Ruby1.9.3(可能还有更早的版本,不确定)中,我试图弄清楚为什么Ruby的String#split方法会给我某些结果。我得到的结果似乎与我的预期相反。这是一个例子:"abcabc".split("b")#=>["a","ca","c"]"abcabc".split("a")#=>["","bc","bc"]"abcabc".split("c")#=>["ab","ab"]在这里,第一个示例返回的正是我所期望的。但在第二个示例中,我很困惑为什么#split返回零长度字符串作为返回数组的第一个值。这是什么原因呢?这是我所期望的:"abcabc".split("a")#=>["bc"

    3. 使用canal同步MySQL数据到ES - 2

      文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

    4. Hive SQL 五大经典面试题 - 2

      目录第1题连续问题分析:解法:第2题分组问题分析:解法:第3题间隔连续问题分析:解法:第4题打折日期交叉问题分析:解法:第5题同时在线问题分析:解法:第1题连续问题如下数据为蚂蚁森林中用户领取的减少碳排放量iddtlowcarbon10012021-12-1212310022021-12-124510012021-12-134310012021-12-134510012021-12-132310022021-12-144510012021-12-1423010022021-12-154510012021-12-1523.......找出连续3天及以上减少碳排放量在100以上的用户分析:遇到这类

    5. sql - 查询忽略时间戳日期的时间范围 - 2

      我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时

    6. ruby - 这个 ruby​​ 注入(inject)魔术是如何工作的? - 2

      我今天看到了一个ruby​​代码片段。[1,2,3,4,5,6,7].inject(:+)=>28[1,2,3,4,5,6,7].inject(:*)=>5040这里的注入(inject)和之前看到的完全不一样,比如[1,2,3,4,5,6,7].inject{|sum,x|sum+x}请解释一下它是如何工作的? 最佳答案 没有魔法,符号(方法)只是可能的参数之一。这是来自文档:#enum.inject(initial,sym)=>obj#enum.inject(sym)=>obj#enum.inject(initial){|mem

    7. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

      我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

    8. ruby - 了解在 Ruby 中与 lambda 一起使用的 inject 行为 - 2

      我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject

    9. ruby - 如何使用 ruby​​ mysql2 执行事务 - 2

      我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi

    10. sql - 在 Rails Console for PostgreSQL 的表中显示数据 - 2

      我找到了这样的东西:Rails:Howtolistdatabasetables/objectsusingtheRailsconsole?这一行没问题:ActiveRecord::Base.connection.tables并返回所有表但是ActiveRecord::Base.connection.table_structure("users")产生错误:ActiveRecord::Base.connection.table_structure("projects")我认为table_structure不是Postgres方法。如何列出Postgres数据库的Rails控制台中表中的所有

    随机推荐