2009.01.07

MD5の脆弱性を用いた証明書の偽造

「MD5 の collision attack を使用して、X.509 証明書の偽造に成功したという研究発表」の報告があった。備忘録として知り得た情報を整理しておく。

今回の攻撃は既知のMD5の強衝突耐性への攻撃を用いており、MD5のハッシュ値の衝突を起こすように作られたX.509の証明書の例は過去にも報告されている。今回の新しい点は

  • 正規のCAの署名をつける事に成功した
  • サーバ証明書ではなく、中間証明書が得られた
の2点であろう(つまり、正規のCAの署名付きの中間証明書が得られた事になり、攻撃者は「正統なCA」に成り済ました状態になる)。これで攻撃者はどのような証明書も自由に作れる事になる(実際の方法は「自堕落な技術者の日記」に詳しい)。

もちろん、SHA-1で署名したサーバ証明書の作成も(必要なら新たな中間証明書の作成も)可能なので、証明書が今回の攻撃によって作られていない事を確認するためには(全ての)中間証明書の署名がMD5を用いていない事を確認しなければならない。また、ルート証明書は自己署名なのでどのようなアルゴリズムを用いているかは関係ない。

尚、今回の攻撃ではMD5の強衝突耐性が破られている(既存の攻撃と同じ)、弱衝突耐性が破られた訳ではない(任意の証明書が作れるのはCAの成り済ましに成功したからであってMD5の弱衝突耐性とは関係ない)。従って、既存のMD5を用いて署名された証明書が直接改竄できる訳ではない。もちろん、いまだにMD5を使用しているCAがある事自体が大問題な訳で、今回の発表は最後通牒と言えるだろう。

| | コメント (0) | トラックバック (0)

2008.08.24

正規等価(Javaの正規表現)

前回の記事で、CANON_EQ モード(正規等価モード)の挙動がよく解らない事について触れたが、「Java 正規表現アプリケーション」というサイトで正規等価モードの挙動が解った(Javaの正規表現と正規等価については前回の記事を参照いただきたい)。

java.util.regex の実装では、正規等価モードの場合パターンをコンパイル時に正規等価である様々な分解形や合成形に展開しておき、いずれかに一致すればマッチした事になる。「Java 正規表現アプリケーション」では NFC と NFD に分解されるとあるが、実際には正規形以外にも正規等価の形があれば展開される。今回も、貞廣知行氏の「使いこなそうユニコード」中の「Unicode正規化とは」で挙げられている例を使って説明する。

「e, U+0304 (MACRON), U+0301 (ACUTE), U+0323 (DOT BELOW)」という文字列(Javaのユニコードエスケープ表現では "e\u0304\u0301\u0323")は、NFDが "e\u0323\u0304\u0301"、NFC が "\u1EB9\u0304\u0301" であるが他にも正規等価の関係にある下記のような文字列に展開される(元のパターンを含む)。

  • "e\u0304\u0301\u0323"
  • "\u0113\u0301\u0323"
  • "\u1E17\u0323"
  • "e\u0304\u0323\u0301"
  • "\u0113\u0323\u0301"
  • "e\u0323\u0304\0301" (NFD)
  • "\u1EB9\u0304\0301" (NFC)

ただし、正規等価の関係に無い "e\u0323\u0301\u0304" には展開されない。これで、正しい正規等価の判定が行われる事になる。パターン側のみを展開するため、判定対象の文字列は元のまま一切変更されない(そのため、前の記事の通り文字数も元のままのコードポイントによって数えられる)。

しかし、「Java 正規表現アプリケーション」にもある通り、正規等価モードにはバグがあるため残念ながら全ての場合に正しい正規等価の判定が行われる訳ではない。Fixしていない既知のバグは2種類である。

1つは、分解(または合成)される文字が2個以上連続すると Pattern#compile が例外を投げるというバグである(Bug ID: 6728861)。例えば、"ギガント" のように濁音が2個以上連続する場合は例外になる。

もう1つは、「合成除外文字」が決してマッチしないというバグである(Bug ID: 6736245)。このため、オングストローム(Å)のような記号や互換漢字は正規等価ではマッチしない(たとえ、自分自身であっても)。これは、パターンの展開時に行われる合成に正規合成用のテーブルを流用している事が原因と考えられる。

