php pdo prepare真的安全吗
详见 这里
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 = /*' 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_ESCAPESSQL mode
You're 100% safe.
Otherwise, you're vulnerable even though you're using PDO Prepared Statements...
php pdo prepare真的安全吗的更多相关文章
- PHP PDO prepare()、execute()和bindParam()方法详解
每次将查询发送给MySQL服务器时,都必须解析该查询的语法,确保结构正确并能够执行.这是这个过程中必要的步骤,但也确实带来了一些开销.做一次是必要的,但如果反复地执行相同的查询,批量插入多行并只改变列 ...
- PHP系列 | PDO::prepare(): send of 68 bytes failed with errno=32 Broken pipe
设计场景 1.开启Redis的键空间过期事件(键过期发布任务),创建订单创建一个过期的key,按照订单号为key,设置过期时间. 2.通过Redis的订阅模式(持久阻塞),获取到订单号进行组装. 3. ...
- PDO::prepare
PDO::prepare — 准备要执行的SQL语句并返回一个 PDOStatement 对象(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0) 说明 语法 publi ...
- PDO prepare预处理语句
预处理语句 $dsn="mysql:host=localhost;dbname=emp"; try{ $pdo=new PDO($dsn,'root','root'); }catc ...
- php prepare
详见 https://stackoverflow.com/questions/134099/are-pdo-prepared-statements-sufficient-to-prevent-sql- ...
- MySQL 、PDO对象
目录 1, singleton 2, pdo与db 3, singleton获取pdo 4, pdo实现db增删改查 5, pdo异常处理exception 6, pdo预处理prepare 7, p ...
- 前端学PHP之PDO预处理语句
× 目录 [1]定义 [2]准备语句 [3]绑定参数[4]执行查询[5]获取数据[6]大数据对象 前面的话 本来要把预处理语句和前面的基础操作写成一篇的.但是,由于博客园的限制,可能是因为长度超出,保 ...
- PHP中关于PDO的使用
执行没有结果集的查询 执行INSERT,UPDATE,DELETE的时候,不返回结果集.这个时候可以是有exec(),exec()将返回查询所影响的行数 int PDO::exec ( string ...
- php的mysql\mysqli\PDO(三)PDO
原文链接:http://www.orlion.ga/1153/ PDO是一种数据库抽象层,不止可以访问mysql还可以访问其他数据库. 一.__construct() PDO::__construct ...
随机推荐
- 一个合约访问另一个合约中的mapping
参考链接:https://ethereum.stackexchange.com/questions/13616/accessing-a-public-mapping-within-a-contract ...
- 我们为什么要在 PHPStorm 中标记目录
问题来源 (1)要开发的项目位于PHPStorm打开的项目的二级目录下,使用PHPStorm来开发Laravel项目 提供的教程在代码自动定位和智能提醒方面,存在无效的情况: (2)使用gulp作为项 ...
- C语言条件编译及编译预处理阶段(转)
一.C语言由源代码生成的各阶段如下: C源程序->编译预处理->编译->优化程序->汇编程序->链接程序->可执行文件 其中 编译预处理阶段,读取c源程序,对其中的 ...
- kubernetes多节点的pod挂载同一个cephfs目录
一.安装cephfs 方法一: 直接进入deploy目录,执行: ceph-deploy --overwrite-conf mds create ceph01:mds-daemon- 上面的ceph0 ...
- “undefined JNI_GetCreatedJavaVM”和“File format not recognized”错误原因分析
如果编译时,报如下所示错误:../../third-party/hadoop/lib/libhdfs.a(jni_helper.c.o): In function `getGlobalJNIEnv': ...
- 日志文件(关于#IRSA_MDPS_RDM软件 密码登录事项 7月26号)
1.登录:sqlplus 用户名:scott 口令:123 qweas.. //2018-7-16号更改密码 2.查看该用户(已登录)下有几个表:select table_name from user ...
- handsontable-developer guide-cell editor
单元格编辑 cell editor renderer:展示数据:editor:改变数据:renderer用一个函数表示:后者有一系列的操作,需要用class来表示: EditorManager han ...
- .NET 匿名方法的BUG,请专家解答
匿名方法是.NET 3.5之后的一个好东东,很多人使用,但是我在最近的工作当中发现了一个问题. 请专家解答 //list里存放10个数字 List<); ; i < ; i++) { li ...
- ASP.NET MVC验证DateTime的问题
今天碰到一个Bug,在IE8中,日期验证失效,输入正确的日期格式也会验证失败,提示:xxx必须是日期格式(the field xxx must be a date) 最终找到问题所在:jquery.v ...
- [Elixir005] 查看指定数据的详细信息 i helper
elixir在1.2后增加了一个新的特性i helper. 在iex shell中使用i可以查看任意数据的数据类型和详细描述 #查看变量描述 iex(1)> i {:test, "Th ...