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

徳丸浩の日記


2010年03月25日

_DNSリバインディングによるルータへの侵入実験

このエントリでは、DNSリバインディング攻撃によりブロードバンドルーターの設定を外部からの侵入を許すように変更してみたので報告する。

DNSリバインディングに対する関心が高まってきている。本年1月12日には、読売新聞夕刊一面トップで、iモードブラウザ2.0のDNSリバインディングによる不正アクセスが報じられた。3月17日のcomputerworld.jpでは、『2010年に最も警戒すべきセキュリティ脅威は「DNSリバインディング」』という翻訳記事が紹介された。実は「最も警戒すべきセキュリティ脅威」というタイトルは誤報だったわけだが*1、DNSリバインディングというマニアックな攻撃手法が注目を浴びるきっかけにはなったと思う。

そこで、専門家の方にはいまさらかもしれないが、DNSリバインディング攻撃により、自宅のブロードバンドルーターの設定を変更してみた。思いの外簡単にできたが、対策も容易であるので、その両方を説明しよう。

侵入実験の前提

まず一般的なDNSリバインディングの概要説明は、「DNS Rebinding」を参照頂きたい。この説明では、イントラネット内のサーバーへの侵入の例を挙げているが、今回の実験では、ブロードバンドルータへの侵入となる。実験に用いた自宅のルータは、プライベートアドレス(内部ネットワーク)からは設定が変更できるが、インターネット側からは設定できないという仕様となっている。そのため、DNSリバインディング攻撃が有効だ。この種のルータに対する攻撃手法としてはクロスサイト・リクエスト・フォージェリ(CSRF)という選択肢も考えられるが、このエントリでは言及しない。

実験では、まず、ルータの設定変更に必要なHTTPリクエストを調査した。その結果、POSTリクエストを3回送信すれば、外部からの侵入を許すように変更できることがわかった。

次に、インターネット上にワナサイトを用意して、ワナサイトをリクエストした後にDNSの内容を書き換え、一定時間置いた後に上記3つのPOSTリクエストを送出するようにした。実験に用いたブラウザはFirefox 3.6.2であり、最初のリクエストから4分間の間隔をとれば、DNSリバインディングが成立することがわかった*2。このため、3回のリクエストはそれぞれ、4分後、4分10秒後、4分20後に送出されるように、JavaScriptによるスクリプトを作成した。

ここで問題になるのが、ルータの認証をどうやってすり抜けるかだ。他の攻撃手法、たとえばCSRFの場合は、ユーザが認証している状態を悪用するわけだが、DNSリバインディングではこの方法は使えない。なぜなら、DNSリバインディングによるリクエストのHost:フィールドはワナサイトのドメインになっているからだ。このため、実験では、攻撃者がルータのパスワードを推測できるという想定にした。以下の説明では、ユーザIDとパスワードがともに「admin」になっていると想定する。これはそれほど突飛な想定ではない。この種のルータの初期設定はパスワードなしか、固定の初期パスワードになっていることが多いからだ。

侵入実験の実際

下図に侵入前のルータの設定を示す。赤い四角で囲った部分に、新たな設定を追加する。



次に、以下は侵入実験に用いたスクリプトのごく一部だ。このルータはBASIC認証が使われているので、以下のようにURLの中にBASIC認証のIDとパスワードを記述した。スクリプトキディが真似するとまずいので、スクリプトの全容は非公開とする。また、router.example.comはワナサイトのドメインだが、仮にものに置き換えた。

    var requester = new XMLHttpRequest();
    requester.open('POST', 'http://admin:admin@router.example.com/entry_ipnat_main_bottom.html');

このスクリプトを含むワナが閲覧されたら直ぐに、router.example.comのIPアドレスを192.168.0.1(ルータのアドレス)に書き換える。その後3回のリクエストが正常にルータに送信されると、設定を反映するためにルータがリセットされた。攻撃成功である。攻撃成功後のルータの設定を下図に示す。



ご覧のように、192.168.0.10の端末に対して、インターネットからTCP3389ポートを受け付けるようになった。3389はWindowsのリモートデスクトップが使用するポートなので、この設定変更により外部からの侵入が可能になった。

対策

ルータやファイアウォールの設定変更は、DNSリバインディングのデモとしては定番と言えるものだが、実際の結果を見るとギョッとした人も多いのではないか。しかし、幸いなことに、対策は容易である。前述のように、DNSリバインディング攻撃は、ユーザの認証状態を引き継ぐことができないので、認証がかかっているサイトの場合、パスワードが分からないと侵入できない。従って、良質のパスワードを設定するだけで、侵入を食い止めることができる。

しかし、問題は、これだけブロードバンドのインターネットが普及した状況下で、ブロードバンドルータの多くがデフォルトパスワードのまま(パスワードなしも含めて)の設定と予想されることだ。従って、DNSリバインディングによる攻撃はいつ起こっても不思議ではないし、既に起こっているかもしれない。