この他にも、正規等価モードには文字クラスの扱いが特殊であるという問題もある。文字クラスの範囲のどちらかの端に合成文字が含まれていた場合、範囲指定そのものが無効になり、両端の文字にのみマッチするようになる。具体的に言えば "[か-ご]" は本来 "[かがきぎくぐけげこご]" の意味になるが、正規等価モードで "[か-ご]" と記述した場合には、単に "(?:か|ご)" と記述した事と等価になる。この動作はバグにようにも見えるが、正規等価の定義「2つの文字列の正規分解が等しい」に照らして考えると、"[か-ご]" を正規分解した場合に意図しないパターンになる("[か-こ\u3099]"となり全ての濁音にヒットする)ため、規格上の動作は未定義と言うべきだと考える。

以上に挙げたように、正規等価モードには仕様上の疑問点やバグが残っている。確実な動作を狙うのであれば、正規等価モードを使用するのではなく、"(?:が|か\u3099)" のように明示的に展開するのが良いであろう。

| | コメント (0) | トラックバック (0)

2008.06.08

IRIの正規化(その2)

前稿では、IRINFC で正規化されていることが前提だと述べた。しかし、Unicode の正規化にはいくつかの問題があり、単純に正規化すれば済むというわけにはいかない。本稿では正規化の問題点を挙げた上で、実装例を元に扱い方を考察したい。

そもそも、Unicode の正規化とは同じ文字を示す複数のコード列を同一のコード列にまとめる事で扱いを容易にするための処理である(例を挙げると、「か」+濁点のコードを「が」に変換する)。IRI は URI と同じく印刷されたり手書きのメモに利用される事も考えられるため、視覚的に同じ IRI を表すコード列が複数あると識別子として不十分なだけでなく、リソースへのアクセスを保証できない。

そのため、IRI は視覚的に同じ文字を示すコード列を一意に特定することを目的に NFC という正規化が行われていることが推奨される訳だが、「視覚的に同じ」という言葉だけをとっても実装の違いや文化的な背景の影響を受け一概には決まらない場合がある。世界共通の文字コードを標榜する Unicode は正規化の規則も世界共通である必要がある訳だが、多様な文化を背景とした多数の実装が存在する事を考えれば正規化の規則を定めるだけでも容易ではない事が解るだろう。

それでは具体的にどういう問題があるかだが、日本語での利用に絞って問題を考える(外国語の問題については筆者は堪能でないため把握できない)。ご容赦いただきたい。

Unicode の正規化の詳細と問題は、貞廣知行氏の「使いこなそうユニコード」中の「Unicode正規化とは」を参考にさせていただいた。

正規化(NFC) の具体的例を以下に示す。

記号
いくつかの記号は正規化の対象となっている。
オングストローム: Å (U+212B) は、上リング付き大文字A: Å (U+00C5) に正規化される。
ただし、マイクロ記号: µ (U+00B5) は、ギリシャ文字ミュー: μ (U+03BC) には正規化されない(NFCの場合)。
かな
平仮名または片仮名と合成可能の濁点または半濁点が連続する場合、合字があれば正規化される。
「か」 (U+304B) と濁点 (U+3099) が連続している場合は「が」(U+304C) に正規化される。
漢字
互換漢字と呼ばれる漢字群は統合漢字と呼ばれる漢字に正規化される。
互換漢字「神」(U+FA19) は、統合漢字「神」(U+795E)に正規化される。

見ていただければ解るように、必ずしも視覚的に同じように見える文字が正規化される訳ではない。そもそも、あるコード列が同じに見えるかどうかは実装によるので一概には決まらないのである。正規化で同一になるコード列(正規等価と呼ぶ)は視覚的に区別できない事が望ましいと言えるが、互換漢字「神」と統合漢字「神」のように元々視覚的に異なっている文字と思われる例もある。

正規化の規格は今まで述べた通りだが、実装はどうなっているであろうか。IRI がリソースの名前や所在を表している場合、もし異なる IRI が正規化によって同じ IRI になればリソースの区別がつかない。同じリソースと看做すか別のリソースと看做すかは実装によって変わる事になる。仮にブラウザ側の実装が正規等価の IRI を同一のリソースと看做して区別しないとすると、もしサーバ側で区別されていた場合には本来存在するリソースにアクセスできない事態になる(後述するようにサーバ側に正規化の規定はない)。これは前稿で述べた「Unicodeで表現された IRI はそれ以上正規化してはならない」規則の理由に他ならない。

