详见 这里

Let's say I have code like this:

$dbh = new PDO("blahblah");

$stmt= $dbh->prepare('SELECT * FROM users where username = :username');

$stmt->execute( array(':username' => $_REQUEST['username']) );

The PDO documentation says:

The parameters to prepared statements don't need to be quoted; the driver handles it for you.

Is that truly all I need to do to avoid SQL injections? Is it really that easy?

You can assume MySQL if it makes a difference. Also, I'm really only curious about the use of prepared statements against SQL injection. In this context, I don't care about XSS or other possible vulnerabilities.

The short answer is NO, PDO prepares will not defend you from all possible SQL-Injection attacks. For certain obscure edge-cases.

I'm adapting this answer to talk about PDO...

The long answer isn't so easy. It's based off an attack demonstrated here.

The Attack

So, let's start off by showing the attack...

$pdo->query('SET NAMES gbk');

$var = "\xbf\x27 OR 1=1 /*";

$query= 'SELECT * FROM test WHERE name = ? LIMIT 1';

$stmt= $pdo->prepare($query);

$stmt->execute(array($var));

In certain circumstances, that will return more than 1 row. Let's dissect what's going on here:

1 Selecting a Character Set

$pdo->query('SET NAMES gbk');

For this attack to work, we need the encoding that the server's expecting on the connection both to encode ' as in ASCII i.e. 0x27 and to have some character whose final byte is an ASCII \ i.e. 0x5c. As it turns out, there are 5 such encodings supported in MySQL 5.6 by default: big5, cp932, gb2312, gbk and sjis. We'll select gbk here.

Now, it's very important to note the use of SET NAMES here. This sets the character set ON THE SERVER. There is another way of doing it, but we'll get there soon enough.

2.The Payload

The payload we're going to use for this injection starts with the byte sequence 0xbf27. In gbk, that's an invalid multibyte character; in latin1, it's the string ¿'. Note that in latin1 and gbk, 0x27 on its own is a literal ' character.

We have chosen this payload because, if we called addslashes() on it, we'd insert an ASCII \ i.e. 0x5c, before the ' character. So we'd wind up with 0xbf5c27, which in gbk is a two character sequence: 0xbf5c followed by 0x27. Or in other words, a valid character followed by an unescaped '. But we're not using addslashes(). So on to the next step...

3. stmt->execute()

The important thing to realize here is that PDO by default does NOT do true prepared statements. It emulates them (for MySQL). Therefore, PDO internally builds the query string, calling mysql_real_escape_string() (the MySQL C API function) on each bound string value.

The C API call to mysql_real_escape_string() differs from addslashes() in that it knows the connection character set. So it can perform the escaping properly for the character set that the server is expecting. However, up to this point, the client thinks that we're still using latin1 for the connection, because we never told it otherwise. We did tell the server we're using gbk, but the client still thinks it's latin1.

client使用latin1, server 使用 gbk, 不一样,轮为addslash一样的效果

纵观以上两种触发漏洞的关键是addslashes()、mysql_real_escape_string()在Mysql配置为GBK时就可以触发漏洞,

另外:mysql_real_escape_string在执行前,必须正确连接到Mysql才有效。

Therefore the call to mysql_real_escape_string() inserts the backslash, and we have a free hanging ' character in our "escaped" content! In fact, if we were to look at $var in the gbkcharacter set, we'd see:

縗'OR 1=1 /*

Which is exactly what the attack requires.

4 The Query

This part is just a formality, but here's the rendered query:

SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1

Congratulations, you just successfully attacked a program using PDO Prepared Statements...

The Saving Grace

As we said at the outset, for this attack to work the database connection must be encoded using a vulnerable character set. utf8mb4 is not vulnerable and yet can support every Unicode character: so you could elect to use that instead—but it has only been available since MySQL 5.5.3. An alternative is utf8, which is also not vulnerable and can support the whole of the Unicode Basic Multilingual Plane.

Alternatively, you can enable the NO_BACKSLASH_ESCAPES SQL mode, which (amongst other things) alters the operation of mysql_real_escape_string(). With this mode enabled, 0x27 will be replaced with 0x2727 rather than 0x5c27 and thus the escaping process cannot create valid characters in any of the vulnerable encodings where they did not exist previously (i.e. 0xbf27 is still 0xbf27 etc.)—so the server will still reject the string as invalid. However, see @eggyal's answer for a different vulnerability that can arise from using this SQL mode (albeit not with PDO).

Safe Examples

The following examples are safe:

Because the server's expecting utf8...

mysql_query('SET NAMES utf8');

$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");

mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Because we've properly set the character set so the client and the server match.

mysql_set_charset('gbk');

$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");

mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Because we've turned off emulated prepared statements.

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

$pdo->query('SET NAMES gbk');

$stmt= $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');

$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Because we've set the character set properly.

$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);

$stmt= $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');

$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Because MySQLi does true prepared statements all the time.

$mysqli->query('SET NAMES gbk');

$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');

$param = "\xbf\x27 OR 1=1 /*";

$stmt->bind_param('s', $param);

$stmt->execute();

Wrapping Up

