2011年10月12日水曜日

属性型JPドメインと地域型JPドメインに対するCookie Monster Bug調査

属性型JPドメインと地域型JPドメインに対するCookie Monster Bugについて調査した結果、複数のブラウザにおいて、これらドメイン名に対するCookie Monster Bugがあることがわかりました。
Cookie Monster Bugによる典型的な影響は、セッションIDの固定化攻撃を受けやすくなることです。
対策として、全てのサイトにおいて、Webアプリケーション側でセッションIDの固定化対策(後述)を実施することを推奨します。

調査結果

第1分類 第2分類 属性型JP
ドメイン
地域型JPドメイン
第2レベル 第3レベル






ケータイWeb iモード
Ezweb × ×
Softbank(1) × ×
Softbank(2)
PCサイト

ビューア
docomo
au × ×
Softbank(1) × ×
Softbank(2)
Android 標準 × ×
Opera × × ×
Firefox
iOS 4.3.5
PC IE 6~9 × ×
Firefox ~2.0.0.20 × × ×
3~
Google Chrome 14.0.835.202
Safari ~3.2.3 × × ×
4.0~
Opera 5 × ×
6~11.0 ×
11.1~11.51 × × ×

※凡例
  • 地域型JPドメインの第2レベルとは、tokyo.jpなど第2レベルのドメイン名でCookieが発行できるもの
  • 地域型JPドメインの第3レベルとは、chiyoda.tokyo.jpなど第3レベルのドメイン名でCookieが発行できるもの
  • 背景が赤のセルは現バージョンでバグのあるもの。無色は旧バージョンの参考情報
  • 携帯電話に関しては機種毎に仕様が異なる可能性が高く、全機種について調査したわけではないので、上記はあくまで抜き取りでの結果であることに注意されたし

※確認機種、バージョン等
  • iモード:P-07A(iモードブラウザ2.0)で確認
  • EZweb:W52T、biblioで確認(結果は同じ)。詳細は後述
  • Softbank(1): 821N, 932SHで確認。これらは非公式JavaScript対応機
  • Softbank(2): 944SH(公式JavaScript対応機)で確認
  • Android:Xperia Arc(Android2.3.3)にて確認
  • iOS:iPadで確認
  • Google Chromeは最新版(14.0.835.202)でのみ確認

調査方法

携帯電話(俗に言うガラケー)は試験環境でのテストができないため、インターネット上に環境を作り、調査しました。具体的には、筆者およびHASHコンサルティング(株)の所有する地域型JPドメインtokumaru.bunkyo.tokyo.jpおよび属性型JPドメインhash-c.co.jp上で発行するCookieを用いて調査しました。

Cookie Monster Bugが再現することを調査するためには、上記とは別のドメイン名で動作するサイトがあると確実です。このため、下記サイトの協力を得て調査を行いました。
  • tricorder.co.jp : 株式会社トライコーダ上野宣氏にご協力いただいた
  • otsuka.bunkyo.tokyo.jp : 左記サイト管理人の上田氏にご協力いただいた
上野氏と上田氏のご協力に深く感謝申し上げます。

EZwbbについての補足

現状のEZwebでは、HTTP通信の場合、ゲートウェイ(EZサーバー)上にてCookieが保管されます。今年の秋冬モデル以降の機種では、Cookieは常に端末に保存されるようになるようです(下記参照)。


このゲートウェイのdomain属性の挙動を調査した結果、独特の仕様であることが分かりました。以下、例示により説明します。

Webサイト http://www.example.co.jp/ において、以下をdomain属性としてCookieを発行する場合。

domain=co.jp
domain=.co.jp
domain=example.co.jp
domain=.example.co.jp
domain=www.example.co.jp

この際、有効となるCookieは、domain属性が .example.co.jp と www.example.co.jpのもののみです。また、このCookieは、example.co.jpドメインのサブドメインのうち、4レベルのサブドメイン(Cookieを発行したホストと同じレベルのドメイン)のみが受け取れます。すなわち、受け取れるホストを例示すると以下の通りです。

Cookieを受け取れるホスト
  www.example.co.jp
  home.example.co.jp  (domain=.example.co.jp のCookieのみ)
  a.example.co.jp  (domain=.example.co.jp のCookieのみ)

Cookieを受け取れないホスト
  example.co.jp
  hohe.www.example.co.jp

ここまでであれば、EZwebのCookieの仕様が独自というだけで、Cookie Monster Bugではありません。しかし、以下の例はCookie Monster Bugです。

例1: example.co.jpで発行された domain=.co.jp のCookieは、hash-c.co.jpにも tricorder.co.jp にも送信される。www.hash-c.co.jpやwww.tricorder.co.jpには送信されない。

例2:tokumaru.bunkyo.tokyo.jpで発行されたdomain=.bunkyo.tokyo.jpのCookieは city.bunkyo.tokyo.jpやotsuka.bunkyo.tokyo.jpには送信される。www.city.bunkyo.tokyo.jpやwww.otsuka.bunkyo.tokyo.jpには送信されない。

結果として、EZwebのゲートウェイのCookie処理にはCookie Monster Bugがありますが、ドメイン名そのものをホスト名とするサイトのみが影響を受けます。サブドメイン(www.example.co.jpなど)をホスト名として運用していれば、影響は受けません。

また、第1レベルのドメイン(TLD)について、domain指定することはできません。例えば、domain=.jpと指定したCookieは無効となります。

Operaについての補足

Operaは独自の方法でCookie Monster Bugに対応していると言われていました(参考:金床著「ウェブアプリケーションセキュリティ」)が、調査してみると、全てのバージョン(バージョン5以降)でCookie Monster Bugがあることがわかりました。不思議なことに、Operaバージョン6~11.0では、ごく限定的なパターン(3レベルの地域型JPドメイン)のみCookie Monster Bugがあったのに、バージョン11.10~11.51(本稿執筆時点の最新版)では、属性型JPドメインと、2レベルの地域型JPドメインのCookie Monster Bugが追加されてしまっています。

しかも、Opera11以降で導入されたアドレスバー上のドメイン強調表示を見ると、属性型JPドメイン、地域型JPドメインとも正しいドメイン名を強調表示しています。それにもかかわらず、Cookie Monster Bugがある原因は不明です。

現在、属性型JPドメインでCookie Monster Bugがあるブラウザは、EZwebのゲートウェイをのぞくと、全てOperaです。auのPCサイトビューアもOperaでした。

影響

Cookie Monster Bugによる典型的な影響はセッションIDの固定化攻撃(Session Fixation Attack, CWE-384)の影響を受けやすくなることです。WebアプリケーションにセッションID固定化の脆弱性がある場合、「なりすまし」の結果、以下の影響があります。
  • 利用者の重要情報(個人情報、メールなど)の閲覧
  • 利用者の持つ権限での操作(送金、物品購入など)
  • 利用者のID によるメール、ブログなどへの投稿、設定の変更など

サイト側対策

