PDO 准备好的语句足以防止 SQL 注入吗?

2023-12-22

假设我有这样的代码:

$dbh = new PDO("blahblah");

$stmt = $dbh->prepare('SELECT * FROM users where username = :username');
$stmt->execute( array(':username' => $_REQUEST['username']) );

PDO 文档说:

准备好的语句的参数不需要加引号;司机会帮你处理。

这真的是我需要做的一切来避免 SQL 注入吗?真的那么容易吗?

如果 MySQL 有影响的话,你可以假设它。另外,我真的只是对使用准备好的语句来对抗 SQL 注入感到好奇。在这种情况下,我不关心 XSS 或其他可能的漏洞。


简短的答案是YES,如果使用得当,PDO 准备是足够安全的。


我正在适应这个答案 https://stackoverflow.com/a/12118602/338665谈谈PDO...

长答案并不那么容易。它基于攻击在这里展示 http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string.

攻击

那么,让我们首先展示攻击......

$pdo->query('SET NAMES gbk');
$var = "\xbf\x27 OR 1=1 /*";
$query = 'SELECT * FROM test WHERE name = ? LIMIT 1';
$stmt = $pdo->prepare($query);
$stmt->execute(array($var));

在某些情况下,这将返回多于 1 行。让我们剖析一下这里发生了什么:

  1. 选择字符集

    $pdo->query('SET NAMES gbk');
    

    为了使这种攻击起作用,我们需要服务器期望在连接上进行编码'如 ASCII 所示,即0x27 and某些字符的最后一个字节是 ASCII\ i.e. 0x5c。事实证明,MySQL 5.6 默认支持 5 种这样的编码:big5, cp932, gb2312, gbk and sjis。我们会选择gbk here.

    现在,注意使用非常重要SET NAMES这里。这设置了字符集在服务器上。还有另一种方法可以做到这一点,但我们很快就会实现。

  2. 有效负载

    我们将用于此注入的有效负载以字节序列开始0xbf27. In gbk,这是一个无效的多字节字符;在latin1,这是字符串¿'。请注意,在latin1 and gbk, 0x27其本身就是一个字面意思'特点。

    我们选择这个有效负载是因为,如果我们调用addslashes()在它上面,我们插入一个 ASCII\ i.e. 0x5c, 之前'特点。所以我们最终会得到0xbf5c27,其中在gbk是一个两个字符的序列:0xbf5c其次是0x27。或者换句话说,一个valid字符后跟未转义的字符'。但我们没有使用addslashes()。那么继续下一步...

  3. $stmt->执行()

    这里要认识到的重要一点是,PDO 默认情况下会执行以下操作:NOT做真正准备好的陈述。它模拟它们(对于 MySQL)。因此,PDO 在内部构建查询字符串,调用mysql_real_escape_string()(MySQL C API 函数)每个绑定字符串值。

    C API 调用mysql_real_escape_string()不同于addslashes()因为它知道连接字符集。因此它可以对服务器期望的字符集正确执行转义。然而,到目前为止,客户认为我们仍在使用latin1对于这种联系,因为我们从未另外说过。我们确实告诉了server我们正在使用gbk,但是client仍然认为是latin1.

    因此调用mysql_real_escape_string()插入反斜杠,我们就有了一个自由悬挂'我们“逃脱”内容中的角色!事实上,如果我们看一下$var in the gbk字符集,我们会看到:

    
    縗' OR 1=1 /*  

    这正是攻击所需要的。

  4. 查询

    这部分只是一种形式,但这是呈现的查询:

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

恭喜,您刚刚使用 PDO 准备语句成功攻击了一个程序...

简单的修复

现在,值得注意的是,您可以通过禁用模拟准备语句来防止这种情况:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

这会usually产生真正的准备好的语句(即数据在与查询不同的数据包中发送)。但是,请注意,PDO 会默默地fallback https://github.com/php/php-src/blob/master/ext/pdo_mysql/mysql_driver.c#L210模拟 MySQL 本身无法准备的语句:它可以的语句是listed http://dev.mysql.com/doc/en/sql-syntax-prepared-statements.html手册中,但要注意选择适当的服务器版本)。

正确的修复方法

这里的问题是我们使用了SET NAMES而不是 C APImysql_set_charset()。否则的话,攻击就不会成功。但最糟糕的是 PDO 没有公开 C APImysql_set_charset()直到 5.3.6,所以在之前的版本中cannot针对每个可能的命令防止这种攻击! 现在它被曝光为DSN参数 http://www.php.net/manual/en/ref.pdo-mysql.connection.php,应该使用代替 SET NAMES...

