パスワードを忘れた? アカウント作成
873125 journal
日記

beroの日記: SQLインジェクション対策:偽プリペアドステートメントに注意 4

日記 by bero

「SQLインジェクション対策」でGoogle検索して上位15記事を検証した

まとめに

SQLインジェクションについて書くときに以下のメッセージを必ず含めて欲しいです。
-単にプリペアドステートメントを使え
-絶対に文字列結合でSQLを構築しようとしてはいけない
-IPAの「安全なSQLの呼び出し方」を読むこと

とあるが、
- 本物のプリペアドステートメントであることを確かめる
を付け加えたい。(まあ『IPAの「安全なSQLの呼び出し方」を読むこと』を守れば、そこに書いていることではあるが)

IPA本では本物のプリペアドステートメントを「静的プレースホルダ」偽物(エミュレーション)を「動的プレースホルダ」と呼んでいるが、
後者はプリペアドステートメントぽい記述をライブラリ内で文字列結合してSQLを構築している。

偽プリペアドステートメントも無意味ではない。
少なくとも自前で文字列連結するのに比べ、単純なバグやエスケープ忘れは無くなる(ことが期待できる)。
しかし文字コードの違いや不正文字によるエスケープ抜けは起こりうるので、脆弱性が残りうる。

IPA本では、Perl + MySQL (DBD::MySQL)、Java + MySQL(MySQL Connector/J)において、デフォルトでは動的プレースホルダなので、静的プレースホルダを使うようにパラメータを指定する必要がある旨書いている。

同様にPHP + MySQL (pdo_mysql) もデフォルトでは動的プレースホルダなので(バージョンによるかも)、パラメータを指定する必要がある
  $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,FALSE)

PDO::prepare

PDO は元々この機能をサポートしていないドライバに対して プリペアドステートメントとバインドパラメータをエミュレートします。 このため、ある形式をサポートしているがその他の形式をサポートしていない ドライバの場合、名前もしくは疑問符形式のパラメータを他の適当な値に 書き換えることも可能です。

PDO::setAttribute

PDO::ATTR_EMULATE_PREPARES プリペアドステートメントのエミュレーションを有効または無効にする。 ドライバによってはネイティブのプリペアドステートメントをサポートしていなかったり 完全には対応していなかったりするものがある。この設定を使うと、常に プリペアドステートメントをエミュレートする (TRUE の場合) か、 ネイティブのプリペアドステートメントを使おうとする (FALSE の場合) かを設定できる。現在のクエリを正しく準備できなかった場合は、常にエミュレート方式を使う。

と書いてて気づいたが、恐ろしいことが書いてある。
現在のクエリを正しく準備できなかった場合は、常にエミュレート方式を使う。
いらんことすんなや

pdo_mysql:

        if (mysql_stmt_prepare(S->stmt, sql, sql_len)) {
                /* TODO: might need to pull statement specific info here? */
                /* if the query isn't supported by the protocol, fallback to emulation */
                if (mysql_errno(H->server) == 1295) {
                        if (nsql) {
                                efree(nsql);
                        }
                        goto fallback;
                }

fallbackすんなや

pdo_sqlite:

        i = sqlite3_prepare(H->db, sql, sql_len, &S->stmt, &tail);
        if (i == SQLITE_OK) {
                return 1;
        }

        pdo_sqlite_error(dbh);

        return 0;

pdo_pgsql:
        PDO_ATTR_CURSORがPDO_CURSOR_SCROLLだったらエミュレーション。
        あとコンパイルオプションとかプロトコルが2以下(PostgreSQL 7.4未満)でも。
        ネイティブprepareに失敗したらエラー。

sqliteとpsgqlはドライバレイヤではエラーを返しているが、上位で(マニュアル通り)fallbackしてるかどうかは知らね。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by bero (5057) on 2011年11月09日 19時53分 (#2047664) 日記

    とはいえ、今時は何らかのフレームワークを使ってるだろうから、フレームワーク->ORM->DBライブラリと追っかけて「本物のプリペアドステートメントであることを確かめる」のは結構大変な気がする。

    ---
    ところで「絶対に文字列結合でSQLを構築しようとしてはいけない」件であるが、
    入力の個数が不定な場合がある(特にIN句)。

    select ... where .... in (?,?,?..?)

    やむを得ずB部分を文字列結合で生成してprepare -> bindしてるが、「絶対に文字列結合を使わない」良い方法があるだろうか?

    • by bero (5057) on 2011年11月10日 14時10分 (#2048148) 日記

      しまった表示されてない

      select ... where .... in (?,?,?..?)
      |-------A(固定)----------|-B(生成)-|

      親コメント
    • by Anonymous Coward

      PHPで、ですが…。

      $args = array('a', 'b', 'c', ...);
      $sql = 'SELECT ... WHERE ... IN (' . join(', ', array_fill(0, count($args), '?')) . ')';

      なんてのはどうでしょう?

typodupeerror

吾輩はリファレンスである。名前はまだ無い -- perlの中の人

読み込み中...