全てのサイト(Cookie Monster Bugの有無に関わらず)に対して以下を推奨します。
  • セッションIDとトークン以外をCookieとして保存しない
  • ログイン成功後にセッションIDを変更する、あるいはトークンによる対策を行う
  • セッションID以外をCookieに保持している場合、外部サイトから値をセットされた場合の影響を分析し、必要な対処を行う*1

*1:「体系的に学ぶ 安全なWebアプリケーションの作り方」では、セッションID以外にCookieに保存する例として、セッションID固定化対策、Cookieのセキュア属性不備対策に用いるトークンがありますが、これらのトークン(Cookie)に関してはCookie Monster Bugのセキュリティ上の影響はありません。

詳しくは以下を参照ください。

想定問答集


Q1:Cookie Monster Bugはブラウザの脆弱性なのか
A1:判断の分かれるところですが筆者はブラウザの脆弱性であると考えます

ブラウザ等インターネット対応のソフトウェアの処理内容は、RFCの規定に従うことになっています。Cookieの規定は、元々はNetscape社の仕様(archive.orgで参照有限会社futomiによる参考訳)があり、その後RFC2109(1997年)、RFC2965(2000年)が発行されましたが、実際にはこれらのRFCは普及せず、Netscape社のオリジナル仕様が広く使われていました。このため、現在広く使われているCookieの利用形態を文書化するような形で、RFC6265が今年の4月に発行されました。このRFC6265には、Cookie Monster Bugに関連する記述として、4.1.2.3. The Domain Attribute と 5.3. Storage Model があります。このうち、4.1.2.3の末尾を以下に引用します。
NOTE: For security reasons, many user agents are configured to reject Domain attributes that correspond to "public suffixes".  For example,  some user agents will reject Domain attributes of "com" or "co.uk".
参考訳:
セキュリティ上の理由から、多くのユーザエージェント(注:ブラウザのこと)は、public suffixと一致するDomain属性を拒否するように構成されます。 たとえば、いくつかのユーザエージェントは、「com」または「co.uk」のDomain属性を拒否します。
public suffix(effective top level domain; eTLD)については、高木浩光氏のエントリ「JPRSに対する都道府県型JPドメイン名新設に係る公開質問」の中に説明があります。上記の記述にあるように、Cookie Monster Bugへの対処はRFC6265上のmustやshouldの規定ではなく、参考情報のような扱いになっています。したがって、RFC6265を根拠として、Cookie Monster Bugが脆弱性であると判定することはできません。

しかしながら、Cookie Monster BugによるセッションID固定化の主要因は、Cookie Monster Bugそのものにあると考えられ、ブラウザ側で対処しないので、やむを得ずWebアプリケーション側で対処している状況と筆者は理解しています。

すなわち、Cookie Monser Bugはブラウザの脆弱性ではあるが、歴史的には、Netscape/Firefox、Safriに当該問題があり、現在でもIEでは完全に対処していないことは広く知られており、やむを得ずWebアプリケーション側で対応している、というのが筆者の理解です。



Q2:セッションIDの固定化以外の脅威はないの?
A2:セッションIDの固定化以外にも若干の問題があります

最近のブラウザには、URL中のドメイン名を強調表示する機能があります。これはフィッシング詐欺に対する保護機能として提供されるものです。IE9の該当機能の説明を引用します(ドメインの強調表示 - Microsoft Windowsより)。

詐欺的な Web サイトを回避する方法の 1 つに、アクセスしようとしている Web サイトのアドレスを正しく理解することが挙げられます。 Internet Explorer 9 にはドメインの強調表示機能があり、アドレス バーでドメイン名を強調表示することで正しい URL かどうかがひとめでわかります。 これにより、偽の URL でユーザーをだまそうとする詐欺的なサイトを注意でき、個人情報が漏えいする可能性を減らすことができます。

しかし、地域型JPドメインの場合、この機能は誤動作します。以下は大田区のホームページと筆者の所有する地域型JPドメインのIE9でのアドレスバーの表示です。

大田区ホームページのアドレスバー表示


tokumaru.bunkyo.tokyo.jpのアドレスバー表示






どちらもtokyo.jpが強調表示されていますが、これは誤りで、4レベルを強調表示するべきです。以下にOpera11.51での表示例を示します。


大田区ホームページのアドレスバー表示




tokumaru.bunkyo.tokyo.jpのアドレスバー表示





前述のように、Opera11.51には属性型JPドメインと地域型JPドメインのCookie Monster Bugがありますが、アドレスバーの強調表示は正しく行われています。なぜそうなっているかは不明です。

IEのケースに戻ると、ドメイン名の強調表示が誤っているため、フィッシング詐欺対策としてはかえって逆効果になると考えられます。意図的に、city.xxxx.xxxx.jpと紛らわしいドメイン名(例えば、cify.xxxx.xxxx.jp)を取得することで、地方公共団体のホームページと紛らわしいサイトを運営することができます。
さらに、今後JPRSが提供を予定している都道府県型JPドメインであれば、実在しない市名により、city.xxxx.xxxx.jp(xxxx.xxxxは市名と紛らわしいドメイン名)の運用が可能になると考えられます。アドレスバーのドメイン名強調表示は、このような詐欺行為に対して有効なはずですが、以下の理由により、有効に機能しないと予想されます。
  • 長年地域型JPドメインに対応していないIEが、さらに複雑なルールの都道府県型JPドメインに直ちに対応するとは考えにくい
  • 対応ブラウザであっても、ドメイン名のルールがややこしすぎて一般市民には理解困難
また、通常あり得ない想定ですが、理論的にはJavaScriptの同一生成元ポリシー(Same Origin Policy)に対する影響も考えられます。同一生成元ポリシーはデフォルトではホスト名の一致を要求しますが、document.domainの設定により、ドメイン(eTLD)の範囲内で制限を緩めることができます。このため、攻撃対象サイト内でdocument.domain="tokyo.jp"; が実行されている場合、このページをiframeの内部に表示させ、iframek外側から内容を読み取ることができます。ただし、サイト運営者がわざわざそのように設定する動機がないことから、この問題の影響はないか、極めて限定的であると考えられます。

同一生成元ポリシーの解説は、「体系的に学ぶ 安全なWebアプリケーションの作り方」を参照ください。


Q3:属性型JPドメインは使わない方がよいのか
A3:どのタイプのドメイン名を利用するかは、Cookie Monster Bugの有無だけではなく総合的に判断する必要があり、下記理由から属性型JPドメインの利用を推奨します

産総研が公表している「産総研 RCIS: 安全なWebサイト利用の鉄則」には、利用者をフィッシングの手口から守るためのドメイン名に対する要件として、以下を挙げています。
  • サービス提供者が保有するドメイン名を使う
  • まぎらわしくないドメイン名を使う
  • ドメイン名を利用者に周知する
属性型JPドメインは、上記を満たしやすい特性を持ちます(参考:JPドメイン名の種類 - 種類と対象 - / JPRS)。
従って、属性型JPドメインに対するCookie Monster Bugが一部のブラウザに存在するというだけの理由から属性型JPドメインを避けることはバランスに欠いた判断であり、属性型JPドメインを選択した上で、セッションID固定化対策をするのがよいと考えます。


