主页 > 网络知识 > 从宽字节注入认识PDO的原理和正确使用

从宽字节注入认识PDO的原理和正确使用

随着数据库参数化查询的方式越来越普遍,SQL注入漏洞较之于以前也大大减少,而PDO作为php中最典型的预编译查询方式,使用越来越广泛。

众所周知,PDO是php中防止SQL注入最好的方式,但并不是100%杜绝SQL注入的方式,关键还要看如何使用。

之前在一篇文章中了解到PDO场景下参数可控导致的多句执行等问题(https://xz.aliyun.com/t/3950)于是对PDO场景下的SQL注入又进行了一些探究。

PDO查询语句可控存在的安全问题:

首先在本地新建一个库和表,随便写点东西。

 

然后写一个test.php,用PDO进行简单的查询:

 

<?php try { $db = new PDO('mysql:host=localhost;dbname=pdotest','root',''); } catch(Exception $e) { echo $e->getMessage(); } if(isset($_GET['id'])) { $id = $_GET['id']; } else { $id=1; } $query = "select balabala from table1 where 1=?"; echo "id:".$id."</br>"; $row = $db->prepare($query); $row->bindParam(1,$id); $row->execute(); $result = $row->fetch(PDO::FETCH_ASSOC); if($result) { echo "结果为:"; print_r($result); echo "</br>"; }

将输入的内容和得到的结果打印在页面上:

 

 

PDO与安全问题相关的主要的设置有下面三个:

PDO::ATTR_EMULATE_PREPARES

PDO::ATTR_ERRMODE

PDO::MYSQL_ATTR_MULTI_STATEMENTS

分别与模拟预编译、报错和多句执行有关。

PDO默认是允许多句执行和模拟预编译的,在之前的很多文章中已经写到,在参数可控的情况下,会导致堆叠注入。

例如我们把查询语句改成:

$query = "select balabala from table1 where 1={$id}"; $row = $db->query($query);

则在$db->query()这一步执行之前,我们便可以对$query进行非法操作,那PDO相当于没用:

 

 

PDO默认设置存在的安全隐患:

如果我们在查询语句中没有可控的参数,并把输入的参数按照prepare->bindParam->execute的方式去写就一定没有问题了吗?

我们按如下语句进行查询:

$query = "select balabala from table1 where 1=?"; $row = $db->prepare($query); $row->bindParam(1,$_GET[‘id’]); $row->execute();

我们在URL中随便输入一个参数:?id=asdasd,然后通过设置SET GLOBAL GENERAL_LOG=ON,从.log里实时监控,看看sql语句到底执行了什么:

 

我们发现模拟预编译的请求发送方式和以往的mysqli并没有什么区别,但我们注意到,在原有的查询语句中对参数并没有用单引号包裹,而在此却用单引号进行了包裹,于是我们可以尝试输入一些特殊字符,比如单引号:

 

 

发现单引号被转义了,这时我们不由得想到如果设置了gbk编码会怎么样:

 

 

 

我们会发现select * from table1成功执行了,尽管PDO只会返回一个结果,但是它的的确确执行了。

也就是说,即使查询语句里没有可控参数,只有?或者:id这类被绑定的参数,依然可以进行堆叠注入。

那如果把多句执行关掉呢?

我们把PDO::MYSQL_ATTR_MULTI_STATEMENTS设为false,重复上述操作:

说点什么吧
  • 全部评论(0
    还没有评论,快来抢沙发吧!