这是假设我们使用的是 2006 年以来的 MySQL 版本。如果您使用的是早期的 MySQL 版本,那么bug http://bugs.mysql.com/bug.php?id=8378 in mysql_real_escape_string()意味着无效的多字节字符(例如我们的有效负载中的字符)被视为单个字节以用于转义目的即使客户端已被正确告知连接编码所以这次攻击还是会成功的。 MySQL 中的错误已修复4.1.20 http://dev.mysql.com/doc/refman/4.1/en/news-4-1-20.html, 5.0.22 http://dev.mysql.com/doc/relnotes/mysql/5.0/en/news-5-0-22.html and 5.1.11 http://dev.mysql.com/doc/relnotes/mysql/5.1/en/news-5-1-11.html.

拯救的恩典

正如我们一开始所说的,要使这种攻击起作用,数据库连接必须使用易受攻击的字符集进行编码。utf8mb4 http://dev.mysql.com/doc/en/charset-unicode-utf8mb4.html is 不脆弱但还可以支持everyUnicode 字符:因此您可以选择使用它,但它仅从 MySQL 5.5.3 开始可用。另一种选择是utf8 http://dev.mysql.com/doc/en/charset-unicode-utf8.html,这也是不脆弱并且可以支持整个Unicode基础多语种飞机 http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane.

或者,您可以启用NO_BACKSLASH_ESCAPES http://dev.mysql.com/doc/en/sql-mode.html#sqlmode_no_backslash_escapesSQL 模式,它(除其他外)改变了mysql_real_escape_string()。启用此模式后,0x27将被替换为0x2727而不是0x5c27以及转义过程cannot在以前不存在的任何易受攻击的编码中创建有效字符(即0xbf27还是0xbf27等)——因此服务器仍然会拒绝该字符串,因为该字符串无效。不过,请参阅@eggyal 的回答 https://stackoverflow.com/a/23277864/623041针对使用此 SQL 模式(尽管不是使用 PDO)可能出现的不同漏洞。

安全示例

以下示例是安全的:

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 始终执行真正的准备好的语句。

包起来

If you:

  • 使用现代版本的 MySQL(5.1 晚期、所有 5.5、5.6 等)ANDPDO 的 DSN 字符集参数(在 PHP ≥ 5.3.6 中)

OR

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

OR

  • Enable NO_BACKSLASH_ESCAPESSQL模式

你100%安全。

否则你很脆弱即使您正在使用 PDO 准备好的语句...

Addendum