Q4:地域型JPドメインは使わない方がよいのか
A4:Webサイトを運用するドメイン名としては、特別な理由がない限り地域型JPドメインは使わない方がよいと考えます

Cookie Monster Bugの対処はWebアプリケーション側で可能とはいえ、セッションIDの固定化脆弱性はまだあまり知られていない脆弱性であり、未対処のサイトが多いと予想されます。そのような状況下で、地域型JPドメインを積極的に利用する理由は見あたりません。属性型JPドメインのように、組織を識別しやすい特性があるわけでもないし、Cookie Monster Bugのあるブラウザの種類も増えてしまいます。また、前述のように、IEではドメイン名の強調表示が誤動作します。

このような状況下では、地域型JPドメインでのWebサイトの運用は避けた方が賢明であると考えます。


Q5:都道府県型JPドメインについての所見は?
A5:地域型JPドメインと同じ理由から、Webサイトのドメイン名としては避けた方がよいと考えます

現状のブラウザの実装状況から判断して、都道府県型JPドメインの登場は、現在の状況をさらに混乱させると予想します。そのような状況下で、Webサイトを運営するドメイン名として都道府県型JPドメインを選択することは当面避けるべきと考えます。


Q6:Cookie Monster Bugはドメインの問題ではなくて、Cookie仕様やブラウザの実装の問題ではないのか?
A6:その通りです

Cookie Monster Bugは前述のようにブラウザのバグであり、Cookieの仕様(RFC6265)に起因するものです。長期的には、これらが改善されるべきであると筆者は考えます。

しかしながらRFC6265は、RFC2965の発行から10年以上経ってようやく発行されたこと、長い間IEの地域型JPドメインのCookie Monster Bugが改善されていない実績などから、今後すぐにCookieの仕様やブラウザのCookie実装が改善される見通しに関しては悲観的に考えざるを得ません。

筆者はWebアプリケーションのセキュリティを専門とするコンサルタントであり、現在のWebの仕様や関連ソフトウェア(Webサーバー、開発言語、ブラウザ等)の状況を受け入れた上で、Webアプリケーションを安全にする方法を示すことが使命と考えます。この文書は、そのような立場で書いています。

地域型JPドメインや(将来の)都道府県型JPドメインを避けた方が良いというのは、あくまでも現状のブラウザにおけるCookieの実装を是認するという想定での話です。例えば、メールサーバーのドメイン名としてこれらドメイン名を用いる場合や、WebやCookieにとらわれない新しいテクノロジーを用いる場合は、上記は該当しません。


Q7:ケータイWebではCookieを使わない方が良いのか
A7:Cookie Monster Bugの存在に関わらず、今後はケータイサイトもCookieによるセッション管理に移行すべきと考えます。

Cookie以外のセッションIDの埋め込み場所としては、URLやケータイIDがありますが、いずれもセキュリティ上の課題があり、その解決は容易ではありません。これに対して、Cookie Monster Bugの対応は比較的容易です。

また、従来Cookieの使えないiモードブラウザ1.0の存在がCookie対応を難しくしていましたが、最近になって状況が変わってきました。最近のiモードブラウザ1.0のシェアを「モバイルサイトの3キャリア共通CSSと最新コーディング事情 - livedoor ディレクターブログ」から、調べてみます。
このエントリによると、ケータイWebのアクセスシェアでiモードは46.4%、そのうちiモードブラウザ1.0のシェアは30.9%ということです。これにより、ケータイWeb中のiモードブラウザ1.0のアクセスシェァは、約14%となります。ケータイの世界でも、そろそろCookie対応機種にのみ対応することを検討してもよい状況になってきたと言えます。

14%を切り捨てるのが忍びない場合でも、Cookieの使えない場合のみURLにセッションIDを埋め込むこともできます。詳しくは以下を参考にしてください。


Q8:Cookie Monster BugによりCookieの盗聴はできるの?
A8:できません


Q9:余計なCookieの削除はしなくてよいの?
A9:余計なCookieの削除は必須ではありません

Cookie Monster Bugを用いた攻撃の結果、攻撃者から注入されたCookieとアプリケーションが発行するCookieが同じ名前で複数登録された状態になります。これに対して、余計なCookieを削除するべきかどうかという質問です。

セッションIDの固定化攻撃への対策としては、余計なCookieを削除は必須ではありません。しかし、同名Cookieがブラウザから送られることにより、アプリケーションが誤動作する可能性はあります。詳しくは以下の参考サイトをご覧ください。

この対処としては、余計なCookieの削除の他に、複数のCookie(セッションIDなど)を検知して、もし複数の同名Cookieがあれば、エラーとして処理を停止する実装も考えられらます。すなわち、同名Cookieについては以下の3種類の実装が可能です。
  • 何もしない(セッションIDの振り直は行う)
  • 認証成功時に余計なCookieを削除する
  • 同名Cookieを検知してエラーとする
同名Cookieを削除してしまうと、セッションIDの固定化攻撃が行われていても、攻撃を見逃して正常処理を継続してしまうことになります。これに対して、同名Cookieを検知してエラーにすると、攻撃(の可能性)を検知できます。

これら実装には一長一短があるため、サイト運営者の判断により仕様を決定することになります。


Q10:私の管理するサイトにセッションIDの固定化脆弱性があるかどうすれば分かりますか?
A10:ログイン前後でセッションIDが変化するかどうかで判定できます

この問題を含めてWebアプリケーションの簡易的な脆弱性検査の目的に「ウェブ健康診断仕様」が利用できます。仕様書は財団法人地方自治情報センターのサイトからダウンロードできます。セッションIDの固定化は、同仕様書K-1に記述されています。


Q11:色々ややこしくてよく分からないんだけど、私のサイトが問題ないか調べてもらえませんか?
A11:未承諾広告※個別サイトの問題についてはHASHコンサルティング株式会社のサービスがご利用いただけます

HASHコンサルティング株式会社では、上記のような調査結果をブログや寄稿記事、勉強会における講演等の形で広く公開しております。これらの情報は無料でご利用頂けます。
一方、サイト個別の特性に応じた調査が必要な場合や、セキュリティ担当者がいないなど、公開した情報だけでは個別の問題に対して判断ができない場合は、HASHコンサルティング株式会社の有償サービスがご利用頂けます。問い合わせはこちらの問い合わせフォームからお願い致します。

[PR]

「体系的に学ぶ 安全なWebアプリケーションの作り方」のDRMフリーPDFによる電子版が販売開始しました。今なら、キャンペーン価格¥1,800-(36%OFF)でお求め頂けます(10月17日まで)。購入はこちらから。

2011年9月27日火曜日

都道府県型JPドメインがCookieに及ぼす影響の調査