従来、ブロードバンドルータのパスワード初期設定がいい加減だった理由は、「外部からは設定変更できないから安全だ」という意識が働いていたからだと推測する。しかし、ルータに対する外部からの侵入手法は、DNSリバインディングの他、ルータのCSRF脆弱性を悪用するなど複数存在する。一方で、ブラウザ側でDNSリバインディングを完璧に対策するのは困難だ。その理由は、IPアドレスの変更は、DNSの正式な仕様として認められた挙動であるからだ。このため、ルータやファイアウォールの初期パスワードやパスワードの管理について、さらなる工夫が求められる。

さらに網羅的なDNSリバインディング対策については、稿を改めて説明する。

追記(2010/03/31)

本エントリと関連して、「DNSリバインディングによる無線LANパスフレーズの読み出しに成功」を書きましたのであわせてお読みください。

*1 原題は「Top Ten Web Hacking Techniques」なので脅威という訳もおかしいが

*2 4分間が長すぎると感じる人もいると思うが、この時間を短くするAnti-DNS Pinningという手法が存在するし、ワナサイト上でYoutubeの映像でも流しておけば4分くらいはワナサイト上にとどまるユーザもいるだろう


2010年02月22日

_ケータイtwitter(twtr.jp)においてDNS Rebinding攻撃に対する脆弱性を発見・通報し、即座に修正された

twitterのケータイ版twtr.jpにおいて、DNS Rebindingによるなりすましを許す脆弱性が発見され、1/15に通報したところ、その日のうちに修正された。以下、その経緯について報告する。

経緯

今年の1月12日に読売新聞の記事が出たのを受けて、現実のサイトはどうなのだろうかと改めて気になった。

 NTTドコモの携帯電話のうち、インターネット閲覧ソフト「iモードブラウザ2・0」を搭載した最新29機種を通じて、利用者の個人情報を不正取得される恐れのあることが、専門家の指摘で明らかになった。

 同社は携帯サイトの運営者にパスワード認証などの安全対策を呼びかけている。携帯電話の機能が高機能化するにつれ、こうした危険は増しており、利用者も注意が必要になってきた。

[ドコモ携帯、情報流出の恐れ…最新29機種より引用]

私自身も携帯電話でいくつかのサイトを巡回しており、その一つにtwitter.comの日本の携帯電話向けフロントエンドであるtwtr.jpも含まれる。ご存じのように、twitterは政府要人や有名人も数多く使っているし、twitter.comの画面で確認する限り、鳩山由紀夫首相原口一博総務相などの閣僚の方々は携帯から書き込みをされることも多いようなので、仮に脆弱性があった場合、影響も大きくなると思った。原口総務相に関しては、記者団からの「Twitterの質問を受けて、つぶやきを確認する原口氏」という報道写真も公開されているので、(スタッフ任せではなく)自ら携帯電話を使いこなしてtwtr.jpにアクセスしておられることは間違いないだろう。

手始めにtwtr.jpのIPアドレスを別のドメインにセットして、携帯電話からアクセスすると、正常に画面が表示される(右の写真)。これはまずい。このため、DNS Rebindingを使ってなりすましができてしまわないか調べることにした。当然ながら、不正アクセス禁止法に抵触しないよう、自分のアカウントを使用して確認を行った。

調査の概要

調査にあたっては、自宅の調査用サーバと調査専用のドメインを用いた。DNS Rebindingを使ったかんたんログインのなりすましは、iモードIDを用いた「かんたんログイン」のDNS Rebinding脆弱性を発表する際に実験で確認していたが、twtr.jpのログイン画面は、次の点で私の実験とは異なっていた。

  • 実験ではGETメソッドだったが、twtr.jpはPOSTを使用
  • twtr.jpはログイン時にトークンの受け渡しをしていた

認証機能は副作用を伴うので本来はPOSTメソッドを使うのが正しいし、トークンは、外部から認証リクエストを強要される行為(CSRFに似ているが、認証前なのでCSRFではない)を防ぐためだろう。デジタルガレージ社の実装はセキュリティ上の考慮がなされていると感じた。

しかし、DNS Rebindingを使用すれば、ログイン画面のトークンを読み出すことが可能である。すぐに確認作業が終わるだろうと思っていたが、意外なところで失敗した。ログイン・リクエストのPOSTがうまくいかないのだ*1。早く確認を終わらせないと、もたもたしているうちに脆弱性を悪用されるとまずい。結局、XMLHttpRequestをあきらめ、IFRAME要素を用いることにした。

最初、IRAME要素をDOMで動的に作ったりしていたのだが、IFRAME内のFORMをうまくSUBMITできない。このため、以下のような構成にした。検証コードを公開すると、スクリプトキディが他のサイトで悪用するといけないので、コードは非公開とする。


