【问题标题】:Why does this MySQLI prepared statement allow SQL injection?为什么这个 MySQLI 准备好的语句允许 SQL 注入?
【发布时间】:2013-11-19 17:47:21
【问题描述】:

今天在教学生如何防止 SQL 注入时,我有点尴尬。在专业项目中,我使用准备好的语句/参数化查询作为防止 SQL 注入的一层(尽管我从未专业地使用过 mySQL)。理论上,我认为在使用准备好的语句时,SQL 注入是不可能的。

但后来这奏效了……

$Search = $_GET['s'];
$stmt = $mysqli->prepare("SELECT * FROM products WHERE id = ?");
$stmt->bind_param("i", $Search);
$stmt->execute();
$Results = $stmt->get_result();

如果我传递参数“?s=1 OR 1=1”,那么我可以获得所有产品的完整列表。我仍然无法在最后插入另一个查询,但我很困惑为什么这种类型的基本 SQL 注入在 mysql/php 中是可能的。我假设参数“1 OR 1=1”会被拒绝,因为它不是数字(显然我可以运行该数字检查......)。

谁能解释一下为什么会这样,以及我对 php/mysql 中的准备好的语句有什么不明白的地方?

顺便说一句,我的学校计算机有一个开箱即用的 Zend WAMP 安装。

编辑:这是导致我困惑的字符串值:

$Search = "1 OR 1=1";

【问题讨论】:

  • 两件事: 1 - 列的类型是什么:int 或 varchar? 2 - 如果您在 binparam 语句中指定类型会发生什么:$stmt->bindParam(':id',$Search, PDO::PARAM_INT)。那应该将变量转换为整数
  • @stUrb 那是 PDO。这不是。此外,列的类型应该是无关紧要的。
  • 您能否使用$Search 的特定固定值(例如字符串)重现该案例并更新代码?
  • (另外,包括返回的行样本)
  • 哎呀,确实是它的 mysqli 而不是 PDO。我的错。

标签: php sql mysqli prepared-statement code-injection


【解决方案1】:

没关系,这里没有任何SQL注入,你看不到所有产品的列表(至少你提供的代码不能显示)

您不需要转换为 int,让我们更深入地了解 bind_param 的实际作用:

它有字符串“i”,表示 1 个integer 参数

您从 $_GET 传递值,即string

bind_param 尝试将 String 转换为 int,因此请检查以下 php 代码:

echo intval('a', 10); // output 0
echo intval('1a', 10); // output 1
echo intval('12a', 10); // output 12
echo intval('b1', 10); // output 0
echo intval('1 or 1=1', 10); // output 1
echo intval("?s=1 OR 1=1", 10); // output 0

那么,您输出 id=1 的产品,也许您有一些?

【讨论】:

  • 我认为OP的意思是参数是1 OR 1=1,就像http://myinsecuresite.com/test.php?s=1 OR 1=1一样
  • 似乎是一个很好的解释:可能不是一个独特的 ID。
  • @EdCottrell 是的,这就是 OP 的意思,1 or 1=1 仅转换为 1
  • @IlyaBursov:完全正确。不过,我看到您已经调整了答案,从而解决了我的问题。
  • 哈哈,松散类型的编程就这么多
【解决方案2】:

我试图重现你的情况,但没有成功。

数据库

CREATE TABLE `Document` (
  `DataID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `Description` varchar(50) CHARACTER SET utf8 NOT NULL,
  PRIMARY KEY (`DataID`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

INSERT INTO `Document` VALUES (1,'BI8392');
INSERT INTO `Document` VALUES (2,'HE8492');
INSERT INTO `Document` VALUES (3,'HE8493');
INSERT INTO `Document` VALUES (4,'HE8490');

代码

<?php
$host = "localhost";
$passwd = "";
$user = "user";
$bdd = "test";

$conn = mysqli_connect( $host, $user, $passwd, $bdd);
mysqli_set_charset($conn, "utf8");

if (!$conn) {
   printf('Error connecting to mysql. Error code: ', mysqli_connect_error());
   exit;
}

$sql = "SELECT * FROM Document WHERE DataID = ?";
if ($stmt = $conn->prepare($sql)) {

    $param = "2 OR 1=1";
    $stmt->bind_param("i", $param);
    $stmt->execute();
    $results = $stmt->get_result();
    $data = mysqli_fetch_all($results);

    var_dump($data);
}

输出

array(1) {
  [0]=>
  array(2) {
    [0]=>
    int(2)
    [1]=>
    string(6) "HE8492"
  }
}

看来,1=1 条件被忽略了。

【讨论】:

  • @PeeHaa 抱歉 2 个帖子 - 浏览器故障
  • 你是对的。我快速创建的课堂脚本中有一个错字。谢谢。
猜你喜欢
  • 2011-07-09
  • 1970-01-01
  • 1970-01-01
  • 2015-12-07
  • 2014-01-28
  • 2016-03-02
  • 2012-12-01
  • 2014-10-14
  • 2012-06-28
相关资源
最近更新 更多