PDO预处理防御SQL注入

PDO可以被认作是一种通过编译SQL语句模板来运行SQL语句的机制。

  • 查询仅需解析(或预处理)一次,但可以用相同或不同的参数执行多次。当查询准备好后,数据库将分析、编译和优化执行该查询的计划。对于复杂的查询,此过程要花费较长的时间,如果需要以不同参数多次重复相同的查询,那么该过程将大大降低应用程序的速度。通过使用预处理语句,可以避免重复分析/编译/优化周期。简言之,预处理语句占用更少的资源,因而运行得更快。
  • 提供给预处理语句的参数不需要用引号括起来,驱动程序会自动处理。如果应用程序只使用预处理语句,可以确保不会发生SQL注入。(然而,如果查询的其他部分是由未转义的输入来构建,则仍存在SQL注入的风险)。

示例1,如下为一个简易的PDO预处理查询环境

<link.php>

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


$link =new PDO("mysql:host=$servername;dbname=$database",$username,$password);
$stmt=$link->prepare('select * from users where id=?');
$stmt->execute([$_GET['id']]);
foreach ($stmt as $item) {
    echo $item['id'].'&nbsp';
    echo $item['name'].'&nbsp';
    echo $item['sex'].'&nbsp';
    echo $item['passwd'].'&nbsp';

}

通过一个简易html页面提交id参数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>pdo-test</title>
</head>
<body>
<form action="link.php" method="get">
    <input name="id" type="text">
    <input name="submit" type="submit">

</form>

</body>
</html>

测试数据表users

 提交敏感字符进行查询并查看日志记录,发现对 ' " 进行了转义处理,这就有点类似于addslashes()和mysql_real_escape_string()

 那可能会想到是否可以进行宽字节注入,使用payload:%df ' or 1=1#一试便知

 答案是当然不可以。宽字节注入需要满足两个条件:1、数据库连接使用宽字符集。2、使用转义函数来过滤输入的敏感字符。

这就涉及到PDO的转义机制

  • 本地转义,使用单字节字符集(PHP<5.3.6)来对输入进行转义,但这种方式存在安全隐患。在PHP版本小于5.3.6时,本地转义只能转换单字节的字符集,大于5.3.6的版本会根据PDO连接中指定的字符集进行转义。
  • mysql服务端转义,首先将SQL语句模板发送到mysql server,而后再讲绑定的变量发送给mysql server,这里的转义在mysql server中完成,根据PDO连接中指定的字符集进行转义。这样的转义方式更健全,同时还可以在有多次重复查询的业务场景下,通过复用模板来提高程序的性能。如果使用此转义机制需要添加参数:$PDO->setAttribute(PDO::ATTR_EMULATE_PREPARES,false),此参数默认情况下为true。如果不修改该参数,PDO会将输入的参数使用本地转义后和sql语句模板拼接并发送至mysql server。

示例1为本地转义机制,示例2如下添加PDO::ATTR_EMULATE_PREPARES参数后看看效果:

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

$link =new PDO("mysql:host=$servername;dbname=$database",$username,$password);

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

$stmt=$link->prepare('select * from users where id=?');
$stmt->execute([$_GET['id']]);
foreach ($stmt as $item) {
    echo $item['id'].'&nbsp';
    echo $item['name'].'&nbsp';
    echo $item['sex'].'&nbsp';
    echo $item['passwd'].'&nbsp';

}

执行刚才的payload后查看日志,比刚才多出了一条预处理语句,原因是数据库先对预处理语句模板进行了解析,再将绑定参数发送给服务端执行。

 本文只做参考,如有不当之处还请师傅们指正,要想更加深入理解PDO还请查阅PHP官方文档。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值