一方、サーバ側では区別するかしないかを独自に決める事ができる。サーバが扱うのは URI であって IRI を直接扱う訳ではない。URI には正規化の規定はないためサーバでの扱いは決まっていない(しかし、ブラウザは IRI を等価な URI に変換してサーバに送るため、ブラウザの利用者からはサーバが間接的に IRI を扱っているように見える)。具体的に実装を見てみよう。

Mac OS X
Mac OS XはWWWサーバの機能を持っている(実装はApache)。Apache は通常 URI の正規化を行う事はないが、Mac OS X のファイルシステムはファイル名について独自の正規化を行う。従ってファイルを指す URI について Mac OS X は正規化して扱う事になる。独自正規化のルールは簡単に言えば一部の記号と漢字を正規化の対象から外すというもので、独自ルールによって互換漢字「神」(U+FA19) と、統合漢字「神」(U+795E)は区別される。
Linux
Linux 上に Apache を載せた場合、Apache によって URI の正規化を行わなかったとすると、Linux のファイルシステム(UFS)はファイル名について正規化を行う事はないため、URI (IRI) はいっさい正規化されない(区別される)。ちなみに UFS が UTF-8 で構築されていなかった場合は URI はファイルシステムの文字コードで表現される。
Wikipedia
Wikipediaの URI の扱いを記述した文書は発見できなかったが実験の結果では NFC による正規化が行われているようである。具体的には、NFC でない(IRIをパーセントエンコーディングした)URI が与えられた場合、NFC に変換後の URI にリダイレクトされる。すなわち、互換漢字「神」(U+FA19) の項目を参照した場合には統合漢字「神」(U+795E)の項目を表す URI にリダイレクトされる。

WWW サーバによって NFC で正規化するもの、独自の正規化を内部的に行うもの、まったく正規化を行わないものがある。ブラウザはサーバの実装を知らないので正規化せずに透過的に扱うしか無いのは前述した通りであるが、IRI が Unicode 以外の文字コードで記述されていた場合や印刷されていた場合には NFC で正規化されていたものとして扱う事を規格は要求している。たとえ規格で決まっていなかったとしても、印刷された IRI の文字が正確にどのコードポイントかを判断するのは時として困難である。安全性を考えれば、IRI としてパーセントエンコーディングを行わずに文字で表現する場合は NFC の場合のみとし、NFC でない(NFC に変換できない)場合、特殊な文字や記号を含む場合はパーセントエンコーディングのママ(URIとして)扱うのが確実である。

| | コメント (0) | トラックバック (0)

2008.05.24

IRIの正規化

国際化URI (IRI) で触れていなかった問題に正規化の問題がある(IRIの詳細は前回の稿を参照いただきたい)。UCS (Unicode) は同じ文字を表す複数のコード列が存在するため、識別子として用いるためには正規化の必要がある。

IRI の規格である RFC 3987 では、正規化を次のように扱っている。

  • 印刷された IRI は NFC*1 で正規化されていると看做す (3.1)

  • Unicode 以外の文字コードで表されている場合は NFC で正規化して UTF-8 に変換する (3.1)

  • 一般に IRI は NFC で正規化されている事が推奨される (5.3.2.2)

扱いは明確なのだが、奥歯に物が挟まった感がある。実は、次のような決まりもある。

  • Unicodeで表現されたIRIは(それ以上)正規化してはならない (3.1)

単純に、「IRIは常にNFCで正規化されなければならない」と決めれば良いようなものだが、規格はそうなっていない。理由は明記されていないが次のような事情があったと推測される。

IRI は新しいプロトコル要素だが URI と相互に変換することによって運用される。具体例を挙げると、ブラウザは IRI を URI に変換した上で Web サーバに送らなければならない(HTTP上では IRI は送信する事ができない)。また、利便性のため、ブラウザは URI を IRI に変換して表示する事も許されている。つまり、IRI は URI の表現方法(ビュー)の一つと考える事ができる。

