当我今天教学生如何防止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";
没
关系,这里没有任何类型的SQL注入,你看不到所有产品的列表(至少你提供的代码不能显示它)
你不需要投射到 int,让我们更深入地了解bind_param实际做什么:
它有字符串"i",表示 1 integer
参数
您传递的价值来自 _GET 美元,这是string
bind_param尝试将字符串转换为 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,也许你有一些?
我试图重现您的情况,但无法重现。
数据库
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 条件被忽略了。