JPRSからのプレスリリース『JPRSが、地域に根ざした新たなドメイン名空間「都道府県型JPドメイン名」の新設を決定』や報道などで「都道府県型JPドメイン」というものが新設されることを知りました。
都道府県型JPドメインとは、現在活発に使われていない地域型ドメインを活性化する目的で、地域型ドメインの制約(ドメイン名が長い、一人・一団体あたり1つまで)を簡略化しようというもののようです。
しかし、現在の地域型ドメインは、ブラウザにとって処理がややこしいもので、IEなどは昔からまともに対応していません。このため、Cookie Monster Bugという脆弱性になっているという経緯があります。このルールをさらに複雑にすることになるということから、ブラウザセキュリティに関心の高い人たちが騒ぎ始めています。

そこで、高木浩光氏の日記「JPRSに対する都道府県型JPドメイン名新設に係る公開質問」の以下の部分に関して、調査をしてみました。
何もしなければ、「都道府県型JPドメイン名」の登録が始まっても、cookieを利用できないなどの欠陥ドメイン名となることが予想される。
調査は、「何もしない」状態、すなわち現在のブラウザで、都道府県型ドメインをシミュレートして、Cookieの受け入れられる状態を確認しようというものです。

まず、ドメインとしては、地域型ドメイン toku.tsu.mie.jp と、都道府県型JPドメイン toku.mie.jp の2種類を実験環境に用意しました。

現在の地域型ドメインは、原則として4レベルのドメインになります。但し、都道府県のドメイン(pref.mie.jp等)は例外です。これに対して、都道府県型JPドメインは3レベルのドメインになることがウリです。都道府県型ドメインの「ややこしさ」とは、この4レベルと3レベルのドメインが混在することで、その切り分けには、市町村の名前全て(tsu.mie.jpなど)をブラウザ側に保持する必要が生じることです。

ホスト名(FQDN)としては、ドメインそのものと、www.で始まるもののの2種類を用意しました。すなわち、ホスト名は以下の4種類です。

地域型ドメイン(toku.tsu.mie.jp)に属するもの
  • toku.tsu.mie.jp
  • www.toku.tsu.mie.jp
都道府県型JPドメイン(toku.mie.jp)に属するもの
  • toku.mie.jp
  • www.toku.mie.jp
これらホストに対して、以下の5種類のdomain属性を持つCookieをセットしてみて、どのCookieが受け入れられるかを調べました。
  • mie.jp
  • tsu.mie.jp
  • toku.mie.jp
  • www.toku.mie.jp
  • toku.tsu.mie.jp
  • www.toku.tsu.mie.jp
まず、Firefox 6.0.1での結果を下表に示します。

Firefox 6.0.1
Cookie 地域型ドメイン 都道府県型JPドメイン
toku.tsu.mie.jp www.toku.tsu.mie.jp toku.mie.jp www.toku.mie.jp
mie.jp
tsu.mie.jp
toku.mie.jp ×
www.toku.mie.jp
toku.tsu.mie.jp
www.toku.tsu.mie.jp

ホスト www.toku.mie.jpに対して、domain=toku.mie.jpのCookieが受け入れられていません。これは、現在の地域型ドメインのルール(4レベルのドメイン)に則したものです。
一方、ホストtoku.mie.jpに対して、domain=toku.mie.jpのCookieが受け入れられているのは規約上微妙な気がします。そのような地域型ドメインのホストは現在の仕様上はないはずですが、現にホストが存在していることを優先して、Cookieを受け入れているのでしょうか。

次に、Chrome 14.0.835.168 および 15.0.874.24 beta-m での結果を示します。どちらも同じ結果です。

Chrome 14.0.835.168 15.0.874.24 beta-m
Cookie 地域型ドメイン 都道府県型JPドメイン
toku.tsu.mie.jp www.toku.tsu.mie.jp toku.mie.jp www.toku.mie.jp
mie.jp
tsu.mie.jp
toku.mie.jp × ×
www.toku.mie.jp
toku.tsu.mie.jp
www.toku.tsu.mie.jp

Firefoxの場合と1箇所異なっています。domain=toku.mie.jpのCookieはいかなる場合にも受け入れられていません。これは現在の地域型ドメインの仕様を重視した結果と思われます。
その結果、高木浩光氏の言われる「cookieを利用できないなどの欠陥ドメイン名と」なっています。現状のChromeの仕様が仮にこのままだとすると、都道府県型JPドメインであっても3レベルのままでは使用できず、www.などのサブドメインをつけなければ使用できないことになります。

続いて、IE9での結果です。

IE9 (9.08112.16421)
Cookie 地域型ドメイン 都道府県型JPドメイン
toku.tsu.mie.jp www.toku.tsu.mie.jp toku.mie.jp www.toku.mie.jp
mie.jp ○(cm) ○(cm) ○(cm) ○(cm)
tsu.mie.jp ○(cm) ○(cm)
toku.mie.jp
www.toku.mie.jp
toku.tsu.mie.jp
www.toku.tsu.mie.jp

よく知られているように、IEは地域型ドメインに対してCookie Monster Bugがあります(cmをつけた箇所)。Cookie Monster Bugとは、本来受け入れてはならない上位のドメインについてCookieを発行できるバグのことです。
現在の地域型ドメイン、例えばtoku.tsu.mie.jpでサイトを運営していると、mie.jpドメインでのCookieが発行できるわけですから、これはpref.mie.jp(三重県のドメイン)やcity.tsu.mie.jp(津市のドメイン)にも適用されるCookieが発行できることになります。これにより、セッションIDの固定化攻撃がやすやすくなるなどの影響かあります。
都道府県型JPドメインの運用が始まった後のIEの対応はわかりませんが、長年地域型ドメインのCookie Monster Bugを放置しているという実績から考えると、都道府県型JPドメインにすぐさま対応するは思えません。

次に、Safariでの結果ですが、これはFirefoxと同じパターンです。

Safari 5.0.2(7533.18.5)
Cookie 地域型ドメイン 都道府県型JPドメイン
toku.tsu.mie.jp www.toku.tsu.mie.jp toku.mie.jp www.toku.mie.jp
mie.jp
tsu.mie.jp
toku.mie.jp ×
www.toku.mie.jp
toku.tsu.mie.jp
www.toku.tsu.mie.jp

最後にOperaでの結果です。

Opera 11.51
Cookie 地域型ドメイン 都道府県型JPドメイン
toku.tsu.mie.jp www.toku.tsu.mie.jp toku.mie.jp www.toku.mie.jp
mie.jp ○(cm) ○(cm) ○(cm) ○(cm)
tsu.mie.jp ○(cm) ○(cm)
toku.mie.jp
www.toku.mie.jp
toku.tsu.mie.jp
www.toku.tsu.mie.jp

この結果を見て驚きました。OperaにもCookie Monster Bugがあります。最初調査方法の問題かと思い、調査方法を変えて調べてみましたが、結果は変わりません。どうも最近のOperaには地域型ドメインに対するCookie Monster Bugがあるようです(*1)。
この結果はIEと同じですので、影響もIEと同じです。