一般ユーザ(被害者)からリクエストあり
①DNS情報書き換え → これ以降、ワナサイトは twtr.jpのIPアドレスを指す

・以下はIFRAME内の処理
 ②ログイン画面を要求(トークンが含まれる)
 ③トークン取り出し → ログイン画面のINPUTにセット(写真2)
 ④ログイン実行 → セッションIDがCookieにセットされる(写真3)

・以下はIFRAME外の処理
 ⑤CookieをINPUTにセット
 ⑥Cookie値を情報収集サーバにPOST

以下は、攻撃者の立場
・セッションID受信を確認
・別の携帯電話にCookieをセット
・twtr.jpにアクセス → なりすましを確認
・書き込みをしてみる → 成功(写真4、写真5)
写真2
写真3
写真4
写真5

写真2はトークンを受信した様子、写真3はIFRAME上でログイン後にCookie上のセッションIDをINPUTにセットしている様子である。このセッションIDを別の端末にセットして、書き込みをしてみたところが写真4である。

通報・届出およびデジタルガレージ社の対応

脆弱性を確認したので、twtr.jpの運営元である株式会社デジタルガレージに通報するとともに、IPAの脆弱性届出窓口に届け出た。その経緯を時系列で示す。

2010/01/14 深夜  脆弱性の確認完了
2010/01/15 11:37 デジタルガレージ社への通報
2010/01/15 12:52 IPAへの届出 取扱い番号 IPA#04364080 として受信される
2010/01/15 19:18 IPAより届出受理および取り扱い開始の連絡
2010/01/15 21:51 デジタルガレージ社の担当者より修正済みの返信。手元でも修正を確認。
2010/01/19 10:53 IPAより修正完了の連絡。その後取り扱い終了となる。

下図に、修正を確認した様子を写真で示す。

通知からわずか10時間あまりでの修正である。 高木浩光@自宅の日記 - はてなのかんたんログインがオッピロゲだった件によると、はてなは通知から修正完了まで20日も掛かったようだが(修正内容は異なるものの)株式会社デジタルガレージの対応の素早さは際だっている。

脆弱性の影響範囲

当該脆弱性の影響を受けるユーザは以下の条件を全て満たす利用者である。

  1. twtr.jpを一度でも使ったことがある
  2. iモードブラウザ2.0の対応機種(2009年夏モデル以降)の利用者
  3. iモードIDを通知設定をONにしている(デフォルトはON)
  4. JavaScriptの設定を有効にしている(デフォルトは有効)

携帯電話のユーザは、大半の方がデフォルト設定で端末を使っていると思われるので、簡単に言えば、iモードブラウザ2.0端末でtwtr.jpを使ったことのあるユーザ、ということになる。

当該脆弱性の影響は、セッションハイジャックによって受ける影響と等しい。すなわち、ワナサイトを閲覧してしまったユーザの当該ユーザでの投稿、ダイレクトメッセージの送信、ダイレクトメッセージの履歴閲覧、自己紹介やプロフィール画像の変更などである。また、「メールでツイートの設定」画面から投稿用メールアドレスを確認しておけば、後からいつでも当該アカウントでのつぶやきが行える。個人情報に関しては、メールアドレスも含めて閲覧できる内容はあまりないようだ。

先にtwtr.jpのユーザの例として挙げた鳩山首相や原口総務相が仮にワナサイトを閲覧させられた場合は、首相や総務相のアカウントで、攻撃者は任意のつぶやきが行えたことになる。

保険的対応

twtr.jpの脆弱性を悪用された書き込みなどは、私の知る限りは公表されていないようだが、脆弱性が修正された今でも油断はできない。仮に、攻撃者がなりすましを成功させていた場合、後から書き込みをする手段があるからだ。それは前述の「メールでツイート」機能を利用する方法だ。これは、その名の通りメール経由でtwitterの投稿をするもので、ユーザ毎の専用メールアドレスに文章を送信すると、その内容がtwitterに投稿される。右の写真は、私の「専用の投稿先メールアドレス」を表示させたものである。このメールアドレスはいつでも変更できるので、twtr.jpユーザは念のため変更しておいた方が良いだろう。

また、twtr.jpはかんたんログインを禁止設定することができない。メニュー上は「各種設定」から「かんたんログインの無効化」というメニューがあるが、試してみたところ、かんたんログインの設定内容を削除するだけで、その後パスワード認証でログインすると、再びかんたんログインが有効となる。このため、ユーザがtwtr.jp上でかんたんログイン設定を残したくない場合は、twtr.jpにアクセスするたびに「かんたんログインの無効化」を実行する(現実的ではない)か、iモードIDを無効化するしかないだろう。iモードIDは、iモードのマイメニューから無効化できる。

利用者がDNS Rebinding攻撃を避ける目的では、JavaScriptの無効化が現実的かつ有効な方法だと考える。


