[PR]小規模ECサイトに最適なWAF、SiteGuard Lite

徳丸浩の日記


2011年01月01日

_PHPのescapeshellcmdの危険性

本を書いています。初稿を一通り書き上げ、第2稿を作成中です。その過程で見つけたことを報告します。

PHPのescapeshellcmdはパラメータをクォートしないので呼び出し側でクォートする必要がありますが、escapeshellcmdの仕様がまずいために、呼び出し側でクォートしても突破できることが分かりました。

escapeshellcmdの仕様

PHPにはシェルのパラメータをエスケープする関数が2つあります。escapeshellargとescapeshellcmdです。escapeshellargは、エスケープだけでなくシングルクォートでクォートもしてくれます(引用符で囲むことをクォートするといいます)。エスケープ方法も、シングルクォートで囲む場合のエスケープ方法に従っているので、割合安心して利用できます*1。一方、escapeshellcmdは、エスケープのみでクォートしないので、呼び出し側でクォートする必要があります。

PHPのマニュアルには、このあたりのことがちゃんと書いてあります。

<?php

$e = escapeshellcmd($userinput);

// ここでは $e がスペースを含んでいても関係ない

system("echo $e");

$f = escapeshellcmd($filename);

// ここでは気を遣い、クォートを使用する

system("touch \"/tmp/$f\"; ls -l \"/tmp/$f\"");

?>

[PHP: escapeshellcmdより引用]

スペースのことを気にする理由は、スペースにより第2、第3の追加のパラメータを挿入される攻撃を防ぐためでしょう。その考え方自体は問題ありません。しかし、ここに穴はないでしょうか。

追加のパラメータの挿入を防ぐためには、ダブルクォートで囲むだけではダメで、パラメータ中にダブルクォートがある場合は、ダブルクォートをエスケープする必要があります。以下のように。

  echo "abc\"def"

この結果は「abc"def」となります。ところが、escapeshellcmdはダブルクォートをエスケープしますが、マニュアルに妙なことが書いてあります。

' および " は、対になっていない場合にのみエスケープされます。

[PHP: escapeshellcmdより引用]

ということは、対になっている場合はエスケープしないのでしょうか。さっそく試してみましょう。

【サンプル】
<?php
   echo escapeshellcmd('abc"def"ghi') . "\n";
【実行結果】
abc"def"ghi

マニュアルに書いてある通りですね。ダブルクォートが3個以上の場合は、偶数の場合はエスケープなし、奇数の場合は最後のダブルクォートのみエスケープされるようです。しかし、これではまずい場合があります。PHPのマニュアルに書いてある「気を遣」っている方のサンプルに、「aaa" "/etc/passwd」を入力してみましょう。