まとめ
現在のブラウザの仕様が変わらないという前提で、都道府県型JPドメインの運用が始まった際の影響を調べました。
Firefox、Chrome、Safariについては、3レベルのドメインについてCookieが受け入れられない場合があります。この結果、Chromeの場合3レベルのドメインではCookieがまったく利用できません。FirefoxとSafariでは、ホスト名そのままのCookieは利用できますが、サブドメイン間でCookieを共有できないという問題につながります。
IEとOperaについては、Cookie Monster Bugのため、Cookieを安全に利用できないという問題があります。
この結果をどう評価するかですが、私は、高木浩光氏の日記で指摘された「cookieを利用できない」という欠陥よりも、IEとOperaのCookie Monster Bugの方が気になります。とくに、IEのシェアは現在でもかなり高いため、地域型ドメインおよびその後継である都道府県型JPドメインでは、Cookieを安心して使えず、セキュリティ上の大きな障害になると考えます。

*1: 地域型ドメインと都道府県型JPドメインのシミュレーションには、DNSキャッシュサーバーにて実験用のドメインを指定する方法を最終的に用いました。

2011年9月26日月曜日

PostgreSQLは標準でバックスラッシュをエスケープしない仕様になった

PostgreSQL9.1の仕様変更にて、デフォルト時の設定として、standard_conforming_stringsがonとみなされるようになりました。この仕様変更により、デフォルト設定でのPostgreSQLは、バックスラッシュをエスケープする必要がなくなり、ISO規格のSQLと同様のエスケープルール(シングルクォートを重ねるのみ)となります。

PostgreSQLの文字列リテラルは、元々MySQL同様に、バックスラッシュをエスケープする仕様でした。その後、リリース8.1にて、設定パラメータ standard_conforming_strings が追加され、この値が on の場合、バックスラッシュをエスケープしない(ISO規格と同様の)仕様となりました。従来のリリースでは、standard_conforming_stringsを指定しない場合offとみなされていました。これは、後方互換性維持のためでしょう。

リリース8.1のドキュメントには以下のように記述されています。
standard_conforming_stringsの値は読み取りのみです。アプリケーションでは、この値を読み取ることで、どのようにバックスラッシュが解釈されるかが分かります。(また、このパラメータが存在するかどうかで、E''文字列構文がサポートされているかどうかが分かります。)今後のリリースでは、standard_conforming_stringsは真になる予定です。
ここで、E''文字列構文という用語が出てきました。これは、PostgreSQL固有の機能で、standard_conforming_stringsの設定に関わらずバックスラッシュによるC言語風のエスケープを行う文字列形式です。

ここで、standard_conforming_strings設定と、バックスラッシュのエスケープ要・不要の関係を下表にまとめました。

standard_conforming_strings設定とバックスラッシュのエスケープの関係
standard_conforming_stringsリリース9.0.4以前リリース9.1以降
offエスケープ要エスケープ要
指定無しエスケープ要エスケープ不要
onエスケープ不要エスケープ不要

表からも分かるように、PostgreSQLリリース9.1にバージョンアップして挙動が変わるのは、standard_conforming_stringsの設定をしていない場合です。この場合は、standard_conforming_stringsをoffにすることで従来通りの挙動となります。

しかし、standard_conforming_stringsの設定を変更しない場合は、既存のアプリケーションはどのような影響を受けるでしょうか。以下、PHPとPerlで記述されたアプリケーションについて、この変更の影響を検討してみます。

PHPのPostgreSQL関数(pg_xxxx)やPDOなどには、SQL文字列のエスケープ用の関数が用意されています。PostgreSQL関数ではpg_escape_string関数、PDOではquoteメソッドが該当します。これらを使用している場合、standard_conforming_stringsのオプションを自動的に反映して、適切にエスケープしてくれます。
DBI/DBDのPgやPgPPを使っている場合、quoteメソッドで文字列をエスケープしている場合も同様です。
これらの挙動を「a\'」という文字列がどうエスケープされるかによって調査しました。

standard_conforming_stringspg_escape_stringquote(PDO)  quote(Pg)quote(PgPP)
offa\\'''a\\'''E'a\\'''E'a\\\''
指定無しa\'''a\'''E'a\\'''E'a\\\''
ona\'''a\'''E'a\\'''E'a\\\''
※各モジュールのバージョン
PHP: 5.3.8
Pg: 2.18.1
PgPP: 0.08

quoteという名前のメソッド(PDO, PG, PgPP)は、いずれもエスケープするだけでなく、シングルクォートで文字列を囲むことにご注意下さい。
上表から、これらの関数・メソッドを使っていれば、文字列は正しくエスケープされることが分かります。pg_escape_stringとPDOのquoteメソッドは、通常の文字列リテラル形式を用い、バックスラッシュのエスケープをstandard_conforming_stringsに応じて切り替えています。
これに対して、PerlのPgとPgPPは、E''文字列形式を用いることによって、standard_conforming_stringsの影響を回避しています。また、プレースホルダを使ってSQLを呼び出している場合も、standard_conforming_stringsの影響は受けません。

一方、エスケープにaddslashesや自作のエスケープ関数を用いている場合は、対応が必要となります。また、PgPPの古いバージョンを使っている場合もstandard_conforming_stringsの設定を考慮しないので注意が必要です。

既存アプリケーションのリリース9.1対応では、standard_conforming_stringsの意味を変えない方が無難だと思います。このため、既存アプリケーションでPostgreSQLのバージョンを9.1以降に変更する場合は以下のようにすればよいでしょう。
  • standard_conforming_stringsを明示している場合はそのままでよい
  • standard_conforming_stringsを明示していない場合はoffを指定する
これにより、standard_conforming_stringsのデフォルト値の変更を吸収できます。

一方、新規アプリケーション場合は、standard_conforming_stringsを指定しない(あるいはonにする)と良いでしょう。その理由は、こちらの方がISOのSQL標準であり、またバックスラッシュをエスケープ対象にすると、セキュリティ上の問題になりやすいからです。MySQLとの互換性を気にする人もいるでしょうが、PDOやDBIなどの抽象度の高いライブラリを使って記述することにより、エスケープ対象の文字をアプリケーション側で意識する必要はなくなり、互換性も向上します。
さらに言えば、プレースホルダを用いてSQL呼び出しすることを強く推奨します。これにより、そもそも文字列リテラルのエスケープが必要なくなり、性能・互換性・セキュリティともに改善されます。

[PR]
安全なWebアプリケーションの作り方」電子書籍版9月28日(水)販売開始します。くわしくはこちら

2011年8月27日土曜日

Apache killerは危険~Apache killerを評価する上での注意~

Apacheの脆弱性(CVE-2011-3192)いわゆるApache killerが話題になっていますが、その脅威については一部誤解があるようです。