我一直在慢慢地开发一个补丁,以将默认值更改为不模拟,为未来版本的 PHP 做好准备。我遇到的问题是,当我这样做时,很多测试都会中断。一个问题是模拟的准备只会在执行时抛出语法错误,但真正的准备会在准备时抛出错误。因此这可能会导致问题(这也是测试失败的部分原因)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PDO 准备好的语句足以防止 SQL 注入吗? 的相关文章

  • 检测 JPEG 图像质量

    我允许用户上传图像 但是 我希望保持 JPEG 质量不超过 90 我打算做的是检测当前的质量 如果低于 90 什么都不做 如果超过90 则使用Image Magick将图像重新压缩到90 可以这样做吗 我更喜欢 PHP 但任何语言都会有帮助
  • 只获取倒数第二条记录 - mysql-query

    我有一个如下表记录 my table id rating description 1 0 0 bed 2 1 0 good 3 0 0 bed 4 1 0 good 5 0 0 bed 6 0 0 bed 7 0 0 bed 现在我通过评级
  • PHP - 如何将图像资源写入文件

    我用了函数imagecopyresampled到裁剪图像 我尝试使用file put contents和 fwrite 将 resized image 写入硬盘上的文件 但所有尝试都失败了 我可以将调整大小的图像写入磁盘而不是使用image
  • 用于验证 ip 列表中的 ip 范围的正则表达式

    我有正则表达式用于验证 50 个 ips 逗号分隔的列表 25 0 5 2 0 4 0 9 01 0 9 0 9 3 25 0 5 2 0 4 0 9 01 0 9 0 9 1 50 列表示例 10 10 10 1 127 0 0 1 现在
  • PHP-将字符串转换为unicode

    我在做这个工作 source mb convert encoding test unicode utf 8 source unpack C source var dump source return array size 8 1 gt in
  • 正则表达式将从文本文件中提取句子

    我需要一个正则表达式来从文本文件中提取句子 示例文本 以 2004 年底发生的亚洲海啸灾难为例 对 Google 新闻 http news google com 的查询在一个月内 1 月 17 日 返回了超过 80 000 篇有关该事件的在
  • 是否存在可处理 PRC/.mobi 文件的 PHP 库?

    我正在写一个WordPress 插件 http github com chrisclarke eBook Export Plugin for WordPress以大多数主要电子书格式从选定类别创建电子书 我想支持 MobiPocket 因为
  • Stripe 支付网关使用 PayumBundle 创建定期付款

    我在用支付包 https github com Payum PayumBundle将 Stripe 支付网关集成到我的 symfony2 应用程序中 我可以创建成功的直接付款 但无法创建定期付款 因为捆绑包的文档非常差 我的问题是如何使用
  • PHP 数组到 JavaScript 数组

    假设我在 php 中有这个数组 cities array Caracas gt array air gt array 4 3 5 Working Days Saturday sea gt array 18 3 5 Days Wednesda
  • 使用 OpenSSL 在 PHP 中进行 AES 加密/在 Node.js 中进行解密

    我正在使用 PHP 和 Nodejs 使用 OpenSSL 进行对称加密 PHP 使用 OpenSSL 库 Node js 解密基于实现的加密 问题是 Node js 中的解密文本只是部分正确 PHP 加密函数 function encry
  • 自动生成PHP代码的策略?

    我正在开发一个框架 在后端创建 ER 图后 该框架可以动态地对自身的各个部分进行编程 作为一名 PHP 新手 我想知道是否还有比打开一个纯 xyz php 文本文件然后将动态生成的代码添加到该文件更奇特的事情 是的 尝试一下Zend Cod
  • 保护 REST 和 JSON

    我想利用 RESTful 架构构建提供 JSON 数据的 Web 服务 但我只想要我自己的客户端应用程序可以从我的网络服务请求 基本上 我的 Web 服务包含不供公众使用的敏感数据 但我想以这种方式构建它 以便我可以构建连接到我的 Web
  • 在 jQuery AJAX 成功中从 MySql 获取特定响应

    好吧 我有这个 ajax 代码 它将在 Success 块中返回 MySql 的结果 ajax type POST url index php success function data alert data My Query sql SE
  • 添加 current_page_item 类

    我正在研究 WordPress 设计 我想创建一个自定义菜单 items wp get nav menu items Menu array order gt ASC orderby gt menu order post type gt na
  • 使用 Laravel 4 验证多个文件上传

    如何在 Laravel 4 中验证上传文件的数组 我已将其设置为允许多个文件 并且已测试这些文件是否存在于 Input file files 数组中 但如何验证每个文件呢 这是我尝试过的 notesData array date gt In
  • 如何使用 PHP 正确添加跨站请求伪造 (CSRF) 令牌

    我正在尝试为我网站上的表单添加一些安全性 其中一个表单使用 AJAX 另一个表单是简单的 联系我们 表单 我正在尝试添加 CSRF 令牌 我遇到的问题是令牌有时只显示在 HTML 值 中 其余时间 该值为空 这是我在 AJAX 表单上使用的
  • 让登录更安全

    我已使用此代码进行管理员登录 仅当用户输入正确的用户名和密码时才应打开loginhome php 但后来我意识到这根本不安全 任何人都可以直接访问 mywebsite loginhome php 而无需登录 注销后 可以使用后退按钮打开 l
  • PHP MySql 百分比

    我的问题是关于百分比 我不是专家 所以我会尽力以更好的方式进行解释 我的 mysql 服务器中有一个表 假设有 700 条记录 如下所示 Name country language Birth Lucy UK EN 1980 Mari Ca
  • Laravel 迁移错误 :: PDOException,找不到驱动程序

    我正在尝试制作 Laravel 但在迁移时卡住了 当我在终端中输入 php artisan migrate 时 显示 PDOException 错误 附上我的终端和 phpinfo 的屏幕截图 这是什么问题 我该如何解决这个问题 我正在使用
  • Zend Framework 生成唯一的字符串

    我想生成一个唯一的 4 6 个字符长的字母数字字符串 以便与每个记录 用户 一起保存在数据库中 db 字段具有唯一索引 因此尝试保存预先存在的字符串会生成错误 现在我正在生成一个随机字符串并使用 try catch 因此在添加新记录时如果抛

