PDO与SQL注入

1. PDO前置知识

  1. PDO预编译。 我们知道,在进行SQL查询时,mysql服务器会对传入的SQL语句进行分析、编译以及优化,这个过程会花费一定的时间,如果同一种类型的查询语句,多次查询,没有存储过程,就会导致每次都经历分析、编译以及优化这个过程,拖慢SQL查询的速度,而PDO预编译就是类似于一个这样的储存过程,从而加快SQL的执行。
  2. PDO预编译分类。 PDO预编译分为本地PHP模拟预编译和MYSQL服务端预编译。其实现通过是否设置:$pdo->$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);来决定是否用php本地预编译,如果设置,则不启用php本地预编译,采用mysql服务端预编译。
  3. PDO防SQL注入原理 PDO通过$pre = $pdo->prepare("select * from mysql.user where host = :host");来实现对SQL语句进行一个模板化的编译,其底层原理也是类似于mysql_real_escape_string(),对特殊字符进行转义,但用了PDO也不是百分之百防得住SQL注入攻击。

2. 在PDO中执行SQL注入

  1. 没有使用PDO::prepare()函数导致SQL注入
  2. 使用了预编译但是在预编译之前对参数进行了污染
  3. 正确使用PDO,但php本地预编译导致的宽字节注入
    使用pdo,并将本地预编译编码设置为utf8或者不设置

    可以看到,单引号仍然逃逸了(由于编码问题,我们看不出来),分析一下原因:首先因为$pdo->query('SET NAMES GBK'); 导致客户端、连接层、结果集都被设置为GBK的解码和编码方式,继续看一下我们的payload:
    $host = "1234".chr(0xb3) . chr(0x27)." or 1=1-- -";
    1.chr(0xb3).chr(0x27)or 1=1 -- -
    经过url解码:
    2.变为0xb327or 1=1 -- -,继续经过PDO的本地预编译:
    3.变为0xb35c27or 1=1 -- -而0xb35c在gbk编码中是:砛,导致了\(0x5c)被吃掉,进而单引号溢出

3. 正确的使用PDO的姿势

  1. 仍然使用PDO本地预编译,但我们需要将PDO本地预编译的字符集指定为与这里设置的编码相同的方式SET NAMES GBK:
    $pdo = new PDO("mysql:host=127.0.0.1;dbname=wordpress_5.0.1;charset=gbk", "root", "youncyb");

    可以看到,这次单引号并没有成功逃逸,究其原因是charset=gbk导致PDO本地预编译考虑到了gbk字符集,不会将0xb35c视作一个整体,说到这,我们知道,防止宽字节是通过mysql_set_charset()和mysql_real_escape_string()同时使用,这两者其实有异曲同工之妙。
  2. 禁止PDO本地预编译,全权交由MYSQL服务器

4. 参考

  1. https://mp.weixin.qq.com/s?__biz=MzI5MDQ2NjExOQ==&mid=2247487247&idx=1&sn=be66d8a8ff1539940fa8985a913ac6b9&chksm=ec1e3f27db69b631ce3fd4907cc774670446d3773358ce143804b0771afb29c3c67059124780&scene=21&ascene=7&devicetype=android-26&version=2607033d&nettype=ctnet&abtest_cookie=BAABAAoACwASABMABQAjlx4AVpkeAMKZHgDamR4A3JkeAAAA&lang=zh_CN&pass_ticket=RxXBTgGK3e1ybaTmm4R0phav%2FLMWVho9gOmD5bmuPrTswdH%2FtmLJ5pQW1AZduhk2&wx_header=1
  2. https://www.cnblogs.com/leezhxing/p/5282437.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注