かんたんログイン手法の脆弱性に対する責任は誰にあるのか(再)

デジタルガレージ社がかんたんログインのDNS Rebinding問題を対策していなかった理由は何だろうか。おそらく、単純にこの問題を知らなかったのだろう。無理もない。私が昨年11月に公表していたとはいえ、小さなセキュリティ会社が公表した内容まで含めて、セキュリティ情報をすべて拾い上げて検証・対応しなければならないとするのも酷な話だと思う。しかも、脆弱性を通知したらその日のうちに対策する能力がデジタルガレージ社にあったのだ。IPAの届出制度は、個別のアプリケーションやWebサイトの脆弱性を扱うもので、かんたんログインのような「認証手法」は取り扱い対象外だ*2。このあたり、問題を周知できない自分自身の力のなさを痛感するとともに、やはり携帯電話事業者に『どうやったら「かんたんログイン」なるものが実現できるのか、ちゃんとした実装方法の公式解説を出』してもらわないと、この種の問題はなくならないだろうと改めて感じた。

*1 これは、後にiモードブラウザ2.0の制限のためだと分かった。こちらを参照されたし

*2 だからtwtr.jpという個別サイトの脆弱性は届け出た

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

_ らんさん [ツイッターを初めて登録しようと思いますので、よろしくお願いいたします。]


2010年02月12日 DNSリバインディング

_かんたんログイン手法の脆弱性に対する責任は誰にあるのか

id:ikepyon日記経由で、NTTドコモのサイトに以下のセキュリティ・ガイドラインが掲示されていることを知った。

iモードブラウザ機能の多様化により、機種によってiモードサイトにおいてもJavaScriptを組み込んだ多様な表現、CookieやReferer情報を有効に活用したサイト構築が行えるようになりました。

しかし、PC向けインターネットサイト同様に、セキュリティ対策が十分に行われていないサイトでは、そのサーバの脆弱性を突き(クロスサイトスクリプティング、SQLインジェクション、DNSリバインディングなど様々な攻撃手法が存在しています)、これらの機能が悪用される危険性があります。十分にご注意ください。

[作ろうiモード:iモードブラウザ | サービス・機能 | NTTドコモより引用]

XSSやSQLインジェクションと並んで、DNSリバインディングというマニアックな手法が紹介されていることは驚きだが、おそらく私が昨年発表したiモードIDを用いた「かんたんログイン」のDNS Rebinding脆弱性のことを指しているのであろう。

しかし、引用部の表現には違和感がある。DNSリバインディング自体は既知の攻撃手法であるが、「PC向けインターネットサイト」に対する攻撃ではなく、インターネットサイトを閲覧しているPC自身やファイアウォールの内側のローカルネットワーク上の端末に対する攻撃手法として知られている。このあたりの解説については、ここここを参照頂きたい。

しかも、先のセキュリティ・ガイドラインには、これら脆弱性についての説明はなく、「十分にご注意ください」とあるだけで、参考情報としてはIPAのトップページが紹介されている。

なお、セキュリティ対策情報については、「独立行政法人 情報処理推進機構」(IPA)が公開する情報なども参考にしてください。

独立行政法人 情報処理推進機構(IPA)のウェブサイト

[作ろうiモード:iモードブラウザ | サービス・機能 | NTTドコモより引用]

IPAにDNSリバインディングの解説があったかなと疑問に思い、「DNS "リバインディング" site:ipa.go.jp」や「DNS rebinding site:ipa.go.jp」などのキーワードで検索してみたが、ヒットしない。ひょっとしたらどこかに存在する可能性はあるが、リンクもなく、サーチでも引っかからないのでは、ないのと同じだ。これでは、単に危険があるから注意しろと言っているだけで、解決策を示していないことになる。

このような状況から、この文書は次のような意図をもって書かれたのではないかという感想を持った。

  • 同DNSリバインディングの問題は、携帯端末や事業者設備の問題ではなく、Webアプリケーション側の問題である(私も同意追記参照)
  • NTTドコモは、かんたんログインのDNSリバインディング脆弱性を、PCも含め昔から存在していた問題と印象づけたいのではないか(実際は違う)
  • DNSリバインディングの解決方法を明確にリンクしなかったのは、「NTTドコモ公認の解決策」という印象を与えることを避けたかったからではないか
  • 一方で、DNSリバインディングに対する注意喚起を行ったという実績は作りたいのではないか

「実績作り」で思い浮かぶのは、Yomiuri Onlineにも掲載された以下の記事だ。この記事は、かんたんログインのDNSリバインディング脆弱性に関して、NTTドコモのコメントを掲載している。

 NTTドコモでは、公式サイトを運営する約3000社には注意喚起したが、それ以外の無数にある「勝手サイト」には「ジャバスクリプトの安全な利用はサイトを作る側にとって基本的知識であり、具体的に説明はしていない」という。