IRI が URI のビューに過ぎないとすれば、既存の URI を無視して定める事はできない。URI に対しては文字コードすら決まっておらず、当然、正規化の規定はない。つまり、URI はパーセントエンコーディングさえ行えば、まったく正規化しまいが NFD*2 で正規化しようが(Shift_JISで*3表そうが)自由という事になる。IRI は URI との往復変換で元に戻る事を保証する必要があるため、IRI だけで正規化を行う事ができない。つまり、既存の URI との互換のため正規化が義務づけられなかったと考えられる。

つまり、UTF-8 で表した(文字列をパーセントエンコーディングした)URI は IRI として us-ascii 以外の文字で表現しても良く、そのまま URI に変換した場合は元に戻る事が保証されている。しかし、その IRI を印刷した瞬間(あるいは Unicode 以外の文字コードに変換した瞬間)に NFC で正規化した事になり、元の URI が NFC で正規化されていなかった(ものをパーセントエンコーディングした)場合は元に戻る事はない。IRI を安全に印刷できるのは NFC の場合のみという事になる。

いずれにしても、IRI を使用する(記述する)場合は NFC で正規化するものだと思えば、ほぼ間違いないであろう*4

--------
*1: できるだけ短くなるように正規化した形式(正確には正規分解したのちに正規合成した形式)。
*2: 全ての合成文字を分解(正規分解)した形式。
*3: IRI は UTF-8 でエンコードされている事が前提である。従って Shift_JIS でエンコードされた URI はIRI に変換してもパーセントエンコーディングのママとなる。
*4: Unicodeの正規化そのものに関しては問題が残る。その件は続稿で考えたい。

| | コメント (0) | トラックバック (0)

2008.05.06

コトノコの広辞苑用外字ファイル

私が普段利用している、広辞苑第五版 CD-ROM版と EPWING用ビューアのコトノコ用の外字定義ファイルを作成しました。

広辞苑第五版 外字ファイル (コトノコ用)

EBWin用の広辞苑の外字定義ファイル(本文に現れる全角外字1675個を中心に定義したもの)と、NDTPD辞書クライアント用の外字定義ファイル(記号を中心に定義したもの)をマージして作成したものです。それぞれの作者に感謝します。

あくまでも個人的な目的のために作成したファイルですが、どなたかの役に立つかもしれないので公開します。文字の割当は元ファイルを利用していますが、マージしたファイルの誤りの責任は私にあります。

(広辞苑第六版でも使用できるという情報もあるが確認はしていない)

| | コメント (0) | トラックバック (0)

2007.04.19

APOPにMD5関連の脆弱性