以下は、非常に脅威とする報告の例です。
一方今回のはプロセスの肥大化を伴うので、実メモリ消費して更にスワップも使い尽くしてOS毎激重になったあげくLinuxとかの場合はOOM Killer発動と、他のプロセスや場合によってはOSを巻き込んで逝ってしまいます。
CVE-2011-3192 Range header DoS vulnerability Apache HTTPD 1.3/2.xより引用
以下は、それほど脅威でなかったとする報告の例です。
pooh.gr.jp は結構頑丈だったので 60 並列でやっと CPU idle 30% まで減らせた。
Apache Killer (CVE-2011-3192) 対策 for CentOS 5.6より引用
この差はなんでしょうか。サーバーが「結構頑丈だった」せいでしょうか。

それだけではありません。Apache killerを評価する上で重要なパラメータが2つあります。以下に示します。
  • ダウンロード指定するコンテンツのバイト数
  • ApacheのMaxClients設定

なぜコンテンツのバイト数が重要か

既に報告されているように、Apache killerはRangeヘッダに多くのパラメータを指定することにより、Apacheの使用メモリを増大させます。現在出回っているPoCの生成するRangeヘッダを以下に示します。
Range: bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,5-10,5-11,5-12,5-13,5-14,5-15,
5-16,5-17,5-18,5-19,5-20,5-21,5-22,5-23,5-24,5-25,5-26,5-27,5-28,5-29,5-30,5-31,5-32,

--CUT--

5-1281,5-1282,5-1283,5-1284,5-1285,5-1286,5-1287,5-1288,5-1289,5-1290,5-1291,5-1292,
5-1293,5-1294,5-1295,5-1296,5-1297,5-1298,5-1299
ご覧のように、5-1,5-2,5-3,…,5-1299 までのRange指定が含まれます。このため、URLで指定するコンテンツのデータサイズが1299バイト以上ないと、このPoCの「真価」が発揮されないと予想されます。
これを実験で試してみました。killapache.plをN=1で走らせ、URLで指定されるコンテンツ(/index.html)を0バイトから2000バイトまで変化させて、apacheのプロセスのメモリサイズを測定しました(追記:MaxClientsは3としました。理由は後述)。まずは生データです。

以下は、グラフにしたものです。

予想通り、コンテンツサイズが1300バイトまではプロセスサイズが大きくなりますが、それ以上ではプロセスサイズは変わりません。すなわち、Apache killerを評価する上では、コンテンツのサイズを1300バイト以上にすることが重要なポイントとなります。

pooh.gr.jpの評価では、robots.txt(約30バイト)を指定していたため、Apache killerはまったく真価を発揮しておらず、単なるF5攻撃と変わらない状況だったと予想されます。


MaxClients設定が重要な理由

ここまで説明すると、Apache killerの評価をする上でMax Clientsが重要である理由を説明することも容易になります。Apache killerのリクエストを受け付けると、元々24メガバイトだったApacheプロセスの消費メモリが96メガバイトと4倍になります。しかし、MaxClientsが十分余裕を持って設定されていれば、プロセスメモリが増大しても処理を継続することができます。

徳丸の評価結果

私も最初上記をよく理解しておらず、「あれ、Apache killer大したことない?」と誤解しかかったり、「評価環境が非力なので差が出ないのかな」と思ったりしました。上記に気がついた後、以下の条件で評価してみました。

VMのメモリ割りあて: 2ギガバイト
swap:512メガバイト
コンテンツのサイズ:約50Kバイト(一般的なコンテンツのサイズを想定)
MaxClients: 50
OS:CentOS5.6(32ビット)


上記条件でApache killerを走らせた場合、必要なメモリサイズは96 * 50 = 4800メガバイトとなり、メモリ+swapがまったく不足する状況となります。
結果は、ping以外はまったく応答しなくなりまりした。sshからコマンドを打つことも、VMwareのコンソールからログインすることもできなくなり、リセットするしかない状態でした。

このエントリを読んだ皆様、Apache killerはやはり凶悪です。くれぐれも油断なきよう、至急の対策をお勧めします。
私は、Apache2.2を使っていましたので、アドバイザリに従い、httpd.confに以下を追加しました。

# Drop the Range header when more than 5 ranges.
# CVE-2011-3192
SetEnvIf Range (?:,.*?){5,5} bad-range=1
RequestHeader unset Range env=bad-range
# We always drop Request-Range; as this is a legacy
# dating back to MSIE3 and Netscape 2 and 3.
RequestHeader unset Request-Range

2011年8月24日水曜日

PHP5.3.7のcrypt関数のバグはこうして生まれた