[ドコモ携帯、情報流出の恐れ…最新29機種より引用]

勝手サイトに対して従来説明していなかったので、新たに説明したということなのかもしれない。しかし、先の説明ではまったく不十分だ

NTTドコモ(および他の携帯電話事業者)は、かんたんログインのセキュリティ問題について、そろそろ態度を明確にする必要があるのではないだろうか。その態度とは、以下の選択肢のどれを選ぶのかということだ。

  • かんたんログインという手法は各サイトが独自に実装しているもので、携帯電話事業者はガイドラインなども提示していないので責任は一切負わない
  • かんたんログインは、携帯電話事業者が提供している端末固有IDの応用であるので、安全な使い方を示すなど携帯電話事業者としても一定の責任が生じる

くだんの「セキュリティ・ガイドライン」を読む限り、NTTドコモは(本音では)事業者として一切責任を負わないと姿勢なのだと想像する。しかし、そう明言すると反発も予想されることから、あいまいな態度をとっているように見うけられる。

しかし、このようなあいまいな状況がもっとも危険なのだ。かんたんログインがこれだけ普及した現在でも、この手法に対する責任がどこにあるのか、非常に不明確な状態が続いている。最終的には、Webサイトの運営者が責任を負うべき問題ではあるだろうが、中小零細企業が多いケータイサイトの運営者がそのような問題意識を持っているとは考えにくいし、DNSリバインディングを含む複雑なセキュリティ問題を独自に研究・解決する能力もないだろう。であれば、日本独自の進化を遂げた携帯電話コンテンツのセキュリティ問題に対して、もっと広い枠組みでの検討が行われる必要があるし、そこにもっとも近い位置にいるのが携帯電話事業者であると私は考える。

追記(2010/2/16)

括弧内で「私も同意」と書いた部分について指摘を頂戴しました。

ここは同意しちゃだめ。Webアプリ側は「対策可能」なのであって、元からそこに問題があるわけじゃない。

[HiromitsuTakagiのブックマーク / 2010年2月15日 (3)より引用]

少なくともWebアプリケーション側の「不具合」ではないように思いますし、実際のところはこんな感じではないでしょうか。

  • iモードブラウザ2.0対応のdocomo端末は、DNS Rebindingの攻撃に対して脆弱である。

  • 端末の問題であるため端末側で対応することが望ましいが、名前解決はdocomoのゲートウェイ側で行われる場合があり、端末側では対応が難しい。

  • docomoのゲートウェイの問題についてはdocomo側で対応することが望ましいが、問題の性質上、対応が難しい (参考: Re:「docomoケータイのDNS Rebinding問題、全国紙で報道」)。

  • これらの問題に対してはWebサイト側で対応することが可能であり、しかも簡単に対応できる方法が存在する。

  • 従って、Webサイト側での対応が推奨される。

[DNS Rebinding問題の所在 | 水無月ばけらのえび日記より引用]

まことに指摘の通りで、私の本意は、ばけらさんがわかりやすく要約していただいたとおりです。

したがって、携帯電話事業者はこの問題に対して免責されるわけではなく実施可能な対策はとるべきであり、具体的には、iモードIDを用いた「かんたんログイン」のDNS Rebinding脆弱性に書いたように、DNSキャッシュの最短TTLを長くする程度の対策はとるべきだと考えます。

追記(2010/2/22)

かんたんログインのDNS Rebinding脆弱性の実例として、「ケータイtwitter(twtr.jp)においてDNS Rebinding攻撃に対する脆弱性を発見・通報し、即座に修正された」を書きましたのでご参照ください。


2010年01月18日

_iモードブラウザ2.0のXMLHttpRequestでPOSTデータの扱いが困難になった

このエントリでは、iモードブラウザ2.0の制限により、XMLHttpRequestでPOSTメソッドの利用が困難になっていることを確認したので報告する。

iモードブラウザ2.0のJavaScriptを試していて、POSTメソッドでデータが渡せていないことに気がついた。以下のようなプログラムで検証してみた。

【post.html】
<html>
<head>
<script>
function test() {
  try {
    var requester = new XMLHttpRequest();
    requester.open('POST', '/dumppost.php', true);
    requester.onreadystatechange = function() {
       if (requester.readyState == 4) {
           onloaded(requester);
       }
    };
    requester.setRequestHeader("Content-Type" , "application/x-www-form-urlencoded");
    requester.send("aaa=bbb&ccc=ddd");
  } catch (e) {
    res = requester.responseText;
    document.getElementById('result').innerHTML = e.toString();
  }
}

function onloaded(requester) {
  res = requester.responseText;
  document.getElementById('result').innerHTML = res;
}
</script>
</head>
<body>
<input type=button value="go" onclick="test()">
<div id="result"></div>
</body>
</html>

