なぜPHPアプリにセキュリティホールが多いのか?

第14回 減らないSQLインジェクション脆弱性

この記事を読むのに必要な時間:およそ 3 分

SQLインジェクション脆弱性を狙った大規模な攻撃が繰り返し行われ,数万から数十万ページが改竄される事件が何度も発生しています。SQLインジェクションは簡単に対策できる脆弱性ですが,未対策のアプリケーションが多く利用されています。外部からの脆弱性の検出も容易であるため,現在でもWebアプリケーション脆弱性の代表的存在です。

SQLインジェクション脆弱性が無くならない理由には以下のようなものが考えられます。

  • 過去のコードやアプリケーションの再利用
  • 基本的なセキュリティ知識不足
  • セキュアコーディングプラクティスの未実施
  • コード監査の不在

SQLインジェクション脆弱性の発見だけを目的にコード監査を行うことはあまりありませんが,SQLインジェクション脆弱性のコード監査は比較的簡単です。MySQLモジュールまたはPostgreSQLモジュールを利用している場合を例に紹介します。

本題に入る前にSQLインジェクションの基礎知識クイズをしてみてください。いくつの質問に自信を持って答えられるでしょうか? すべて自信がある方はこの記事を読まなくても大丈夫な方です。

SQLインジェクションクイズ

SQLインジェクションに対する知識をクイズとしてお聞きます。○か×かで答え,その理由も考えてください。回答はできるだけ厳格に考えてください。特定の環境や設定など条件が限定的でも構いません。答えは次回に掲載します。

  1. SQLインジェクションはエスケープ処理を確実にしていれば大丈夫?
  2. プリペアードクエリを利用していれば大丈夫?
  3. SQLインジェクションはデータベース構造を知らないと攻撃が難しい?
  4. SQLインジェクションはWebアプリケーションファイアーウォールで防御できる?
  5. 文字エンコーディングベースのSQLインジェクションは文字エンコーディングが正しければ行えない?

SQLインジェクションを目的としたコードのチェック

PHPアプリの場合,プリペアードクエリを利用するアプリケーションより,文字列エスケープを用いてSQLインジェクションを防いでいるアプリケーションが多いと思います。ここではPostgreSQLを利用している場合を例にチェック箇所を紹介します。チェック手順の紹介の前に,SQLインジェクション対策のベストプラクティスを紹介します。

  • 文字エンコーディングが正しいか検証し,文字エンコーディング関連の処理を厳格に行う
  • クエリを生成する場合に「すべて」のパラメータを文字列として扱いエスケープ処理してクエリを生成する
  • プリペアードクエリ生成の際に,プリペアード文にパラメータを入れない
  • テーブル名,フィールド名をパラメータで指定する場合,ホワイトリスト方式で確認する

コードチェックはこれらのベストプラクティスが実践されているかチェックすることが目的となります。これらのベストプラクティスを守っていなくてもSQLインジェクションが不可能なコードを記述可能ですが,SQLインジェクションを確実に防ぐためにはベストプラクティスに従うべきです。

SQLインジェクション脆弱性チェックのポイント

チェックポイント:すべての入力がmb_check_encoding関数で文字エンコーディングチェックされ,不正な入力でスクリプトの実行が停止するか?

壊れた文字エンコーディングを利用したSQLインジェクションを防ぐためには文字エンコーディングが正しいか確認することが最も確実です。SQLインジェクション以外の攻撃にも壊れた文字エンコーディングが利用されるので,アプリケーション全体で入力文字エンコーディングのチェックが必要です。

$_POST,$_GET,$_COOKIE,$_FILES,$_SERVERの値を利用する場合に必ず文字エンコーディングをチェックします。常に文字エンコーディングはチェックすべきなので,グローバルなチェック処理の一つとしてmb_check_encoding関数を利用しなければなりません。一部例外が必要な場合──例えばクロスサイトでデータ交換を行うためにバイナリデータをクエリ文字列にURLエンコードして受け渡すなど──には例外処理として除外するようにします。

チェックポイント:文字列のエスケープにデータベースAPI関数を利用し,データベース接続リソースも指定しているか?

文字列のエスケープ処理を常に正しく行うには,クエリを送信するデータベース接続で利用している文字エンコーディング情報を利用してエスケープ処理する必要があります。特にMySQLを利用している場合,APIを正しく利用しないと文字エンコーディングベースのSQLインジェクションに脆弱になる可能性が高くなります。

チェックポイント:すべてのパラメータをエスケープしているか?

文字列のエスケープは多少のオーバーヘッドが必要です。すべてのパラメータをエスケープするのは無駄に思えるかもしれませんが,エスケープしないとセカンドオーダーSQLインジェクション(間接SQLインジェクション)の可能性も考慮しなければならなくなるなどの弊害も発生します。このようなリスクとセキュリティチェックの容易性を考慮すると,すべてのパラメータはエスケープするほうがよいと言えます。

著者プロフィール

大垣靖男(おおがきやすお)

University of Denver卒。同校にてコンピュータサイエンスとビジネスを学ぶ。株式会社シーエーシーを経て,エレクトロニック・サービス・イニシアチブ有限会社を設立。
オープンソース製品は比較的古くから利用し,Linuxは0.9xのころから利用している。オープンソースシステム開発への参加はエレクトロニック・サービス・イニシアチブ設立後から。PHPプロジェクトでは,PostgreSQLモジュールのメンテナンスを担当している。

URLhttp://blog.ohgaki.net/

著書

コメント

  • Re:

    もう1点

    > キャストした場合,不正な攻撃目的の入力が行われてもクエリエラーが発生しない可能性があります。攻撃目的の入力は検出できるほうが好ましく,文字列として扱えばクエリエラーで簡単に攻撃用の文字列が検出できます。

    入力として整数が期待されるのであれば整数(として扱える文字列)であるかのチェックを行い、問題があればクエリを実行せずにエラーとして処理するべきです。

    型キャストするのはベストプラクティスではないとのことですが、整数が期待されるパラメータに整数以外の文字列が入力された事をクエリの失敗で検出するというのはそれとは比較にならないほど致命的な誤りです。

    クエリーの失敗はあくまで結果であり、失敗することを何かの目的として期待すべきではありません。

    Commented : #2  鈴木 (2008/09/18, 14:12)

  • Re:

    PostgreSQLではインターフェースライブラリのlibpqを通してSET client_encoding TOを呼ぶのとlibpqのPQsetClientEncodingを呼ぶのに変わりはありません。

    SET client_encoding TOを使わないのはなく、SET client_encoding TOを含む何らかの方法で適切に文字コードを設定する事が重要です。

    本記事はSET client_encoding TOに問題があるかのような誤った理解に繋がるように思えます。

    Commented : #1  鈴木 (2008/09/18, 10:18)

コメントの記入