Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
76
Help us understand the problem. What is going on with this article?
@stk2k

PDOでATTR_EMULATE_PREPARESを適切に設定してないとSQLインジェクションの原因になるかも(MySQL編)

More than 3 years have passed since last update.

確認環境

MySQL 5.5.27
PHP 5.4.7

現象

PDO::setAttribute( ATTR_EMULATE_PREPARES, false )

をせず、prepareしても静的プレースホルダは使用されない。

確認

<?php

$dsn = 'mysql:dbname=test;host=localhost';
$user = 'root';
$password = '';

try{
    $pdo = new PDO($dsn, $user, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

    $sql = 'select blog_id, blog_name from blogs where blog_name like ?';
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array('%my%'));

}catch (PDOException $e){
    print('Error:'.$e->getMessage());
    die();
}

結果

 MySQLのクエリーログで確認

setAttribute(PDO::ATTR_EMULATE_PREPARES, true)またはデフォルトの場合:

130722 14:40:59 2 Connect root@localhost on test
2 Query select blog_id, blog_name from blogs where blog_name like '%my%'
2 Quit

setAttribute(PDO::ATTR_EMULATE_PREPARES, false)した場合:

130722 14:41:26 3 Connect root@localhost on test
3 Prepare select blog_id, blog_name from blogs where blog_name like ?
3 Execute select blog_id, blog_name from blogs where blog_name like '%my%'
3 Close stmt

3 Quit

まとめ

 SQLインジェクションの対策としてプリペアドステートメントの使用が推奨されますが、PDOにおいてデフォルトではPDO::ATTR_EMULATE_PREPARESがtrueに設定されており、この状態ではprepareでプレースホルダを指定してもエミュレートされ、実際にMySQLにわたる際にはプリペアドステートメントではなくなってしまいます(普通にSQL発行するのと同じ)。この状態では、 自動エスケープはされるものの 、SQLインジェクションの危険があると思います。
(2013.07.22 19:14 訂正: 「追記」を参照してください。)

 PDOで静的プレースホルダを使用する場合(少なくともMySQLでは)、PDO::ATTR_EMULATE_PREPARESを「false」にするべきでしょう。
(え、常識?)

(2013.07.22 18:57 追記)
 ATTR_EMULATE_PREPARESがデフォルトの状態(true)でエミュレーションが効いている場合、ドライバ側で自動エスケープしてくれるみたいです。

 だから安心かというとそうではなくて、文字コード変換を利用した下記攻撃でSQLインジェクションできてしまいました。

 ちなみにATTR_EMULATE_PREPARESをfalseにすると下記プログラムでもTRUNCATEは実行されません(SQLインジェクション対策できている)。

$dsn = 'mysql:dbname=test;host=localhost';
$user = 'root';
$password = '';

try{
    $pdo = new PDO($dsn, $user, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//  $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

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

    $attack = "\x97' OR 1=1; truncate blogs; #";

    $sql = 'select blog_id, blog_name from blogs where blog_id = ?';
    $stmt = $pdo->prepare($sql);
    $stmt->execute(array($attack));

    while($result = $stmt->fetch(PDO::FETCH_ASSOC)){
        print_r($result);
    }

}catch (PDOException $e){
    print('Error:'.$e->getMessage());
    die();
}

76
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
stk2k
福岡在住のPHPerです。 https://github.com/stk2k

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
76
Help us understand the problem. What is going on with this article?