【dumppost.php】
<?php
echo "aaa=" . htmlspecialchars($_POST['aaa'], ENT_QUOTES, 'Shift_JIS') . "<br>";
echo "ccc=" . htmlspecialchars($_POST['ccc'], ENT_QUOTES, 'Shift_JIS') . "<br>";
?>

実行結果は、以下のようになった。まずはChromeのものだが、IEやFirefoxでも同等の結果だ。

Choromeでの結果

次に、ドコモP-07Aによる結果

P-07Aでの結果

ドコモの場合を検証するために、Webサーバーに来ているリクエストをキャプチャしてみた。

POST /dumppost.php HTTP/1.1
X-UE-Version: 1
Host: XXXXXXXXXXXXX
User-Agent: DoCoMo/2.0 P07A3(c500;TB;W24H15)
Content-Type: text/xml
Content-Length: 15

aaa=bbb&ccc=ddd

ご覧のようにPOSTデータそのものは送信されてきているが、Content-Typeがtext/xmlになっているために、Webアプリケーション側で受け取れないようだ。

JavaScript側では、この値をapplication/x-www-form-urlencodedに変更しているが、P-07AでもJavaScriptが再開され、あらたな制限がみつかったで報告したように、setRequestHeaderが無効化されているために、この設定が無視されていることが原因のようだ。

これは明らかに、setRequestHeader無効化の副作用であるが、その代償は大きいように思う。PHP以外に、ASP/ASPX、J2EE(JSP)にこのデータを入力してみたが、いずれも値を読み取ることはできなかった。一方、PerlのCGIモジュールでは、POSTDATAという名称のデータとして、POSTデータ全体を読み取ることができた。Perl以外の場合でも、Webサーバーにデータ自体は到達しているのだから値を利用する手段はあるかもしれないが、私が調べた範囲では分からなかった。

上記の結果として、AjaxでPOSTメソッドを扱うには、標準的でない方法を用いる必要があるわけだが、その方法を検討してみた。

  1. POSTをあきらめてGETを使う
  2. Perlのように、text/xml形式のデータを読み出せる言語を選択する
  3. DOMにより、IFRAME内にFORMを作成してSUBMITする

このうち、上記1.については、セキュリティ上の問題が発生し得る。Ajaxのセキュリティ対策として、「GETメソッドを拒絶する」という方法があるからだ。具体的には、SCRIPT要素を使ってSame Origin Policyを回避してAjaxデータを読み出す手法に対抗して、SCRIPT要素では必ずGETメソッドになることから、POSTのみを許容することで対策するという方法だ。既存のアプリケーションがこのような手法によりセキュリティ対策されている場合に、安易にGETメソッドを許容してしまうと、セキュリティホールが混入することになりかねない。

3.については、私が試した範囲ではうまくいっていない。JavaScriptからIFRAME内のFORM操作がうまくいかないのだ。これも、ひょっとすると制限を掛けているのかもしれない。アドホックな方法ならありそうだが、まだ十分に検証できていない。

一つの疑問は、このような情報がインターネット上に見あたらないことだ。AjaxでPOSTメソッドが使えないというのはとんでもないことだが、検索してもそのような情報がみあたらないのだ。iモードブラウザ2.0のAjaxを誰も使っていないのか、それとも事業者が恐ろしくて口をつぐんでいるのか。

しかし、このような技術情報が流通していかない限り、iモードブラウザ2.0のJavaScriptを使ったコンテンツは普及していかないだろう。NTTドコモにはさらなる情報開示を期待したいし、ケータイWebの開発者にも、もっとブログなどでの情報公開を期待する。

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

Before...

