【问题标题】:Hack prepare statement (read first)Hack prepare 语句(先读)
【发布时间】:2013-09-08 17:54:33
【问题描述】:

我有这个代码:

<?php
$table = $_GET ["table"];
$query = "SELECT 1 FROM $table";
$st = $pdo->prepare($query);
$st->execute();

这不是真正的代码,但它是一个例子。

如果我做:

hacked.php?table=users;DROP TABLE users;

它会起作用,因为它没有正确转义。

但是,如果我想更新这样的信息:

hacked.php?table=users; UPDATE users SET name="abc" WHERE name="def";

它不起作用,因为它被转义了,pdo会将查询转换为

SELECT 1 FROM users; UPDATE users SET name=\"abc\" WHERE name=\"def\";

显然它失败了。

有没有办法让这个查询有效?

编辑 1

我们团队中有一个人专门检查我的代码并破解它。所以我想做好准备,如果这可以以某种方式完成。

编辑 2

我已经读过这个:Are PDO prepared statements sufficient to prevent SQL injection? 但它确实没有回答我的问题。然而,它给了我一个通过的方法。 @duskwuff 的解决方案与我的解决方案相同。因此,对于管理员来说,如果应该将其删除或标记为重复是可以的。但我坚持认为,这有助于人们了解 pdo prepare 是如何被黑客入侵的。

【问题讨论】:

  • prepare 不会使您的代码安全。如果您使用外部数据构建了 SQL 命令,那么您将处于危险之中。
  • 不需要一个“敬业的团队成员”就可以说解决方案很糟糕。即使它是您自己的代码,从 GET 中获得表名也是非常可耻的。意味着 数据库结构首先存在缺陷,除了那种实际上没有使用准备好的语句的方法。仍然帮助破解网站管理员是一种耻辱。
  • 关于“敬业的团队成员”的故事仍然是廉价的谈话。为了证明这段代码可以被黑,你已经有一个工作的 DROP TABLE 就足够了。
  • php.net/manual/en/mysqli.quickstart.prepared-statements.php 这样做就安全了,你没有绑定参数...

标签: php mysql pdo sql-injection


【解决方案1】:

它不起作用,因为它被转义了,pdo会将查询转换为

SELECT 1 FROM users; UPDATE users SET name=\"abc\" WHERE name=\"def\";

这是不正确的! PDO 不会对插入查询的文本执行转义,因为它不知道插入了什么文本。您所看到的是 PHP 已弃用的 magic_quotes 功能在请求变量的内容中添加反斜杠(如 $_GET$_POST)的结果。即使启用了此功能,也可以在类似这样的查询中轻松通过使用不带引号的结构来避免它,例如:

SELECT 1 FROM users; UPDATE users SET name = CHAR(97,98,99) WHERE name = CHAR(100,101,102)

CHAR() 是一个 MySQL 函数,它从字符代码值列表中构造一个字符串。如果您使用的是其他数据库,则可能存在等效函数。)

将未转义的内容直接插入到查询中是从不安全的。不要这样做。

【讨论】:

  • 我已将您的答案标记为解决方案,因为这是我发现的。 Char 可用于破解系统。但你是不正确的。我没有使用实际的 GET,请求是通过 post 发出的,并且服务器已删除了 magic_quotes,因为它在较新版本的 PHP 中引发了致命错误,我知道在生产中使用它是非常不安全的。这是因为 pdo 准备了查询。尽管如此,您的解决方案仍然有效:)
  • 唯一会导致您在问题中描述的效果的是magic_quotes。要么它已经启用,要么你的应用程序中的其他东西正在复制它的效果。
【解决方案2】:

我认为你问错了问题。如果您的代码与此非常相似,那么您编写代码的方式就有很大的问题......并且可能与您将需要解决的问题概念化的方式有关,或者您正在从一个非常糟糕的设计开始工作。

如果出于某种原因,您需要在 URL 查询字符串或 http 帖子中传递有关数据库设计的任何信息,并且如果出于某种原因,您认为执行未转义的查询是你需要的方法......然后无论你在做什么,you're doing it wrong

如果,在某个遥远的机会,您确实需要将表格名称传递给网页,那么您至少必须将输入值与某种静态结构进行比较,以查看输入是否值在列表中...然后使用列表中的值,或者来自静态的值,而不是来自输入。

简单地说,像下面这样原始的方法将是一种优越得多的方法,尽管如果表名、列名或任何数据库内部需要进入浏览器领域,则可以说这是一个糟糕的设计。

$table = $_GET ["table"];
IF ($table == "users")
{
   $query = "SELECT 1 FROM users;"
}
ELSEIF ($table == "points")
{
   $query = "SELECT 1 FROM points;"
}  

...

【讨论】:

  • 感谢您的回答。我不是那样做的。但是,我有一个可以像这样使用的系统的一部分......不是完全相同的代码,但只是为了理解我写了那个例子。
  • 所以你真的相信 OP?
猜你喜欢
  • 2013-11-17
  • 2016-10-07
  • 1970-01-01
  • 1970-01-01
  • 2011-01-26
  • 1970-01-01
  • 2011-08-19
  • 2019-11-09
  • 2016-01-17
相关资源
最近更新 更多