If you:

Use Modern Versions of MySQL (late 5.1, all 5.5, 5.6, etc) AND PDO's DSN charset parameter (in PHP ≥ 5.3.6)

OR

Don't use a vulnerable character set for connection encoding (you only use utf8 / latin1 / ascii / etc)

OR

Enable NO_BACKSLASH_ESCAPES SQL mode

You're 100% safe.

Otherwise, you're vulnerable even though you're using PDO Prepared Statements...

mysql pdo prepare_php pdo prepare真的安全吗相关推荐

  1. pdo mysql fetchall_pdo fetch,pdo fetchAll,pdo query,pdo prepare用法举例

    通过pdo来从数据库中调取数据. 其中主要涉及到 pdo fetch,pdo fetchAll,pdo query,pdo prepare使用 . 可以通过以下几种方式,学习下php pdo的用法. ...

  2. php pdo mysql 预处理_PHP PDO mysql 处理类(prepare 预处理)

    使用预处理,动态处理需要操作的字段和值.先使用PDO来动态绑定参数.PDOModel: /** * Description of PDO * * @author Administrator */ cl ...

  3. php mysql 替换 pdo,php操作MySQL数据库之PDO方式

    这篇文章主要介绍了关于php操作MySQL数据库之PDO方式 ,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下基本使用 1)连接数据库$pdo = new PDO("mysql ...

  4. mysql php pdo_php PDO mysql写法

    php PDO写法连接mysql: 写法一: $db="mysql:host=localhost;dbname=sql" : //连接数据,地址localhost:数据库名称sql ...

  5. mysql pdo教程_php中mysql连接方式PDO使用详解

    本文主要详细介绍了PHP中mysql的连接方式PDO的各种使用方法,算是个人的一个小结,如有遗漏还请告之,有需要的小伙伴来参考下吧. PDO常用方法: PDO::query()主要用于有记录结果返回的 ...

  6. php pdo attr_emulate_prepares,PDO操作,设置了ATTR_EMULATE_PREPARES属性为false后发现还是在本地模拟prepare...

    PDO操作,设置了ATTR_EMULATE_PREPARES属性为false后发现还是在本地模拟prepare //连接数据库部分 try{ $dbh = new PDO('mysql:dbname= ...

  7. PDO及PDO - prepare的简单使用

    <?php $host = 'localhost'; $user = 'root'; $pwd = '1234'; // sakila数据库是安装mysql时, 系统自带的一个示例数据库 $db ...

  8. mysql敤 罚_php中mysql连接方式PDO使用详解,pdo浣跨敤璇﹁В_PHP教程

    php中mysql连接方式PDO使用详解,pdo浣跨敤璇﹁В PDO常用方法: PDO::query()主要用于有记录结果返回的操作(PDOStatement),特别是select操作. PDO::e ...

  9. mysql敤 罚_php中mysql连接方式PDO使用详解,pdo浣跨敤璇﹁В

    php中mysql连接方式PDO使用详解,pdo浣跨敤璇﹁В PDO常用方法: PDO::query()主要用于有记录结果返回的操作(PDOStatement),特别是select操作. PDO::e ...

最新文章

  1. am.java_6.3.1 从am说起
  2. 启动Jupyter Notebook时出现Kernel error错误的解决方法
  3. 服务器一般在什么位置,云服务器比较普通服务器差异在哪里
  4. summernote 不能上传图片_Typora自定义命令自动上传图片
  5. WPF and Silverlight 学习笔记(二十二):使用代码实现绑定、绑定数据的验证
  6. apt搜索某个软件及版本
  7. Mat拜耳数据邻域转换算法返回Mat对象,16bit三通道
  8. centos php ioncube_如何在CentOS 7云服务器上安装ionCube?
  9. 安装GNOME3桌面并设置开机启动图形界面
  10. cz.cc免费域名申请教程(因为有朋友不知道怎么操作,特写此简单教程)
  11. 免费滴永恒G,试试无妨
  12. 墨者 SQL手工注入漏洞测试(MySQL数据库)
  13. NLPCC2021.10.14
  14. Android Studio连接手机设备教程(完全步骤)
  15. android 启动app过程,应用程序进程启动过程
  16. Qt之QTextEdit
  17. 高考数学题目:导数及其简单应用
  18. 2021年起重机司机(限桥式起重机)考试内容及起重机司机(限桥式起重机)最新解析
  19. 数据库系列(5):SQL查询
  20. 科达陆吉良: 行业“管多控少”,交通AI化是大势所趋

热门文章

  1. Java中的秒表-StopWatch
  2. signature=de15a75f36f4ab4d3249c21fbaa05791,1-out-of-n Signatures from a Variety of Keys
  3. linux中dns超时时间,Linux DNS timeout, attempts.---DNS超时,重试的配置
  4. JDK1.5提供的原子类原理及使用
  5. spring源码之—Assert.notNull()
  6. GDB中创建要素数据集
  7. 使用Python编写简单网络爬虫抓取视频下载资源
  8. web.config forms节点中的属性的含义和用途
  9. windows进入后自动注销处理
  10. 1-1-Html技术