随机推荐

  • Objective C 未定义符号编译错误

    请帮助 我在目标 c 中的第一个程序 逐字逐句地遵循教程 但它给了我这个错误 我不太知道如何阅读目标 c SimpleCar h import
  • Facebook 请求代码

    我有一个 Activity 应该处理来自 Facebook SDK 和其他自定义 Activity 的结果 我在哪里可以找到 Facebook SDK 使用的 requestCodes 以免我的活动使用相同的 requestCodes 我应
  • EditText 自动换行

    我有这样的布局
  • 如何更改 html5 canvas 的尺寸而不缩放内容

    我像这样初始化画布
  • CXF - Wsdl2java - XX 属性已定义

    我使用 CXF 生成客户端类来访问 Web 服务服务器 Web 服务基于 WCF NET 当我调用 wsdl2java 时 出现以下错误 The id property is already defined use
  • 单元测试中未定义注入的依赖关系

    我是 Angular 的新手 不太清楚依赖注入到底是如何工作的 我的问题是我的服务 A 依赖于服务 B 但是当我将服务 A 注入到我的测试中时 服务 B 变得未定义 我见过在对 AngularJS 服务进行单元测试时注入依赖服务 https
  • 了解模数运算符 %

    我根据以下表达式理解模运算符 7 5 这将返回 2 因为 5 会变成 7 一次 然后给出剩下的 2 但是当您反转此语句以阅读时 我会感到困惑 5 7 这给了我 5 的值 这让我有点困惑 7虽然不能整成5 但有一部分能整成5 为什么不是没有余
  • 如何在 OSX 上自动加载 R 中的设置?如何找到R_HOME、配置Rprofile.site等?

    我有一台 Macintosh 我试图自动加载包 自制函数 并在每次启动 R 时使用修改后的设置 我相信这可以通过名为 Rprofile site 的文件并创建函数 First 和 Last 来完成在该文件中 一个问题是 我不知道我的 R H
  • 为什么商店负载屏障被认为是昂贵的?

    大多数 CPU 架构都会重新排序存储加载操作 但我的问题是为什么 我对商店负载屏障的解释如下 x 50 store load barrier y z 此外 与释放和获取语义相比 我不明白这个屏障在无锁编程中有何用处 简答 存储加载屏障可防止
  • 正则表达式:修剪字符串的一部分并返回剩下的内容

    我尝试使用正则表达式来获取字符串 12344dfdfsss isa 中 后面的内容 在本例中我想从字符串中获取 isa 我找到了这些答案 如何使用 RegExp 删除大字符串中的一小部分字符串 https stackoverflow com
  • 离线时如何安装conda环境?

    我想在没有网络连接的机器上创建 conda 环境 到目前为止我所做的是 在连接到互联网的机器上 conda create n python3 python 3 4 anaconda Conda 将所有相关包存档到 Anaconda pkgs
  • ASP.NET 依赖注入 HTTP 模块(MS 企业库)

    我一直按照 Microsoft Enterprise Library 5 0 文档中的步骤创建一个 HTTP 模块 将对 Enterprise Library 容器的引用注入到 ASP NET Web 应用程序的页面中 它包含以下代码 也出
  • .Net 中(对称)加密的最佳实践?

    什么是加密 SQL 数据库中某些敏感或个人身份数据的 最佳实践 根据 PCI HIPAA 或其他适用的合规标准 这里有很多关于解决方案各个方面的问题 但我还没有看到任何在高层讨论该方法的问题 经过一段时间的观察 我得出以下结论 使用 Cry
  • SwiftUI 添加反转蒙版

    我正在尝试向两个形状添加蒙版 以便第二个形状遮盖第一个形状 如果我做类似的事情Circle mask Circle offset 这会产生相反的效果 防止第一个圆之外的任何内容可见 For UIView答案在这里 iOS 在drawRect
  • 应该使用 NIB 或 iPhone 中的代码创建视图吗?

    使用 Interface Builder 设计视图时有性能 开发缺点或优点吗 通常您想使用 Interface Builder 您希望通过编程接口执行此操作有几个原因 它是更被接受的创建用户界面的方式 因为它的简单性和视觉优势是您无法通过简
  • 逗号分隔文本的正则表达式

    我创建一个文本字段来添加用逗号分隔的标签 例如 php jquery js ruby on Rails 该字段类似于 stakoverflow 上的字段 您可以在其中为帖子添加标签 我想验证输入以确保标签输入正确 这意味着用户只能输入字母
  • 如何以编程方式确定 C++ 中的表达式是右值还是左值?

    在 C 中确定表达式是右值还是左值的最佳方法是什么 也许 这在实践中没有用 但由于我正在学习右值和左值 我认为有一个函数会很好is lvalue如果传入输入的表达式是左值 则返回 true 否则返回 false Example std st
  • 在 ASP.NET MVC3 中使用 Razor 进行文件上传控件

    有没有办法在 ASP NET MVC3 中使用 Razor 帮助器定义文件上传控件 没有用于文件输入的 html 帮助器 但是这样做有什么问题 using Html BeginForm Action Controller FormMetho
  • 如何 close() 和 quit() Selenium 驱动程序而不影响 Cucumber 和 Selenium 中的其他步骤?

    我有两个功能文件Cucumber链接到相应的步骤文件 问题是 当其中一个步骤文件完成执行时 它会关闭所有浏览器窗口 因为driver quit 从而终止尚未完成处理的其他步骤文件的执行 这里每个步骤文件都会打开一个新的浏览器窗口 在其中执行
  • PDO 准备好的语句足以防止 SQL 注入吗?

    假设我有这样的代码 dbh new PDO blahblah stmt dbh gt prepare SELECT FROM users where username username stmt gt execute array usern