【スクリプト】
$filename = 'aaa" "/etc/passwd';
$f = escapeshellcmd($filename);
system("touch \"/tmp/$f\"; ls -l \"/tmp/$f\"");
【実行結果】
 touch: `/etc/passwd'にtouchできませんでした: Permission denied
 -rw-r--r-- 1 root    root    1320 2010-12-31 14:48 /etc/passwd
 -rw-r--r-- 1 wasbook wasbook    0 2011-01-01 17:33 /tmp/aaa

第2のパラメータとして、/etc/passwdが挿入されています。touchの方は権限がないのでエラーになっていますが、lsの方はしっかり表示されています。この際のsystem関数の引数は以下の通りです。

touch "/tmp/aaa" "/etc/passwd"; ls -l "/tmp/aaa" "/etc/passwd"

コマンドラインという観点からは非の打ち所がない指定ですが、セキュリティという観点では非常に問題です。これにより、意図しないファイルを追加されたり、オプションを追加する(例えば、findコマンドの-execオプション)ことで、意図しない動作を引き起こす可能性があります。

この原因は、escapeshellcmdの「' および " は、対になっていない場合にのみエスケープされます」という仕様にあります。まったく余計なお世話としか言いようがありません。この仕様では、恐ろしくてescapeshellcmdは使えませんし、マニュアルにここまではっきり書いてある仕様を今さら変えられないでしょう。escapeshellcmdはお蔵入りするしかないと思います。幸い、escapeshellargの方はまともな仕様と思われますので、escapeshellargで代替してください。

ただし、そもそもOSコマンドを呼ぶのがよいかとか、もっと良い方法はないのかという疑問が出てきます。もっと良い方法はあります。それは、本が出てからのお楽しみ、ということで。


追記:2011年1月4日 12:50

escapeshellcmdを使うと危険となる実例について、日記を書きましたので参考になさってください。。escapeshellcmdの危険な実例

*1 ただし、ロケールの問題は注意が必要です


2010年10月03日 「PHPで作成する携帯会員サイトの基本」の諸問題(1)

_問題点の概要

CodeZineから発表されている「PHPで作成する携帯会員サイトの基本」という記事はツッコミどころ満載で、既にいくつかの問題が修正されているのだが、まだ残っている問題があることや、修正内容にも疑問があるので、いくつか指摘してみたい。ざっと書いたところ、ものすごく長くなりそうだったので、小出しで「連載」の形で書く。忙しいので途中でやめるかもしれない。今回は、問題点の概要を報告する。

くだんの記事をざっと見たところ、以下の問題を見つけた。

IPアドレス制限のない「かんたんログイン」

Net_UserAgent_Mobileを用いて携帯電話の端末IDを取り出し、かんたんログインを実装しているが、ゲートウェイのIPアドレス経由であることを確認していない。以下のリストは、端末IDを取り出しているところ(4ページ目)。

$agent = Net_UserAgent_Mobile::singleton();
$uid = $agent->getUID();

かんたんログインは、IPアドレスチェックをしても安全とはいえないが、IPアドレスチェックをしていないのは論外といえる。

参考→「高木浩光@自宅の日記 - はてなのかんたんログインがオッピロゲだった件

クロスサイト・スクリプティング(XSS)

プロフィール画面にて、氏名の表示箇所にクロスサイト・スクリプティング脆弱性がある(4ページ目)。以下のリストが該当の表示部分だが、登録・更新の画面でも文字種などはチェックされていない。加えて、データベースには、どの項目も255文字までの登録が可能なので、攻撃には十分な文字数である。

<?php $userInfo = $ss->getSession(); ?>
ようこそ、<?php echo $userInfo['user_name']; ?>さん    <br /> 

このページはユーザプロファイルの表示画面だから、本来は、自分自身のユーザ情報のみが表示される。この制約下で、どこまでXSS攻撃が可能かどうかを考えることは、脆弱性のリスクを分析するトレーニングとして好適だろう。ヒントとしては、このサンプルスクリプトは、URLにセッションIDを埋め込んでいることと、かんたんログインをサポートしていることである。

無意味なセッションIDの再生成

ページ生成毎にHTTP_Session2::regenerateId(true);を呼び出している。すなわち、ワンタイムのセッションIDを採用している。脆弱性ではないが、以下の欠点があり、使い物にならないだろう。

  • ブラウザ機能による「戻る」操作ができない
  • リロードするとセッションが切れる(ログアウト状態になる)
  • 電波状態などの理由で一度でも通信が途切れるとセッションが切れる

最初の稿では、regenerateId()の引数trueが省略されていたので、上記欠点がない代わりに、セキュリティ上はなんの効果もなく、逆にログアウト処理ができないという副作用があった。

MySQLの設定によってはSQLインジェクション脆弱性

最初の稿では、Shift_JIS文字エンコーディングでPDOを使っているため、SQLインジェクション脆弱性があった(参考→「ぼくがPDOを採用しなかったわけ(Shift_JISによるSQLインジェクション) 」)。現在は改修されているが、それでもPHPの内部文字エンコーディングをShift_JISにしている点はそのままで、好ましくない。

元パスワードを確認しないパスワード変更機能

これは見出しの通り

パスワードの保存方法

元々は平文でパスワードを保存していたが、その後改修され、MD5ハッシュの形で保存されている。要件によってはパスワードの平文保存が絶対ダメとまでは思わないが、仮にハッシュで保存するのであれば、saltなしのMD5ハッシュでは意味がない。これについては後日詳しく書く予定。

まとめ

PHPで作成する携帯会員サイトの基本 」という記事について、主にセキュリティ上の問題点の概要を説明した。現在は改修されているものもあるが、SQLインジェクションあり、XSSあり、IPアドレス制限すらないオッピロゲのかんたんログインありで、脆弱性のオンパレードだ。SQLインジェクションは改修されたが、他の脆弱性は現時点で改修されていないようだ。読者は、絶対に参考にしてはならないし、ましてやコピペして本番サイト開発に利用してはならない。もっとも、regenerateId(true)の副作用が強すぎて、仮にコピペしても使いものにならないと思われる。

本日のツッコミ(全5件) [ツッコミを入れる]

Before...

_ 徳丸浩 [rooさん、コメントありがとうございます。 そうですね、僕も書きたいと思っているのですが、ちょっと時間がとれません。..]

_ roo [お返事ありがとうございます。 講演資料、大変参考になりました。 saltなしハッシュの危険性がわかりました。 そうい..]

_ (^_^.) [分かりやすかったです。 情報の授業に役立ちました☆ (あ、コピーなどはしていないのでご安心くださいませ。)]


2010年09月27日

_文字コードに起因する脆弱性を防ぐ「やや安全な」php.ini設定

PHPカンファレンス2010にて「文字コードに起因する脆弱性とその対策」というタイトルで喋らせていただきました。プレゼンテーション資料をPDF形式slideshare.netで公開しています。

文字コードのセキュリティというと、ややこしいイメージが強くて、スピーカーの前夜祭でも「聴衆の半分は置いてきぼりになるかもね」みたいな話をしていたのですが、意外にも「分かりやすかった」等の好意的な反応をtwitter等でいただき、驚くと共に喜んでいます。土曜にPHPカンファレンスに来られるような方は意識が高いというのもあるのでしょうね。

さて、その場で少し触れた「文字コードをやや安全に扱う方法」を紹介したいと思います。

実はこの方法を紹介するかどうかは悩みどころでして、完全に安全になるならともかく、「これで全部おk」みたいに開発者が文字コードのセキュリティに無関心になる方法に働くといやだなと思うわけです。しかし、ネットには、既に根拠の不明確なバッドノウハウであふれているわけで、少しマシなバッドノウハウを流通させることで、改善になるのではないかと思い、公開に踏み切りました。しかし、専門家として、どこまで安全にできるかとか、安全になる根拠は示したいと思います。

文字コードをやや安全に扱うphp.ini設定

まずは結論を示します。php.iniに(あるいは.htaccessで)以下を設定します。

;; 出力バッファリングを無効にする (追記:文字エンコーディングの変換をしなければ、On でもいいです)
output_buffering      = Off

;; HTTPレスポンスの文字エンコーディングを設定
default_charset       = UTF-8

;; デフォルトの言語を日本語にする
mbstring.language = Japanese

;; HTTP 入力変換を有効にする
mbstring.encoding_translation = On

;; HTTP 入力エンコーディング変換を UTF-8 に設定(UTF-8→UTF-8の変換)
mbstring.http_input   = UTF-8

;; HTTPレスポンスは変換しない
mbstring.http_output  = pass

;; 内部エンコーディングを UTF-8 に設定
mbstring.internal_encoding = UTF-8    

;; 無効な文字は「?」に
mbstring.substitute_character = "?"

php.iniで指定できない条件として以下も必須です。

htmlspecialcharsの第3引数に必ず'UTF-8'を指定する

PHPのソースはUTF-8でセーブする

データベースの接続・テーブル・データベースの文字エンコーディングは全てUTF-8で統一する

この設定の要点を箇条書きで説明します。

  • Webアプリケーションの入口から出口まですべてUTF-8で統一する
  • 入力時にUTF-8→UTF-8の変換をすることで、不正な文字エンコーディングを除去する
  • HTTPレスポンスの文字エンコーディングをUTF-8と明示する

この設定でなぜ、どこまで安全になるか、PHPカンファレンスで実演した脆弱性デモとの対応で説明します。

文字コードをやや安全に扱うphp.ini設定はなぜ安全か

以下の説明は、先に紹介したプレゼン資料と対比させながらご覧ください。

「デモ1:半端な先行バイトによるXSS」への対応

半端な先行バイトはUTF-8でもあり得ますが、UTF-8→UTF-8変換の過程で半端な先行バイトは除去されます。

「デモ2:UTF-8非最短形式によるパストラバーサル」への対応

非最短形式のUTF-8もUTF-8→UTF-8変換の過程で除去されます

「デモ3:5C問題によるSQLインジェクション」への対応

UTF-8では原理的に5C問題は発生しません

「デモ4:UTF-7によるXSS」への対応

default_charsetをUTF-8に設定することで、HTTPレスポンスヘッダの文字エンコーディングが設定され、UTF-7によるXSSを防げます。

「デモ5:U+00A5によるSQLインジェクション」への対応

実はこれについてはphp.iniだけでは防げませんが、データベース側の設定もUTF-8に統一すれば防げます。

「デモ6:U+00A5によるXSS」への対応

アプリケーションの内部でUTF-8を一貫して使うことにより、文字集合の変更がなくなるので、U+00A5によるXSSは発生しません。

確認方法

PHPカンファレンスでも紹介した「尾骶骨テスト」や「つちよしテスト」が有効です。以下の文字を入力・登録して、どのように表示されるかを調べてください。


  • ¥(U+00A5) バックスラッシュに変換されないか
  • 骶(U+9AB6) JIS X 0208にない文字
  • 𠮷(U+20BB7) BMP外の文字 UTF-8では4バイトになる
  • このテストにより、文字集合の変更がどこかで起こってないかを確認することができます。データベースによってはBMP外の文字(U+10000以降の文字)に対応していないものもあるので、「つちよしテスト」を全てのアプリケーションがパスできるとは限りません。逆に、U+00A5がバックスラッシュ(0x5C)に化けるアプリケーションには潜在的な危険性があります。

    注意事項

    今回紹介したphp.ini設定は、UTF-8→UTF-8の変換以外はバッドノウハウでもなんでもなくて、ごくまともな設定です。問題は、UTF-8→UTF-8の変換に文字エンコーディングの妥当性チェックを頼っているので、サーバーの移設などでphp.iniの設定が変更され、文字エンコーディングの自動変換がされなくなった場合に危険になることです。php.iniが変更された可能性がある場合は、phpinfoなどにより、mbstring.encoding_translationがOnになっていることを確認してください。同様に、default_charsetがUTF-8になっていることを確認してください。

    こういう問題もあるので、本当はスクリプトで、レスポンスヘッダの文字エンコーディング指定や、文字エンコーディングのチェックをした方が頑丈なアプリケーションになります。新規開発の場合は、フレームワークの設定やカスタマイズなどで、これらの処理が行われることを確実にするとよいでしょう。

    また、講演中にhtmlspecialcharsの第3引数(文字エンコーディング指定)を必ず指定するように強調しましたが、それは今回紹介する方法でも変わりません。技術力のある方の中には、「でも入口で不正な文字エンコーディングが除去されているから、htmlspecialcharsの第3引数は指定しなくても一緒でしょ」と言う人もいると思いますが、以下のような実効性もあるのです。

    それは、mbstringではチェックしないが、htmlspecialcharsではチェックするという項目があるのです。UTF-8の5バイト、6バイトの形式です。これを確認するスクリプトを作りました。

    <?php
      $u8_6 = "\xFC\x84\x80\x80\x80\x80"; // 6バイト形式のUTF-8
      var_dump(mb_check_encoding($u8_6, 'UTF-8'));
      echo bin2hex(mb_convert_encoding($u8_6, 'UTF-8', 'UTF-8')) . "\n";
      var_dump(htmlspecialchars($u8_6, ENT_QUOTES, 'UTF-8'));
      echo bin2hex(htmlspecialchars($u8_6, ENT_QUOTES));
    
    【実行結果】
    bool(true)         # checkは通っている
    fc8480808080       # UTF-8→UTF-8の変換後もそのまま
    string(0) ""       # htmlspecialcharsにUTF-8を指定すると削除される
    fc8480808080       # htmlspecialcharsに文字エンコーディングを指定しないと、そのまま
    

    このように、mbstringではUTF-8の5、6バイトの表現を認めていますが、htmlspecialcharsを通すと、5バイト以上の表現は除去されます*1。IEやFirefoxなど、現在広く使用されているブラウザはUTF-8の5、6バイトの表現を認識しないと思いますが、万一、UTF-8の5、6バイトの表現を用いた攻撃手法が見つかっても、htmlspecialcharsが削除してくれると安心です。こういうこともあり得るので、htmlspecialcharsの第3引数は指定するようにしましょう。これも、フレームワークに組み込むか、ラッパー関数を用意しておけば簡便です。

    本当は、htmlspecialcharsのデフォルト文字エンコーディングがmbstring.internal_encodingになってくれると楽ちんなのですが、現在はISO-8859-1がデフォルトなので、こういう面倒なことになります。

    まとめ

    「文字コードをやや安全に扱うphp.ini設定」を紹介し、安全になる根拠および限界を説明しました。「これさえやっておけば、おk」となるのではなく、むしろ文字コードの問題に興味を持っていただくためのきっかけになれば幸いです。なお、文字コードの話は、SQLインジェクションとかXSSなど脆弱性対策をした上での話ですので、php.iniだけで脆弱性対策が不要になるわけではないので、念のため。

    *1 この問題については、id:t_komuraさんの素晴らしいブログエントリ「最新の PHP スナップショットでの htmlspecialchars()/htmlentities() の修正内容について」に詳しい分析があります


    2010年07月25日

    _ツッコミSPAM対策で、ツッコミ抜きのRSSフィードを用意しました

    昨日17:47から19:13にかけて、大量のツッコミSPAMが来ています。取り急ぎ、ツッコミの一日あたりの上限数を0にすることにより、対応しましたが、フィードで日記をご覧いただいてる方々には、見苦しいものをお見せして申し訳ございません。

    ツッコミ対策は色々工夫してはおりますが、まだ決定的なものはない状態で、時々このような事態となっております。

    当面の問題として、RSSフィードがSPAMだらけになることが問題ですので、ツッコミを含まないRSSフィードも生成する設定にいたしました。ツッコミがうざいと思われる方は、恐れ入りますが、以下のURLから再設定いただけるとよろしいかと思います。

    http://www.tokumaru.org/d/no_comments.rdf

    no_comments.rdfの方は当面試験運用といたします。不具合などありましたら、ツッコミなどでお知らせください。

    本日のツッコミ(全2件) [ツッコミを入れる]

    _ YuneKichi [no_comments.rdfに限らないのですが、この記事以降のフィードが更新されていないように思われます。]

    _ 徳丸浩 [YuneKichiさん、ご指摘ありがとうございます。 設定がおかしかったので本日修正しました。ありがとうございました..]


    2010年07月01日

    _ぼくがPDOを採用しなかったわけ(Shift_JISによるSQLインジェクション)

    PHPのデータベース・アクセス・ライブラリPDOは、DB接続時の文字エンコーディング指定ができないため、文字エンコーディングの選択によっては、プレースホルダを使っていてもSQLインジェクション脆弱性が発生します。


    追記(2011/06/19)

    ここに来て急にブクマが追加されはじめていますが、このエントリを書いてから状況が改善しています。PHP5.3.6(2011/03/17)にて、PDOでもデータベース接続の文字エンコーディングを指定できるようになりました。この版で、UNIX版のPHPでは解決しましたが、Windows版のPHPではバグがあり、解決には至っていませんでした。その後、千葉征弘さん(@nihen)により原因が判明し、PHP5.3.7RC1では修正されていることを確認しています。したがって、PHP5.3.7の正式版が出れば、この問題は解決すると思われます。(追記終わり)

    はじめに

    本を書いています。SQLインジェクションの節から書き始めて、初稿はレビュアーの方々にお送りしましたところ、さっそく有意義なコメントを多数頂戴しています。ありがとうございます。

    その原稿の中で、DBアクセスに使用するPHPのライブラリとしてMDB2を紹介していたところ、PDOを紹介すべきではという意見を複数頂戴しました。そこで、現時点でPDOを採用していない理由を報告したいと思います。これは、「安全なSQLの呼び出し方」でPDOを取り上げていない理由でもあります。

    PDOは接続時に文字エンコーディングを指定できない(指定しても無視される)ので、データベースへの接続時の文字エンコーディングはLatin1が暗黙に指定されます。すると、日本語の読み書きで文字化けが生じるため、「SET NAMES SJIS」により、文字エンコーディングを接続後に指定するようなプログラミングがなされているようです(MySQLの場合)。

    そのようなプログラム例を示します。このプログラムでは、内部文字エンコーディングとしてShift_JISを利用しているという想定です。PHP5.3.0とMySQL5.1.37の組み合わせで確認しました。

    <?php
      $dbh = new PDO('mysql:host=localhost;dbname=test;charset=sjis', 'username', 'password');
      $dbh->query("SET NAMES sjis");
      $sth = $dbh->prepare("select * from test WHERE name=?");
      $sth->setFetchMode(PDO::FETCH_NUM);
      $name = ...
      $sth->execute(array($name));
      while ($data = $sth->fetch()) {
        var_dump($data);
      }
    

    PDO+MySQLの場合、プレースホルダは「動的プレースホルダ」あるいは、俗に「クライアントサイドのプリペアードステートメント」などと呼ばれる実装になっています*1

    このため、$nameとして「ソ' OR 1=1#」という文字列を指定した場合、MySQLに対して以下のようなクエリが送信されます(Wiresharkによるダンプ)。


    0x83は「ソ」の先頭バイトです。これをPDOではLatin1(ISO-8859-1)として扱うので、0x83はNBHという制御文字を意味し、1バイト文字になります。PDOは、続く「\'」を「\\\'」とエスケープします。

    一方、MySQLサーバー側では、受け取ったSQLをShift_JISと想定しているので「ソ\\'」という並びと解釈します。「\\」は「\」をエスケープしたものなので、後続の「'」は文字列リテラルの終端に使われ、残りの「 OR 1=1#'」はSQL文の一部とみなされます。SQL文が注入できたので、SQLインジェクション脆弱性があるということになります。ここまでの説明を下図にまとめました。


    my.cnfの修正でもダメ

    「set names」がセキュリティ上問題があることが広く知られるようになったせいか、PDOの文字化けを別の方法で対処するようにすすめているエントリもあります。たとえばこのエントリでは、my.cnf(my.ini)の修正でPDOの文字化けに対応しているのですが、Shift_JISを使う場合はこれもダメです*2。その理由は、「PDOはLatin1として処理したものを、MySQLはShift_JISとして解釈する」状態には変わらないからです。

    まとめ

    • PDO+MySQLでは、動的プレースホルダ(なんちゃってプリペアード・ステートメント)が使われる
    • PDOでは文字エンコーディングが指定できない
    • PDO+MySQL+Shift_JISでは、プレースホルダを使ってもSQLインジェクション脆弱性となる

    解決策・回避策

    PDOの文字化け問題とSQLインジェクション脆弱性問題を解決するにはどうすればよいでしょうか。私が思いつくのは以下の2つの方法です。

    1. PDOではなくMDB2など文字エンコーディングを正しく扱えるライブラリを使用する(根本的対策)
    2. アプリケーションからDBまで一貫してUTF-8で処理し、set names utf8により文字化けを回避する(回避策)

    2.で具体的な問題が起こるかどうかは分かりませんが、文字エンコーディングを正しく扱えないことで潜在的なリスクがあると考えます。そのため、書いている本の中では1.を採用したという訳です。

    しかし、私自身PHPを熟知しているわけではないので、ひょっとすると、PDOの使い方などでもっとよい方法があるかもしれません。識者のご指摘をいただければ幸いです。

    追記(2010/07/01 22:20)

    id:nihenさんが、PDOに対して文字エンコーディングを設定する方法を調べてくださいました。nihenさん、どうもありがとうございます。以下に引用します。

    PDOからよばれるreal_escape_stringで文字コードを考慮させたい場合は
    $dbh = new PDO('mysql:host=localhost;dbname=sandbox;charset=cp932', 'sandbox', 'sandbox', array(
        PDO::MYSQL_ATTR_READ_DEFAULT_FILE => '/etc/mysql/my.cnf',
        PDO::MYSQL_ATTR_READ_DEFAULT_GROUP => 'client',
    ));
    
    もしくはserver-side-prepareを使う場合(文字コード気にしなくておk)
    
    $dbh = new PDO('mysql:host=localhost;dbname=sandbox;charset=cp932', 'sandbox', 'sandbox', array(
        PDO::ATTR_EMULATE_PREPARES => false,
    ));
    
    ちなむと前者は接続時に使われるオプションなのでnew PDOしたあとにあとから
    $dbh->setAttribute
    しても、ダメ。後者はおk。
    

    ご覧のように、MySQLサーバーへの接続時に、MySQL APIに渡すパラメータとして、my.cnfのファイル名を指定しています。上記の例ではGROUPを「client」に設定していますので、my.cnfに以下の設定を追加することになります。

    [client]
    default-character-set=sjis
    

    ただし、Windows版のPHPでは、PDO::MYSQL_ATTR_READ_DEFAULT_FILEが使えないようです。このため、サーバー接続時に文字エンコーディングを指定する方法がありません。このため、Windows版のPHPを使う場合は、PDO::ATTR_EMULATE_PREPARESをfalseにする(真のプリペアード・ステートメントを使う)ことで対策することになります。

    *1 高木浩光氏のいわれる「なんちゃってプリペアド・ステートメント

    *2 元エントリはUTF-8なので問題はおきませんが、逆に言えば、set names utf8でも問題ないことになります

    本日のツッコミ(全7件) [ツッコミを入れる]

    Before...

    _ bero [> PDO+MySQLの場合、プレースホルダは「動的プレースホルダ」あるいは、俗に「クライアントサイドのプリペアード..]

    _ bero [上記mysql->libmysql ドキュメントにも書いてた http://php.net/manual/ja/r..]

    _ carrot [PHP5.2.1から、PDO_MYSQLは、デフォルトではクライアントサイドのプリペアードステートメントになったよう..]


    2010年04月06日

    _PROXY(プロキシ)経由でのDNSリバインディングと対策

    このエントリでは、PROXY経由でWebアクセスしている場合のDNSリバインディング対策について考察する。

    PROXY(プロキシ)経由でWebアクセスしている場合、DNSによる名前解決はブラウザではなくPROXYにより行われる。したがって、 ブラウザ側ではDNS Pinningできないので、PROXY側での対策が重要になる。

    広く使われているPROXYサーバーであるsquidの場合、DNS Pinningに相当するパラメータは、negative_dns_ttlであり、デフォルト値は1分間である。したがって、最初のアクセスから1分あけてXMLHttpRequestすれば、DNSリバインディングが成立する。これは実験で確認した。Internet ExplorerやOperaは、他のブラウザと比べてDNS Pinningがしっかりされている印象があるが、PROXY経由の場合は、これらブラウザでも比較的簡単にDNSリバインディングが成立するので注意が必要だ。

    先のエントリで説明したように、JavaアプレットによるDNSリバインディングでも、URLクラスを使いかつPROXY経由でのアクセスの場合は、やはりPROXYでのDNS Pinningが問題となる。PROXY経由でのアクセスでは、Javaアプレットの場合でも、JavaScriptのXMLHttpRequestの場合同様、1分以上間隔をあけてやればDNS Rebindingが成立する。ただし、攻撃に使用できるプロトコルはHTTPのみであるので、JavaアプレットとJavaScriptでは、リスクは変わらない。

    negative_dns_ttlの値を大きくすれば、squidがDNSキャッシュする最低時間を延ばすことが可能だが、二つの点で問題がある。

    第一の問題は、negative_dns_ttlを大きくすることの副作用だ。negative_dns_ttlは元々、DNSアクセスに失敗した場合のキャッシュ保持時間を意味するからだ。以下に、negative_dns_ttlのリファレンスを引用する。

    Time-to-Live (TTL) for negative caching of failed DNS lookups. This also sets the lower cache limit on positive lookups. Minimum value is 1 second, and it is not recommendable to go much below 10 seconds.

    [squid : negative_dns_ttl configuration directiveより引用]

    このため、一例として、negative_dns_ttlを6時間に設定した状態でDNS参照に失敗した場合(参照先DNSサーバーが一時的に停止した場合など)も6時間はDNSを再アクセスしない。すなわち、DNSのエラーが発生した場合は、negative_dns_ttlで設定した時間内は、当該サイトにアクセスできなくなるのだ。このため、negative_dns_ttlの値をあまり大きくすると、サイト閲覧に支障がでる可能性がある。

    第二の問題は、negative_dns_ttlを長くしてもDNSリバインディングの根本対策にはならないことだ。 その背景には、PROXYサーバーをマルチユーザで使う場合が多いことや、タブブラウザの普及などがある。 やはりnegative_dns_ttlを6時間に設定した場合で説明しよう。AさんとBさんが同じPROXYサーバーを共有しているとする。Aさんが10:00にワナサイトにアクセスした場合、PROXYは16:00までワナサイトのIPアドレスをキャッシュする。したがって、Bさんが15:59にワナサイトを閲覧すると、そこから一分間でDNSキャッシュが無効になり、Bさんのブラウザ上動作するXMLHttpRequestがプライベートアドレスにアクセスすることを許してしまうかもしれない。 また、シングルユーザで考えても、タブブラウザのユーザはタブを開きっぱなしにする傾向があるので、6時間後の攻撃が成立する可能性はある。

    アクセス制御による対策

    結局negative_dns_ttlによる対策はうまくいかないので、別の方法を考える必要がある。有効な対策は、proxyのアクセス制御(ACL)により、squidにプライベートアドレスを中継させないことだ。以下に、192.168.0.0/16に対してアクセスを禁止する場合のsquid.confの設定例を示す。

    acl localnet dst 192.168.0.0/16
    http_access deny localnet
    

    この場合、プライベートアドレスに対してはPROXY経由でアクセスできなくなるので、ブラウザのPROXY設定の例外設定をお忘れなく。下図に例外設定の例を示す。


    このACLによる対策の効果であるが、JavaScriptとJavaによるアクセスに関しては完全に対策できると考える。当初Javaアプレットが対策から漏れることを心配していたが、先のエントリで検証したように問題ないことがわかった。Flashについては未検証なので今後検証したい…ところだが、私はFlashのコンテンツを書いたことがない。どなたか教えてください。

    まとめ

    PROXY経由でWebアクセスしている場合のDNSリバインディング対策について検討し、PROXYのACLによりプライベートアドレスを中継禁止することで対策可能であることを示した。DNSリバインディング対策のまとめについては別稿にて報告する。



    [PR]小規模ECサイトに最適なWAF、SiteGuard Lite

    ockeghem(徳丸浩)の日記はこちら
    HASHコンサルティング株式会社

    最近の記事

    最近のツッコミ

    1. かめ (11-29)
    2. いーさん (09-27)
    3. (^_^.) (07-03)
    Google