_  [記事とは関係ないコメントですいません http://nat-q.jp/ctg3/q_detail.php?no=1..]

_ 徳丸浩 [姫さん 質問を見てみましたが、これはご本人がドコモショップに相談に行くのがよいと思います。あくまで想像ですが、のっ..]

_ 徳丸浩 [コメントいただいた皆様、ありがとうございました。 さすがに、言語毎に方法はあるのですね。 ですが、あまり知られていな..]


2009年10月19日 [Perl][PHP][SQL]

_quoteメソッドの数値データ対応を検証する

このエントリでは、PerlのDBI、PHPのPDOMDB2にて用意されているquoteメソッドが数値データをどのように扱えるかを検証しました。結論としてMDB2が合格、それ以外は不合格で、とくにDBD::mysqlを使用した場合、脆弱性といってもよいような結果となりました。

概要

DBI、PDO、MDB2は、いずれもデータベースアクセスを抽象化したモジュール(クラス)であり、汎用的な記述によりさまざまなデータベースを利用できるように工夫されています。これらモジュール(クラス)にはquoteというメソッドが用意されています。DBIのquoteメソッドの呼び出し例を示します。

my $dbh = DBI->connect('DBI:mysql:dbname:localhost', 'user', 'pass');
print $dbh->quote("a\\'");    # 「a\'」という文字列を指定

【処理結果】
'a\\\''

ごらんのように、あるいはquoteという名称が示すように、これらメソッドは入力データをSQL文字列リテラルとしてエスケープした上で、シングルクオートでくくります。SQLの文字列リテラルのエスケープ方法はデータベースソフトウェア依存であり、MySQLの場合はシングルクオートとバックスラッシュをエスケープしますが、標準SQLではシングルクオートのみです*1。quoteメソッドを利用することにより、データベースの種類を自動的に考慮して、SQLを安全かつ簡便に動的組み立てできます。

さて、quoteメソッドには省略可能な第二引数があり、データの型を指定できるようになっています(デフォルトは文字列型)。以下に、整数型を指定した場合の呼び出し方を示します。

DBI:  $dbh->quote($n, SQL_INTEGER)
PDO:  $dbh->quote($n, PDO::PARAM_INT)
MDB2: $dbh->quote($n, 'integer')

ごらんのように、呼び出し方はほとんど同じです。では、この処理結果は、どうなる *べき* でしょうか。私の過去の日記「数値項目に対するSQLインジェクション対策のまとめ」や「SQLの暗黙の型変換はワナがいっぱい」で説明したように、SQLは本来厳格な型をもった言語であり、数値リテラルをシングルクオートで囲むことは好ましくありません。したがって、quoteメソッドに数値型を指定した場合は、たんなる数値が返るべきだと考えます(SQL組み立てが目的なので、型は文字列型でもよい)。そこで、入力データのサンプルとして「1a\'」を第一引数に、整数型指定を第二引数としてquoteメソッドを呼び出してみました。

DBI:  $dbh->quote("1a\\'", SQL_INTEGER)
PDO:  $dbh->quote("1a\\'", PDO::PARAM_INT)
MDB2: $dbh->quote("1a\\'", 'integer')

結果は以下のようになります。PDOとMDB2は、MySQLとPostgreSQLで同じ結果です。

モジュール名 結果
DBI(DBD::mysql) 1a\'
DBI(DBD::PgPP) '1a\\\''
PDO '1a\\\''
MDB2 1 (int型)

先に書いた「あるべき結果」になったのはMDB2だけでした。PDOとDBD::PgPPは、文字列型として指定したのと同じ結果であり、SQLインジェクション対策として機能しますが、なんのためにわざわざ整数型を指定したのかわかりません。DBD::mysqlはエスケープもクオートもしていないので、入力として「1 union select table_name from information_schema.tables」を与えられると、一覧表を表示する画面でテーブルの一覧が表示させられるような結果となります。これはまずい。現実問題として、何もしないのでは、わざわざquoteをメソッドを呼ぶ動機がありません。

また、文字列としてエスケープする方法も、先のエントリで説明したように、さまざまな副作用がありますので、quoteメソッドを使うよりは、SQL呼び出し直前でバリデーションするか、sprintfで%dの書式を与えることで、数値のみが出力されるように制限する手法が考えられます。

今回の調査で、MDB2のquoteメソッドは、わりといい線行っていると思いました。MDB2のquoteでは、'decimal'という指定もできるのですが、20桁程度の数値を与えても、破綻なく動作します。

入力 出力
123456789012345678901 (文字列型) 123456789012345678901 (文字列型、クオートなし)
1a2b3 (文字列型) 123 (文字列型、クオートなし)

'1a2b3'に対する結果が「123」となるところ、「サ、サニタイズかよ」と思ってしまいますが、まぁ数値としてのバリデーションは入り口でやるとして、SQL発行時の「最後の砦」としては、まぁ許容できる範囲ではないかと思います。「例外を発生させるべきじゃないの?」と思うのですが、他のモジュールのだめさ加減との比較では上出来でしょう。

なお、DECIMAL型の場合に、文字列型で結果を返すのは妥当な処理です。PHPに標準で用意されているintegerやfloatでは、DECIMALの桁数(MySQLでは最大65桁、PostgreSQLでは最大1,000桁)を表現するのに十分なビット長がないからです。演算はどうするのだという疑問がありそうですが、DBの性能に余裕があればSQLで演算するという手もありますし、BCMath 任意精度数学関数を使う手もあります。BCMathは文字列型のままで数値演算ができます。

まとめ

DBI、PDO、MDB2のquoteメソッドについて、第二引数明示により数値として処理する内容について検証しました。期待どおり動作するのはMDB2であり、一方DBD::mysqlではなにもしていないので脆弱性対策としては使えないことがわかりました。

また、今回のエントリでは示していませんが、PDOは処理が高速なために人気があるものの、内部処理をつっこんで見ていくと、不安が出てきました。その内容については別途何かの形で発表したいと思いますが、現時点では、id:moriyoshiさんの以下のエントリが参考になると思います。

参考: PDOとプリペアードステートメント

*1 詳細については、「 SQLのエスケープ再考 」および「SQLエスケープにおける「\」の取り扱い 」に詳しく書きましたので参考になさってください


2009年10月14日 [php]

_htmlspecialchars/htmlentitiesはBMP外の文字を正しく扱えない

PHPの安定版(PHP5.3.0、PHP5.2.11)のhtmlspecialcharsおよびhtmlentitiesには、Unicodeの基本多言語面 (BMP)範囲外の文字、すなわち、U+10000以降の文字を正しく扱えない問題があります。

もっともシンプルな再現コードを以下に示します。

<?php
  $c = "\xF0\x90\x80\xBC"; // U+1003C
  $a = htmlspecialchars($c, ENT_QUOTES, 'UTF-8');
  echo bin2hex($a) . ':' . $a;

【処理結果】
266c743b:&lt;

U+1003Cは、Wikipediaの説明によると、大昔のギリシャの「線文字B」を表すそうで、小なり記号とは関係ないので、本来そのまま出力しなければならないものです。htmlspecialcharsおよびhtmlentitiesの内部処理で、コードポイントの下位16ビットしかみていないようで、このような結果となります。

線文字Bを扱う人口は少ないと思われますので、もう少し身近な例を探してみました。

<?php
  $c = "\xF0\xA2\x89\xBF";  // U+2227F(𢉿 …マダレに馬という字)
  $a = htmlentities($c, ENT_QUOTES, 'UTF-8');
  echo bin2hex($a) . ':' . $a;

【処理結果】
26736373696d3b:&scsim;

𢉿は、京都府長岡京市の地名で𢉿子ケ岳(からねがたけ)に使われている文字です(参考:稀少地名漢字リストGoogle Map)。一方、&scsim;はSGMLの文字実体参照のマッピングに出てくる記号で、≿(カーブした大なり記号の下に~)を表します。この実体参照形式はFireFoxなどのブラウザでは表示できませんが、そのような文字実体参照に変換される経緯は、id:moriyoshiさんの「PHPのhtmlentities()で (HTML4.0的に) 余計に実体参照に変換されてしまう文字の一覧」に説明されています。

BMP外の文字を扱う機会は少ないとは思いますが、正常系のデータが正しく扱えないという意味では、先の文字エンコーディングのチェック不備よりも重い問題だと考えます。幸い、id:moriyoshiさんが先に気づかれて、文字エンコーディングの問題と合わせて修正されていますので、おそらくPHP5.3.2からは修正されるものと思われます。PHPの最新のスナップショットにて修正ずみであることを確認しています。

影響を受けるケース

この問題を受けるのは、文字エンコーディングとしてUTF-8を使用している場合に、BMP範囲外の文字が与えられた場合です。

対策

PHP側の対応が完了するまでの間は以下のようにすればよいと思います。htmlentitiesよりはhtmlspecialcharsの方が影響が少ないこと、通常htmlentitiesを使う理由はない(参考:htmlspecialcharsと不正な文字の話 )ことから、htmlspecialcharsを使った上で、影響のある文字を扱わなければならない場合は個別に手当するしかないでしょう。Unicode5.1の範囲で、htmlspecialcharsにより不正に変換される文字は、以下の13種です。htmlentitiesを使用すると616種に増えます。𢉿もその一つです。

U+10022 線文字B音節文字
U+10026 線文字B音節文字
U+1003C 線文字B音節文字
U+20022 𠀢
U+20026 𠀦
U+20027 𠀧
U+2003C 𠀼
U+2003E 𠀾
U+E0022 言語タグ
U+E0026 言語タグ
U+E0027 言語タグ
U+E003C 言語タグ
U+E003E 言語タグ

個別に対応する方法を考えてみましたが、簡単な方法は思いつきません。いったん他の文字列に置き換えておいて、htmlspecialcharsの処理結果から、元の文字に戻す方法があると思いますが、面倒な処理になります。あるいは、影響を受ける文字を数値文字参照(&#x20022;など)に変換しておいて、htmlspecialcharsの第四パラメータ$double_encodeを0にして実行する方法もありますが、$double_encodeを0にする副作用もあります。元々実体参照や数値文字参照の形になってる文字列を変換しなくなるからです。このため現状では上記問題を許容した上で、PHP側で対応されるのを待った方がよい場合が多いような気がします。

まとめ

PHPのhtmlspecialcharsおよびhtmlentitiesには、Unicodeの基本多言語面 (BMP)範囲外の文字を正しく扱えません。次バージョン(PHP5.2.12、PHP5.3.2)では修正されると思われます。それまでの間は以下の方法で対処可能です。

  • htmlentitiesではなくhtmlspecialcharsを使用する
  • htmlspecialcharsでも影響を受ける13文字は個別に対応する、あるいは許容する


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

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

最近の記事

最近のツッコミ

  1. らんさん (04-09)
  2. 徳丸浩 (04-15)
  3. 徳丸浩 (02-06)
Google