IPAによれば「APOP 方式に、MD5 ハッシュ衝突に基づく攻撃手法が発見され」たとの事(JVN#19445002 APOP におけるパスワード漏えいの脆弱性)。

MD5は以前からハッシュ衝突の危険性が指摘されていた訳であるが、実際に衝突を起こして署名を捏造したりパスワードを解読したりする事に成功した事例は報告されていなかったように思う。今回は APOP 自体のプロトコル上の脆弱性と MD5 のハッシュ衝突の問題を組み合わせてパスワードの取得を行ったらしい。公開された情報から攻撃方法は「中間者攻撃」である。したがって正当にやりとりされるネットワーク上の通信を傍受してパスワードを取り出したという訳ではない。

とりあえず、詳しい情報へのリンク

乱暴に要約すると、今回の攻撃はMD5の脆弱性を突かれている事に加えハッシュをチャレンジ・レスポンスにしか使っていない事が対象になっているように思える。同じMD5を使用したチャレンジ・レスポンス型のHTTPのダイジェスト認証では、サーバ上に平文パスワードのハッシュ値が格納され、さらにハッシュ値に対するチャレンジ・レスポンスでハッシュ値を求める(つまり、ハッシュを2回使う)形になっているのでAPOPよりは平文パスワードを取り出す事の障壁は高い。もちろん、IPAの報告通り(中間者攻撃そのものを防ぐ事ができ、メール本文も保護する)SSLを使用するのが正しい対処である。

| | コメント (0) | トラックバック (0)

2007.03.10

Javaの正規表現

SUNの標準の正規表現ライブラリであるjava.util.regexパッケージについて調べてみた。java.util.regexは非決定性有限オートマトン(NFA)に基づく(Javaには多数の正規表現パッケージがあるが決定性有限オートマトン(DFA)を用いた実装はないらしい)。

いきなりですが、ここで問題です。次のJavaの式の値は何でしょう(ちょっと引っ掛けかも。ヒントはサロゲートペア)。

  • "\uD835\uDD21".matches(".*[\\uD834\\uDD21]+.*")

ちょっといやらしい例だったので、もっと素直な例を。以下のJavaの式の値は何か(String#matchesは全体一致である事に注意)

  • "\uD834\uDD21".matches(".")

解説、\uD834\uDD21 は Java の Unicode エスケープでコードポイント U+1D121(一文字)を表す(サロゲートペア)。Unicode の正規表現での扱いはコードポイント単位であることが規定されている
U+1D121 のカテゴリは OTHER_SYMBOL (So)。ちなみに Java 1.4 と 1.5 では扱いが異なる(はず)*1

基本を確認したところで再び問題。以下のJavaの式の値はそれぞれどうなるか。(最後の式の結果は論理的には導けません。あしからず)

  • "\uD834\uDD21".matches("\\p{So}")
  • "\uD834\uDD21".matches("\\p{Cs}+")
  • "\uD834".matches("\\p{Cs}+")

一応、正解(というか実際の動作結果)を記しておく。結果は全てbooleanである。true を t、false を fで記す。出題順に / で区切り、複数の式がある場合は連続して記述する。

f/t/tft(Java version 1.5.0_09 での結果)


ちなみに、サロゲートペアは以下のように範囲で使用することもできる。

"\uD834\uDD22".matches("[\uD834\uDD21-\uD834\uDD24]+")

評価結果は true となる。
これはもちらん、

"\uD834\uDD22".matches("[\\uD834\\uDD21-\uD834\uDD24]+")

と書いても同じである。ところが、正規表現を

"[\\uD834\\uDD21-\\uD834\\uDD24]+"

と記述した場合、java.util.regex.PatternSyntaxException: Illegal character range が発生する。どうやら正規表現の文字クラス内のUnicodeエスケープの処理にバグがあるようである。

--------
*1: Java 1.4 (J2SE 1.4) はUnicode 3.0に準拠している。Unicode 3.0にはサロゲートペアの規定はあるものの、実際にBMP以外の領域(サロゲートペアを必要とする範囲)に文字は割り当てられていない。このため、J2SE 1.4のサロゲートペアへの対応は最小限にとどまっており、実質的には使えないに等しい。サロゲートペアに対応したのは Java 1.5 (J2SE 5.0)以降である。


ここまで、主にUnicodeのコードポイントに着目してJavaの正規表現(java.lang.regex)について調べてみた。しかし、Unicodeは単にコードポイントが決まれば文字が決まるような単純な体系ではない。同じ文字を表現する多くのコード列が存在する。同じものが複数あると不便なので「正規形(Normalization Forms)」という形が決められている(正規形と正規表現(regular expression)、英語だと全然違うのに日本語だと混乱しますね)。

ところが、Unicodeでは正規形も複数決められているのである(一体、どこが "Uni" code なんだ?という感じだが)。NFD, NFC, NFKD, NFKC の4通りある。NFDが最も基本的なもので、全ての「合成文字」を「分解」した形になる。と言っても解りにくいので例を挙げると、「が(U+304C)」という文字は「か(U+304B)+結合用濁点(U+3099)」に分解される。

さて、java.lang.regexでは CANON_EQ をサポートしている。CANON_EQ とは、Canonical Equivalents の事でJavaDocの日本語版では標準等価と訳しているが、一般的ではないようなのでここではCANON_EQ とする。問題は CANON_EQ の定義であるが、Unicode Technical Standard #18 に規定されている。しかし、規定をみても具体的な方法までは決められていない。Javaの正規表現では、「2 つの文字の完全な標準分解がマッチした場合に限り、それらの文字がマッチするとみなされます」とあるので、NFDに基づいている事が解る。

ところが、実際にjava.util.regexを使ってみると、どうも不思議な動きをする。具体的には合成文字を与えた場合と、結合文字列(基底文字と1以上の結合文字を続けた列)を与えた場合で動作が変わるのである。たとえば、合成文字を与えた場合は1文字と数えるのに対して結合文字列を与えた場合は複数文字と数えたり、合成文字を与えてもマッチしなかったパターンに結合文字列を与えればマッチする事がある。単純にNFDに変換してから比較している訳ではないらしい。

誤解していたかもしれないので補足しておく。「2 つの文字の完全な標準分解がマッチした場合に限り、それらの文字がマッチするとみなされます」は Canonical Equivalents の定義(文字列のNFDが等しい)を述べているに過ぎない。つまりJavaの「実装」がNFDに基づいているという意味はない。と読める(文字単位の比較である事は述べているように見えるが、文字=コードポイントとすると文字単位の比較がCannonicalというのは矛盾が残る)。JDK 6のドキュメントでは用語が「正規等価」、「正規分解」に改められている。

結局のところ、java.util.regex はコードポイント単位で処理するのが大原則であるという事につきるらしい。したがって、「が」は一文字だし、「か+濁点」は2文字と数える。「が」は「か」や「濁点」にはマッチしないが、「か+濁点」は「か」にも「濁点」にもマッチする。

従って、CANON_EQ の有無は、この例では「が」が「か+濁点」にマッチするかしないか、「か+濁点」が「が」にマッチするかしないかの違いだけである。

利用者から見れば、画面上に表示される文字(Grapheme Clusterと呼ぶらしい)単位で処理される事を期待しそうだが、java.util.regex はそうなっていない。あくまでもコードポイント単位である。従って同じ文字(Grapheme Cluster)であってもコードポイントの表現の方法によって正規表現のマッチの結果が変わる(たとえ CANON_EQ でも)。

Unicode Technical Standard #18 によればコードポイント単位の処理は誤りではない。例えばドット (.) は1つのコードポイントにマッチするのが標準らしい。Grapheme Cluster にマッチする記号の例としては \X が挙げられている(Javaでは実装されていない)。正規化と CANON_EQ はあくまでも別物と考えるべきであろう。

これはJavaの処理の場合であって、Unicode Technical Standard #18 ではあらかじめ正規化してから処理する方法も認めている。この場合は処理単位のコードポイントが元の文字列と変わる事になる。その代わり同じ正規形を持つ文字列であれば同じマッチ結果を持つことが保証される。

| | コメント (0) | トラックバック (1)

2006.09.24

猫舌対応自動販売機

自動販売機
先日、東京国際フォーラムで自販機を見つけた。右の写真がそれである。一見何の変哲もない自動販売機であるが、右側に液晶画面が付いているのが解ると思う。この液晶画面は普段は付近のイベント情報などを映しているようであるが、硬貨を投入して購入モードになると、下のような画面に変わる。


猫舌仕様設定
購入する飲物の設定を行うための画面である。ちょっと写りが悪くて恐縮であるが、一番下に注目して欲しい。「熱さ」という設定があり、「標準」と「ぬるい」が選べるのである。「ぬるい」コーヒー。味を追求する自動販売機の開発者の発想ではないだろう。猫舌の利用者の要望を実現したものと思われる(残念ながら猫マークは付いていない)。

| | コメント (0) | トラックバック (0)

2005.04.25

QR code

世間ではいろいろなものでQRコードを作ってみるのが流行っているようですが、真似してやってみました。



   ■■■■■■■    ■  ■■■■■■■
   ■     ■ ■     ■     ■
   ■ ■■■ ■    ■■ ■ ■■■ ■
   ■ ■■■ ■  ■    ■ ■■■ ■
   ■ ■■■ ■ ■■ ■■ ■ ■■■ ■
   ■     ■  ■■■  ■     ■
   ■■■■■■■ ■ ■ ■ ■■■■■■■
            ■           
   ■ ■ ■ ■  ■  ■   ■  ■  
   ■   ■  ■ ■ ■ ■■  ■■  
     ■■■ ■   ■■    ■ ■ ■
   ■■■■■■  ■■■■■■■■   ■ 
    ■■ ■ ■■ ■ ■  ■ ■■ ■ 
           ■■■■   ■ ■ ■ 
   ■■■■■■■   ■ ■  ■ ■  ■
   ■     ■  ■■■ ■ ■    ■
   ■ ■■■ ■ ■   ■ ■■■ ■■■
   ■ ■■■ ■   ■■  ■  ■ ■ 
   ■ ■■■ ■ ■■ ■  ■ ■■  ■
   ■     ■   ■■■■ ■   ■ 
   ■■■■■■■ ■  ■■ ■■ ■■  





テキストをQRコード化するのは当たり前だが、逆にQRコードをテキスト化するのは初めてだろう、と思ったら既に先にやってるところがありました(残念)。

携帯を持っていないので、携帯電話で読めるかどうか解らない……

| | コメント (2) | トラックバック (0)

2005.04.21

Cross-Site Request Forgeries

Mixi で問題になっていたのだが、Cross-Site Request Forgeries (CSRF) と呼ばれる Web サイトに対する攻撃方法*1があるらしい(原典?)。要するに、悪意のある者(偽装者)が特定のサイト(攻撃対象)のフォームをコピーし、まったく違う目的のフォーム(またはリンクや単なるIMG要素など)に紛らわせてアクセスさせるものである。偽装する方法は単に偽装者が自身のサイトを用いて行ってもかまわないし、クロスサイトスクリプティング(XSS) を用いても良い。ちなみに、CSRF は XSS と似ているが、XSS とは異なり、必ずしも攻撃対象のサイトそのものにスクリプトを埋め込む必要は無い。したがって、対策も XSS とは異なるものが要求される。単純に思いつく対策を挙げてみる。

クッキー
リクエスト元ではなくリクエスト先のクッキーをブラウザが自動的に付与するため、先に攻撃対象のサイトに誘導して認証を済ませていれば防護にならない。
Basic認証
リクエスト先に応じてブラウザが自動的に認証情報を送るため、クッキーと同じ問題を持つ。
POSTメソッドの判定(GETメソッドの禁止)
IMG要素を利用した簡単なものなら検出できる可能性がある。ただし、フォームを利用したりJavaScript を併用した高度な攻撃には対応できない。
SSL
ブラウザはSSLの場合、他のサイトに対するアクセスは警告を発する場合が多いので防護できる可能性がある。
Referer
フォームなりリンク元のサイトなりが正しい事を確認する最も直接的な方法がRefererである。ただし、Refererは必ずつくわけではない。詐称も可能である。*2

これらの方法はまったく効果がないものからある程度の効果が期待できるものまで含まれているが、完全に防げるものではない。完全に防ぐための方法は以下のようなものが考えられる。

フォームの要素によるセッションの確認
偽装者が予測不能な要素をフォームの要素なりリンクなりに含める。単純にセッション保持用のクッキーの値と同じものでも良い。ブラウザはクッキーを自動的に付けるが、フォーム要素は明示的に指定しない限り付け加えない。セッションのキーが偽装者に予測不能(これは当然の要件)であれば充分である*3
ユーザ介入の要求
「削除して良いですか?」のようにユーザに対して確認を求める画面を出力する。ただし、単純に Yes と答えるように固定の応答を要求する場合は CSRF をもう一度繰り返して破られる恐れがある*4。再度ユーザ認証を求める(単純に同じパスワードをもう一度入力させる方法でも効果がある)か使い捨てトークンを併用するなど固定の応答にならないようにすれば機械的にまねる事はできない。

フォームの要素によるセッション管理は JavaScript でアクセスすれば破られるのではないかという指摘もある。しかし、偽装者が他サイトに仕組んだ JavaScript で攻撃対象の情報を取得できたとすればブラウザのセキュリティホールである。また、攻撃対象サイトに JavaScript が送り込まれたとすれば、サイトに XSS 脆弱性があったと言うことができる。

--------
*1: CSRFという名前は使用していないが kjm's home page にも同様の議論がある。
*2: 偽装者によって詐称することはできないのではないかという指摘が「高木浩光@自宅の日記」になされていたので修正した。
*3: 同じく、「高木浩光@自宅の日記」での指摘を受けて修正した。
*4: 言うまでもないが、サイト内部でのセッション管理がなく、単純に type=hidden の要素などで情報を引き継いでいる場合には直接確認画面に対して1度のCSRFを行えば攻撃できる。

| | コメント (0) | トラックバック (0)

«SHA-1が破られた?