昨日のブログエントリ「PHP5.3.7のcrypt関数に致命的な脆弱性(Bug #55439)」にて、crypt関数の重大な脆弱性について報告しました。脆弱性の出方が近年まれに見るほどのものだったので、twitterやブクマなどを見ても、「どうしてこうなった」という疑問を多数目にしました。
そこで、このエントリでは、この脆弱性がどのように混入したのかを追ってみたいと思います。

PHPのレポジトリのログや公開されているソースの状況から、PHP5.3.7RC4までこのバグはなく、PHP5.3.7RC5でこのバグが混入した模様です。RC5はPHP5.3.7最後のRelease Candidateですから、まさに正式リリースの直前でバグが入ったことになります。
バグの入る直前のソースは、ここの関数php_md5_crypt_rから参照することができます。以下に、おおまかな流れを図示します。まずはバグの入る前です。


上図の各処理の概要は以下の通りです。
  1. passwdはstatic配列なので初期値は全て0('\0')
  2. MD5を示すマジック「$1$」をpasswdの先頭にmemcpyでコピー
  3. ソルトを定位置にstrlcpyでコピー
  4. 現時点の文字列末尾に$をstrcatで追加
  5. ハッシュ値を定位置に書き込み
  6. 文字列の終端を示す'\0'を書き込み
このような状態で、ソースコードの静的解析が実施され、その結果、(4)のstrcatが指摘されたようです。strcatはバッファ長を指定できないので、データの内容によってはバッファオーバーフローの原因になります。それを指摘されたのでしょう。もっとも、この処理の場合はデータが固定長なので脆弱性の心配はないのですが、コミットログのコメントには以下のように書いてあります。
Make static analyzers happy
静的解析ツールの警告表示を消したかったのでしょうね。このため、以下の変更が行われました(差分)。

strcat(passwd, "$");
  ↓
strncat(passwd, "$", 1);

strncatは書き込む文字列の最大長を指定するstrcatの改良版です。この場合は1が指定されているので、最大1文字書き込み、その後に終端の'\0'を書き込みます。

この時点ではバグは入っていません。問題は、この後です。いったんstrncatに変更した箇所が、さらにstrlcatに変更されています。

strlcat関数はstrncatの改良版です。strncatは、追加する文字列の最大長を指定しますが、元の文字列長と追加文字列、さらに終端の'\0'のトータルの長さを意識しなければならないという点で使いにくいという問題があります。これに対して、strlcatはバッファ長を指定するので、文字列長の計算を呼び出し側で意識しなくてもよいという利点があります。バッファオーバーフロー対策としてはstrncatよりも確実です。
この変更点を以下に示します(差分)。

strncat(passwd, "$", 1);
  ↓
strlcat(passwd, "$", 1);

ここで不幸にもバグが入りました。strncatとstrlcatでは、第3パラメータの意味が異なります。strncatは、追加する文字列の最大長なので、1で問題ありません。一方、strlcatはバッファ長を指定するので、既に"$1$"とソルト(8文字)の都合11文字が入っているバッファに対して、バッファ長1を指定したことになり、「バッファは既にいっぱいなので文字を追加する余地はない」と解釈されます。その結果、"$"の書き込みは行われません。バッファはそのままの状態になります。
ここで、strlcatへの変更後の処理を以下に示します。(4)の部分が変更点です。


処理(5)と(6)でハッシュ値と文字列終端'\0'が書き込まれますが、その直前の箇所が'\0'のままです。このため、C言語の文字列としては、"$1$"とソルトだけでちぎれた状態になります。このため、肝心のハッシュが出力されないという結果になりました。

以上がBug #55439の混入した経緯です。

ところで、strlcpy/strlcatの仕様をWikipediaで確認していたところ、興味深い記述を見つけました。
一方で、GNU Cライブラリ (glibc) の開発者たちは、GNU Coding Standardsで禁じられている「長い行を黙って切り詰める」関数である、このような仕様の関数はバグである、いい加減なプログラムを助長してしまう、新たなセキュリティ問題を生む、など否定的な見解を示しており、標準規格に含まれない限りはglibcには実装しない意向である。
上記の意見には賛否両論あるところでしょうが、今回のケースでは、glibcの開発者達の予想が不幸にも的中してしまったことになります。

プロジェクトマネジメント上の問題としては、テスト不足というのは明らかですが、その前段階の問題として、RC5というリリース直前の状態で不急の修正をしたことが大きな要因だと考えます。

追記

今回の原因を作ったRasmus Lerdorf(PHP/FIのオリジナル開発者)がGoogle+で今回の経緯を説明しています。英語ですが、興味のある方はご覧下さい。私のエントリの修正は必要なさそうです。

追記2

今さらですが、PHP5.3.7のmake testを走らせたところ、ちゃんとテストでFAILしているのですね。
TEST 8080/8990 [ext/standard/tests/strings/crypt.phpt]FAIL crypt() function [ext/standard/tests/strings/crypt.phpt]
テスト(crypt.phpt)の内容を見ると非常に単純なテストであり単体レベルではもっと色々なパターンで試験すべきだと思います。しかし、せめてこのテストを単体テストで実施することと、テストしたことをコミット時に確認していれば、バグ流出は防げたと思います。

2011年8月23日火曜日

PHP5.3.7のcrypt関数に致命的な脆弱性(Bug #55439)

 PHP5.3.7のcrypt関数には致命的な脆弱性があります。最悪のケースでは、任意のパスワードでログインできてしまうという事態が発生します。該当する利用者は、至急、後述する回避策を実施することを推奨します。

概要

PHPのcrypt関数は、ソルト付きハッシュ値を簡単に求めることができます(公式リファレンス)。crypt関数のハッシュアルゴリズムとしてMD5を指定した場合、ソルトのみが出力され、ハッシュ値が空になります。これは、crypt関数の結果がソルトのみに依存し、パスワードには影響されないことを意味し、crypt関数を認証に用いている場合、任意のパスワードでログインに成功する可能性があります。

影響を受けるアプリケーション

crypt関数を用い、ハッシュアルゴリズムとしてMD5を指定しているアプリケーション。
環境にも依存しますが、デフォルトがMD5の場合もあります。筆者のテスト環境(CentOS5.6上のPHP5.1.6およびUbutu 10.04上のPHP5.3.7)では、デフォルトでMD5でした。

解説

crypt関数はパスワードと、省略可能なソルトを引数にとり、ソルトおよびハッシュ値をつなげたものを出力します。
string crypt ( string $str [, string $salt ] )
まず、脆弱性のない正常系を説明するために、一番簡単な呼び出し例を以下に示します。
var_dump(crypt('pass'));
【実行結果】
string(34) "$1$tggeeij6$gtuAR4G2wT9XKWCuLxgcc."
実行結果で、$1$はハッシュアルゴリズムがMD5であること、その次の「tggeeij6」はcrypt関数の内部で生成したソルト、「gtuAR4G2wT9XKWCuLxgcc.」はソルト付きハッシュ値です。ユーザ登録およびパスワード変更の際、上記のように呼び出したcrypt関数の結果をDBに保存します。
認証の際のパスワード照合は以下のようにします。まず、ログイン画面で指定されたユーザに対応するハッシュ値をDBから取り出し、そこから、ハッシュアルゴリズムとソルトまでの部分を切り出します。上記の例では、「$1$tggeeij6」が該当します。次に、利用者の指定したパスワードを用いて、crypt関数を呼び出します。
var_dump(crypt('pass', '$1$tggeeij6'));
【実行結果】
string(34) "$1$tggeeij6$gtuAR4G2wT9XKWCuLxgcc."
この値と、DBに保存されたハッシュ値を比較し、同一であれば認証成功です。上記例では、両者が一致しているので、認証成功になります。
一方、パスワードとして「hoge」が指示された場合を以下に示します。
var_dump(crypt('hoge', '$1$tggeeij6'));
【実行結果】
string(34) "$1$tggeeij6$kCvgMXx7rTKbrciqtQYDs1"
今度は、DBに保存されたものと違う結果になりましたので、認証は失敗と言うことになります。

次に、PHP5.3.7での結果を以下に示します。まず、パスワードのみを指定した場合です。
var_dump(crypt('pass'));
【実行結果】
string(11) "$1$BOF.kMcm"
ご覧のように、ソルトまでの部分しか返っておらず、肝心のハッシュ値がありません。この値をDBに保存したと仮定して、次にユーザが認証時にパスワード「hoge」を指定した場合を以下に示します。
var_dump(crypt('hoge', '$1$BOF.kMcm'));
【実行結果】
string(11) "$1$BOF.kMcm"
パスワードの違いは反映されず、DB上のハッシュ値(実はハッシュは空)とcrypt関数の結果は一致します。認証プログラムの実装にもよりますが、最悪のケースでは「任意のパスワードで認証可能」という状況になります。
データベース上のハッシュ値がPHP5.3.6以前の環境で生成された場合、利用者が正しいパスワードを指定してもログインできないという状況になります。こちらは実装に依存しません。

対策

現在この問題を改修したPHP5.3.8を準備中とのことですが、当面のあいだPHP5.3.6にロールバックすることを推奨します。PHP5.3.8が出た後でも、試験環境などでテストを十分に実施するか、しばらく安定度を確認してからPHP5.3.8に移行することを推奨します。
PHP5.3.6にロールバックするまでの間、あるいはPHP5.3.6にロールバックできない場合、上記に該当するWebアプリケーションは、問題が改修されるまでサービスを停止するべきです。

また、認証用のデータベースを確認して、ハッシュ値が空である利用者がいれば、パスワードのリセットが必要となります。

まとめ

PHP5.3.7のcrypt関数の脆弱性について報告しました。ハッシュアルゴリズムとしてMD5を指定している場合、影響は甚大ですので、サービスを即時停止した上で、PHP5.3.6にロールバックすることを推奨します。

追記

この問題の原因について「PHP5.3.7のcrypt関数のバグはこうして生まれた」にまとめましたのでお読み下さい。

2011年8月22日月曜日

PDOにおける一応の安全宣言と残る問題点

 8月18日にPHP5.3.7がリリースされました。このリリースにより、PDOのSQLインジェクションの問題が一応解決されたと判断しましたので、ここに「一応の安全宣言」を表明するとともに、残る問題について報告します。

PDOの問題とは何か

 以前、ぼくがPDOを採用しなかったわけ(Shift_JISによるSQLインジェクション)にて報告したように、PHP5.3.5以前のPDOにはDB接続時に文字エンコーディングを指定する機能がないため、文字列リテラルのエスケープの際に文字エンコーディングをLatin1を仮定してしまうという問題がありました。この状態ですと、DBにShift_JISで接続している際に、SQLインジェクション脆弱性が混入しました。

※ 実は、先のエントリの「追記(2010/07/01 22:20)」に紹介した方法で文字エンコーディングを指定できるのですが、ほとんど知られていないのと、Windowsでは使えないという問題がありました。

 このSQLインジェクションは、プレースホルダを用いてSQL呼び出ししている場合にも起きえます。以下の条件がすべて満たされる場合です。

  • MySQLを用いている
  • 接続時に文字エンコーディングを指定していない
  • MySQLとの接続の際の文字エンコーディングとしてShift_JISを使っている
  • 動的プレースホルダ(MySQLのデフォルト)

 こんなサイトがあるのかと思われる方もおられるでしょうが、私は意外にあると予想しています。この種のサイトをソースコード診断で見つけたこともありますし、去年のPHPカンファレンスの講師飲み会で「MySQLにShift_JISで接続しているサイトありますかね」と周囲の方に聞いてみたところ、ケータイサイトはShift_JISだし、文字コード変換したくない人はShift_JISで接続している場合もあるのではないかという答えでした。

PHP5.3.6での改善

 この状況がPHP5.3.6(3月17日リリース)で改善されます。PDOにて、DB接続時に文字エンコーディングが指定できるようになったのです。しかし、さっそく検証した結果、以下の状態で完全にはSQLインジェクションが解消されてはいませんでした。
  • Linux版ではOK
  • Windows版ではShift_JIS時のエスケープが不完全

 このときの内容は、エントリ「PHP5.3.6からPDOの文字エンコーディング指定が可能となったがWindows版では不具合(脆弱性)あり」にまとめました。
 Windows版での不具合原因は、千葉征弘さん(@nihen)さんが調査した結果、単純なバグだと分かりました。その内容は、PDO/MySQL(Windows版)の文字エンコーディング指定の不具合原因にまとめました。

PHP5.3.7での改善

 千葉さんがレポートを書いてくださった結果、Windows版での不具合は、PHP5.3.7にて修正されました。

今後の推奨する書き方

 今後は、PDOを使う場合は以下のようにするとよいでしょう。
<?php
  $dbh = new PDO('mysql:host=DBHOST;dbname=test;charset=utf8', USERNAME, PASSWORD);
  $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);  // 静的プレースホルダを指定

  $sth = $dbh->prepare("select * from test WHERE a=? and c=?");
  $sth->setFetchMode(PDO::FETCH_NUM);
  $a = 'abc';
  $c = 1;
  $sth->bindParam(1, $a, PDO::PARAM_STR);
  $sth->bindParam(2, $c, PDO::PARAM_INT);
  $sth->execute();
  while ($data = $sth->fetch()) {
    var_dump($data);
  }

 new PDOの行で、文字エンコーディングをchrset=utf8と指定しています。PHP5.3.7以降、Shift_JISを用いてもSQLインジェクションは起きなくなりましたが、utf8を指定するのが色々な意味で安全です。
 次の行では、静的プレースホルダを指定しています。静的プレースホルダを使えば、SQLに指定するパラメータ操作によるSQLインジェクション攻撃は原理的にできなくなりますので、安全性が高まります。
 プレースホルダの値指定(バインド)は、execute関数で配列に指定する方法もありますが、上記のようにbindParam(あるいはbindValue)を用い、型を指定するほうがよいでしょう。型を指定しないと文字列型が仮定されますが、SQLの暗黙の型変換はワナがいっぱいで指摘したような問題が起きるため、型は明示すべきです。

 型を明示する場合としない場合の違いを、動的プレースホルダの場合に生成されるSQLを例示して説明します。まず、型を明示しない場合の呼び出し例をしめします。列aは文字列、列cは整数型とします。
$sth = $dbh->prepare("select * from test WHERE a=? and c=?");
$sth->execute(array('abc', 1));
 この場合のSQL呼び出しをパケットキャプチャを用いて示します。

 ご覧のように、列cに対して、c='1'と文字列リテラルを用いて条件式が展開されています。次に、型を明示した場合です。
$sth = $dbh->prepare("select * from test WHERE a=? and c=?");
$sth->bindValue(1, 'abc', PDO::PARAM_STR);
$sth->bindValue(2, 1, PDO::PARAM_INT);
$sth->execute();
 この場合のSQLは以下のようになります。

 このように、型を明示すると、整数型のパラメータは数値リテラルとして展開されますが、型を明示しないと、文字列リテラル'1'として展開されます。

quoteメソッドの問題

 PDOにはquoteメソッドというものがあり、様々な型のリテラルのエスケープとクオートを自動的に行います。便利なメソッドですが問題が二つありました。

  • quoteメソッドでも文字エンコーディングを考慮しておらず、Shift_JISを用いている場合にSQLインジェクション脆弱性が混入する(PHP5.3.6以前)
  • 数値の場合の処理がおかしい(脆弱性ではない。参照→quoteメソッドの数値データ対応を検証する

 これらのうち、文字エンコーディングの考慮についてはPHP5.3.7で修正されました。
 数値の場合の問題は依然として直っておらず、文字列として処理されます。静的プレースホルダを用いる場合はquoteメソッドを使う必要はありませんが、既存システムの脆弱性対策等でやむを得ずプレースホルダを使用できない場合は、数値に関してはキャストするか、バリデーションで対応することになります。

まとめ

 最近のPDOの安全性向上と残る課題について報告しました。
 PDOを使う際の安全上のポイントは以下の通りです。

  • 接続時に文字エンコーディング(UTF-8を推奨)を必ず指定する
  • 静的プレースホルダを用いてSQLを呼び出す
  • バインドの際に型を明示する

 残る課題としては、quoteメソッドの数値型の処理が依然としておかしいことです。quoteメソッドは数値に対して用いないことで対処することになりますが、プレースホルダを使えば、そもそもquoteメソッドの出番はないので、プレースホルダの使用を強く推奨します。

フォロワー

ブログ アーカイブ