2008年6月2日月曜日

SQLエスケープにおける「\」の取り扱い

補足

この記事は旧徳丸浩の日記からの転載です。元URLアーカイブはてなブックマーク1はてなブックマーク2
備忘のため転載いたしますが、この記事は2008年6月2日に公開されたもので、当時の徳丸の考えを示すものを、基本的に内容を変更せずにそのまま転載するものです。
補足終わり


昨日のエントリ(徳丸浩の日記 - そろそろSQLエスケープに関して一言いっとくか - SQLのエスケープ再考)は思いがけず多くの方に読んでいただいた。ありがとうございます。その中で高木浩光氏からブクマコメントを頂戴した
\がescape用文字のDBで\のescapeが必須になる理由が明確に書かれてない。\'が与えられたとき'だけescapeすると…。自作escapeは危うい。「安全な…作り方」3版で追加の「3.失敗例」ではDBで用意されたescape機能しか推奨していない
このうち、まず「\」のエスケープが必須となる(MySQLやPostgreSQLで)理由を説明しよう。

「\」をエスケープしないと処理がおかしくなる

MySQLにおいて、文字列「\n」は改行を意味する。その他、\に続く文字によって、様々な制御文字などを表現できるようになっている。
このため、ユーザがたまたま入力欄などから「\n」と入力した際に、エスケープしないままだと、ユーザの意図に反して「\n」が改行に化けることになる。また、エスケープシーケンスとして定義されていない場合、たとえば「\x」は単に「x」を表すと規定されているので、「tokumaru.org大安売り¥100-」が「tokumaru.org大安売り100-」となり、「¥」が欠落してしまう。これはユーザの意図ではない。
このため、「\」を「\\」エスケープすることにより、上記のような文字化けを防ぐ必要がある。これが、そもそも「\」のエスケープが必須となる理由で、セキュリティ上の要求がなくても、必要な処理である。

「\」のエスケープもれによるSQLインジェクション

上記に加えて、「\」のエスケープが必要な状況でそれがもれている場合、SQLインジェクション脆弱性の原因となる。高木氏が指摘しておられるように、「\'」という入力に対して「'」のみエスケープすると、「\''」という文字列になる。前半の「\'」で「'」を表すので、末尾の「'」がエスケープされないで残ってしまう。つまり、文字列リテラルを終端できる。前回指摘したように、これによりSQL断片を埋め込むことが可能となる。この例だと分かりにくいので、もう少し現実的な攻撃パターンで説明しよう。
SELECT * FROM XXX WHERE NN='$id'

$id として \'or 1=1# が入力されると

\'or 1=1#
  ↓ エスケープ
\''or 1=1#

元のSQLに適用すると、

SELECT * FROM XXX WHERE NN='\'' or 1=1#'

すなわち、SQLの構文が改変された
上記でデータベースとしてはMySQLを想定している。「#」はMySQLでコメントを表すので、行末の「#'」は無視される。

Shift_JISでの問題

ここまでならまだよい。データベースの種類によっては「\」のエスケープを忘れないようにしようで済む。ところが、文字「\」を表す文字コード0x5cがShift_JISの2バイト目にも現れうることから話がややこしくなった(一方、「'」を表す0x27の方はShift_JISの二バイト目に現れない)。0x5cを二バイト目に含む文字は多数あるが、例として以下を紹介する。
ソ(835c)
能(945c)
表(955c)
予(975c)
上記のように出現頻度の高い文字が含まれている。言語処理系やデータベースエンジン、APIなどに日本語処理の不完全な部分があると、SQLインジェクションの可能性が出てくる。

データベースエンジンの日本語処理が不完全な場合

この場合は、「表'」のような組み合わせによりSQLインジェクションができる可能性がある。
'
0x950x5c0x27
↓フロント側でのエスケープ処理
''
0x950x5c0x270x27
↓データベース側の解釈
0x950x5c0x270x27
0x95\' で'一文字' がエスケープされずに余る

このように、末尾の「'」がエスケープされない状態となり、SQLインジェクション脆弱性が生まれる。

フロント側の日本語処理が不完全な場合

フロント側(言語処理系)の日本語処理が不完全な場合も「表'」の処理において「'」のエスケープ抜けが発生する
'
0x950x5c0x27
↓フロント側でのエスケープ処理(0x5cと0x27をそれぞれエスケープ)
0x950x5c0x5c0x270x27
↓データベース側の解釈
0x950x5c0x5c0x270x27
「表」一文字\'で一文字' がエスケープされずに余る

上記はShift_JIS固有の現象であるので、できるだけShift_JIS以外の文字エンコード、例えばUTF-8を使うとよい。しかし、ケータイブラウザのようにShift_JISのみ受け付けるものや、エンタープライズ系の応用では文字化けを避ける目的でShift_JISを要求される場合もある(入出力時に文字コード変換して処理はUnicodeに統一する手もあるが、わずらわしい場合もあるだろう)。

PostgreSQLの対応

PostgreSQLでは上記の問題に対応するために、バージョン8.1.4(2006年5月24日リリース)では、以下のような変更が行われた
  • 常にサーバ側で無効なコードのマルチバイト文字を拒否するように修正された
  • 文字列リテラル中の安全でない「\'」を拒否する機能が追加された
  • 以下略
上記については、ITproへの石井達夫氏による寄稿(【PostgreSQLウォッチ】第27回 SQLインジェクション脆弱性を修正,日本語ユーザーに大きな影響)が詳しい。簡単に要約すると、「\'」形式のエスケープを禁止・エラーにし、「''」方式(ISO標準)に限定できるようにした。これにより、Shift_JISの二バイト目の0x5cにまつわる「'」のエスケープ抜けを防止するというものである。但し、後方互換性の確保のため、backslash_quoteという設定パラメタが用意され、これが on の場合には、従来どおり「\'」形式のエスケープを許容する。

pg_escape_stringの挙動調査

冒頭に紹介した高木浩光氏のブクマコメントの後半は、自作のエスケープではなくDBで用意されたエスケープ機能を利用するようにという指摘であった。まことにその通りで、私のブログを読むと、自作のエスケープを推奨しているようにも読めるが、それは良くない。
それでは、PHPで用意されているpg_escape_stringは期待通り動作するのだろうか。簡単なスクリプトで検証してみた。
検証用スクリプト(PHP)

$cn = pg_connect("host=localhost user=xxxx password=xxx";
echo pg_escape_string($cn, "表\\'");

実行結果1 (standard_conforming_strings = on の場合)
表\''

実行結果2 (standard_conforming_strings = off の場合)
表\\''
PHP言語側のエスケープの都合で紛らわしいが、入力文字列は「表\'」である。前回紹介したstandard_conforming_stringsの設定を正しく反映して、onの場合は「表\''」(「\」のエスケープをしない)、offの場合には「表\\''」(「\」、「'」ともエスケープする)結果となっている。いずれの場合にも「'」は「''」とエスケープされるので、backslash_quoteの設定には依存しない。素晴らしい。

DBD::PgPPの場合

こんどはPerlでの例。PerlからPostgreSQLを利用する場合には、DBIとDBD::Pgの組み合わせが利用される・・・と思うのだが、筆者の環境では中々DBD::Pgがインストールできなかったので、代わりにDBD::PgPPを使って検証してみた。PgPPはピュアPerlで記述されたPostgreSQL用インターフェースである。DBD::PgPP中のquote()のソースを見ると、文字の変換部は以下のようになっていた(バージョン0.05)。
$s =~ s/(?=[\\\'])/\\/g;
return "'$s'";
正規表現の「?=」はゼロ文字の先読み表明というやつで、後ろに「\」か「'」が続くゼロ文字にマッチする。すなわち、「\」と「'」はそれぞれ「\\」と「\'」にエスケープされる。これはいただけない。standard_conforming_stringsもbackslash_quoteも無視されている。
これは恐らくDBD::PgPPの完成度があまり高くないということなのだろう。従って、自作のエスケープをせずにDBで用意されたエスケープ機構を使えというガイドラインは一般論として正しいと思うが、上記のような例もあるので、初めて使う前に簡単なテストをしておけば安心できる。

まとめ

  • 「\」のエスケープを要求するデータベースは日本語処理に特に注意
  • 例えば、Shift_JISを避ける
  • 自作のエスケープを避け、DBにて用意されたものを使う
  • その場合でも過信は禁物で、できるならチェックしてから使うとよい
参考:WASForum Conference 2008講演資料「SQLインジェクション対策再考」

2008年6月1日日曜日

そろそろSQLエスケープに関して一言いっとくか~SQLのエスケープ再考~

補足

この記事は旧徳丸浩の日記からの転載です。元URLアーカイブはてなブックマーク1はてなブックマーク2
備忘のため転載いたしますが、この記事は2007年11月26日に公開されたもので、当時の徳丸の考えを示すものを、基本的に内容を変更せずにそのまま転載するものです。
補足終わり


本稿ではSQLインジェクション対策として、SQLのエスケープ処理の方法について検討する。
最近SQLインジェクション攻撃が猛威を振るっていることもあり、SQLインジェクションに対する解説記事が増えてきたようだが、対策方法については十分に書かれていないように感じる。非常に稀なケースの対応が不十分だと言っているのではない。ごく基本的なことが十分書かれていないと思うのだ。
SQLインジェクション対策には二通りある。バインド機構を使うものと、SQLのエスケープによるものだ。このうち、SQLのエスケープについて、十分に書かれているテキストが見当たらないのだ。このため、自分で書いてみようと思う。
IPAの「安全なウェブサイトの作り方改訂第三版」ではSQLのエスケープについて以下のように説明されている。
1)-2 バインド機構を利用できない場合は、SQL 文を構成する全ての変数に対しエスケープ処理を行う
解説 これは、根本的解決 1) のバインド機構を利用した実装ができない場合に実施すべき実装です。
 利用者から入力されるパラメータや、データベースに格納された情報などに限らず、SQL 文を構成する全ての変数や演算結果に対し、エスケープ処理を行ってください。エスケープ処理の対象は、SQL文にとって特別な意味を持つ記号文字(たとえば、「'」→「''」、「\」→「\\」 など)です。
 なお、SQL 文にとって特別な意味を持つ記号文字は、データベースエンジンによって異なるため、利用しているデータベースエンジンに応じて対策をしてください。データベースエンジンによっては、専用のエスケープ処理を行うAPIを提供しているものがあります(たとえば、Perl ならDBIモジュールのquote()など)ので、それを利用することをお勧めします。

引用した部分は、一般的な内容を網羅しているものの不満もある。短い文章の中で「など」が3回も現れることに象徴されるように、あいまい性の残る文章となっている。これはデータベースの製品依存のところでやむを得ないというのはよく理解できるのだが、結果として、読者に「データベースエンジンのマニュアルを読め」と言っているの等しい。それでいて、読者には、マニュアルのどこをどう調べたらよいかまでは示していない。「エスケープ処理の対象は、SQL文にとって特別な意味を持つ記号文字」と書かれているが、この説明だと、「;」や「=」、空白までエスケープしなければならないと誤解する読者が出てくるかもしれない。実際には、これらの文字をエスケープする必要はないし、SQLの標準規格には、そもそもこれらの文字をエスケープする手段が用意されていない。エスケープの必要がないので手段も用意されていないのだ。
そこで、「安全なウェプサイトの作り方」を補完するような形で、もう少しSQLエスケープについて書き足してみたい。

SQLインジェクションのおさらい

SQLインジェクション攻撃では、SQLに渡すパラメータ部分にSQL断片を挿入し、SQLの意味を書き換えることによって行われる。 バインド機構を使わずに自前でSQLを組み立てる場合、SQLに対するパラメータはSQLのリテラル(定数)の形で渡される。
リテラルには複数の型があるが、通常問題になるのは数値と文字列である。
数値リテラルの場合:
SELECT * FROM XXXXX WHERE NNUM=●●●      -- ●●●は数値、例えば 123
文字列リテラルの場合:
SELECT * FROM XXXXX WHERE EID='■■■'      -- ■■■は文字列、例えば S853
なぜリテラルを構成する文字列(●●●や■■■)を操作することでSQLの構文まで変わるのか。それは特殊記号などでリテラルを終端させ、その後にSQL断片を埋め込むからだ。
数値リテラルを終端させるには、数値以外の文字を加えればよい。
例: 123OR TRUE         -- 数値リテラル123の後に、OR TRUE が続く。123 OR TRUEと同じ
文字列リテラルを終端させるには、単一引用符「'」を加えればよい
例: A'OR'A'='A

この例を先のSQLに適用すると、

SELECT * FROM XXXXX WHERE EID='A'OR'A'='A'

となる。つまり、WHERE句は常に真となる。

SQLインジェクション対策の基本的な考え方

すなわち、SQLインジェクション対策の方針としては、リテラルを勝手に終端させないようにすることが必要なのだ。そして、この処理は数値リテラルと文字列リテラルとでは、方法が異なる。
数値リテラルの場合は、数値以外の文字が出てきた時点でリテラルの終端となるので、数値としての妥当性検証を行うことになる(数値項目に対するSQLインジェクション対策のまとめ参照) 。
一方、文字列リテラルの場合は、文字列リテラルのエスケープを行えばよい。SQL92などSQLの標準規格で規定しているのは、単一引用符「'」のエスケープであるが、データベースの種類によっては、円記号(バックスラッシュ)「\」のエスケープも必要となる。

商用データベースの場合

商用データベースの場合は、SQL標準に従い、単一引用符「'」を「''」と重ねる処理を行う。OracleSQL ServerIBM DB2についてはリファレンスと動作の両方で確認した。

オープンソース・データベースの場合

オープンソースのデータベースとして広く普及しているMySQLの場合、文字列リテラル中にC言語風の「\」を使ったエスケープシーケンスが記述できる。従って、文字列リテラル中に「\」自体を記述する際には、「\\」とする必要がある。一方、単一引用符「'」は「''」としてもよいし、「\'」としてもよい。その他、二重引用符「"」が利用できるなど自由度が高い。
オープンソース・データベースのもう一方の雄PostgreSQLの場合は事情が少し複雑となるが、デフォルトではMySQLと同じで、単一引用符と円記号(バックスラッシュ)の両方をエスケープする必要がある。しかし、設定パラメータstandard_conforming_stringsがonの場合(デフォルトはoff)は、Oracleなどと同じ挙動となる(現実にはもう少し複雑だが稿をあらためて説明したい)。
まとめると以下のようになる。

データベース元の文字エスケープ後
Oracle
MS SQL
IBM DB2
'''
MySQL
PostgreSQL
''' または \'
\\\

なお、念のため補足すると、エスケープ処理はセキュリティ対策のために行うものでは元々なく、与えられたパラメータに対して正しく処理を行うために必要な処置である。入力に「'」や「\」が使えないと不便で仕方がないし、現実的に不具合が生じるだろう。

その他のデータベース製品の場合はどうしたらよいか

今回説明した内容にもっとも近い記述があるドキュメントとしては、佐名木智貴氏の近著「セキュアWebプログラミングTips集(ソフト・リサーチ・センター)」がある。同書では、SQLエスケープの基本として
「'」は、「''」(シングル・クォート2個)にエスケープ処理することで、SQLインジェクションから防御することができる(同書P210)
とした上で、
mySQLとPostgreSQLの場合のSQLインジェクション対策として、入力データをSQL文の文字列リテラルとして使う場合、「'(シングル・クォート)」と「\」をSQLエスケープすること(同書P213)
と指摘している。
非常に丁寧な仕事ぶりで好感を持った。ただ、同じページの以下はいただけない
筆者の知らないデータベース・ソフトウェアでは、SQLが拡張され、それ以外のメタキャラクタもあるかも知れない。ぜひ読者諸氏には今一度、自分の使っているデータベース・ソフトウェアのSQLリファレンスを通読することを推奨する(同書P213)。
「SQLリファレンスを通読」とは・・・無茶言うなよと思う。
実際には通読する必要はなく、「リテラル」、「定数」、「文字列」などのキーワードを手掛かりに、文字列リテラルの項を探すとよい。本稿で引用したリファレンスもこのようにして探したものである。
Oracleの場合を例にマニュアルの見方を説明しよう。題材として、Oracle10gのオンラインマニュアルを利用する。
まず、目次から「リテラル」を探すと、「2 Oracle SQLの基本要素」に「リテラル」や「テキスト・リテラル」という項が見つかる。「テキスト・リテラル」の項を読むと、
  • cは、データベース・キャラクタ・セットの任意の要素です。リテラル内の一重引用符(')の前には、エスケープ文字を付ける必要があります。リテラル内で一重引用符を表すには、一重引用符を2つ使用します
  • ''は、テキスト・リテラルの始まりと終わりを示す2つの一重引用符です。
このように、エスケープの必要な文字は一重引用符「'」であること、エスケープの仕方は一重引用符を2つ使用することであることがわかる。他のデータベース・ソフトウェアでも、同様に探すことができるだろう。
本稿を参考に正しいSQLインジェクション対策を実施していただきたい。

続く(徳丸浩の日記 - SQLインジェクション対策 - SQLエスケープにおける「\」の取り扱い)
参考:WASForum Conference 2008講演資料「SQLインジェクション対策再考」

追記(2010/03/26)

このエントリを書いた後、IPA非常勤研究員として、SQLインジェクションの正しい対策方法について調査・検討しました。その成果は「安全なSQLの呼び出し方」という冊子(安全なウェブサイトの作り方別冊)という形にまとめられました。ぜひご活用いただければと思います。ダウンロードはこちらから。

本日のツッコミ(全3件)

_ 佐名木 (2008年06月24日 21:59)
mySQL での「\」のエスケープについて知ったのは、(私にとっては無知の知の領域にある)mySQL のリファレンスを通読している時でした。私には衝撃的でした。無意味だと思うからです。「'」→「''」だけで十分なのに、「\」を導入する必要性を感じないからです。
なので、読者の人が使っているかも知れない私の知らない DB については、DB 開発者が自由に拡張しているかも知れないから注意してね。
というつもりなのです。

出版までにちゃんとまとめなくて中途半端だったので、次回は正規表現(Like演算子)の時のエスケープについてお願いします。
といっても正規表現の使用場面を考えると、そもそもインデックスを張っていないカラム対象が多いのでそれほど問題にはならないとは思いますが、プログラミング書法という観点でもお願いしたいですね。

_ momo (2010年05月06日 21:24)
クオートをエスケープした内容をSQLに格納するのはいいが、次にDBからその内容を利用するとき、エスケープは消え、クオートは裸のままであることをおわすれなく。

_ Nkzn (2010年05月27日 14:08)
↑セカンドオーダーSQLインジェクション?っていうんでしたっけ?

2007年12月6日木曜日

画像ファイルによるクロスサイト・スクリプティング(XSS)傾向と対策

補足

この記事は旧徳丸浩の日記からの転載です(元URLアーカイブはてなブックマーク1はてなブックマーク2)。
備忘のため転載いたしますが、この記事は2007年12月6日に公開されたもので、当時の徳丸の考えを示すものを、基本的に内容を変更せずにそのまま転載するものです。
補足終わり


最近、画像ファイルを用いたクロスサイト・スクリプティングが注目されている。本稿では、画像を悪用したXSSについて説明した後、対策方法について解説する。

画像によるXSSとはどのようなものか

Internet Explorer(IE)の特性として、コンテンツの種類を判別する際に、レスポンスヘッダ内のContent-Typeだけでなく、コンテンツの内容も判断基準にしている。このため、Content-Typeが例えばimage/gif(GIF画像)となっていても、中身がHTMLであればHTMLと解釈して表示する。このため、JavaScriptが埋め込まれていたら、JavaScriptを実行することになる。 例を示そう。このファイル(html.gif)は、拡張子は.gifなので、ダウンロードするとContent-Typeはimage/gifとなるが、内容はHTMLでありJavaScriptが埋め込んである。これをIEとFirefoxで表示した例を以下に示そう。
IE7での表示
Firefoxでの表示
IEが、状況によってはContent-Typeを無視して、画像をHTMLと解釈し内部に埋め込まれたJavaScriptを実行してしまう。これによるXSSが成立する場合がある。典型的な状況とてしは、画像のアップロード機能を有するアルバム・サービスのような場合だ。たとえば、はてなの画像アップロードシステムには、この脆弱性があったとして、対応がなされたことがある。 下図は、画像によるXSSの模式図である。攻撃者(左側のサングラスの人物)は、攻撃用JavaScriptを仕込んだ画像を、maitter.com画像アップロードシステムを利用して登録・公開し、この画像のURLをダウンロードするよう、メールやブログなどで誘導する。被害者がこの誘導に引っかかって画像を閲覧すると、JavaScriptが実行され、maitter.comドメインのCookieが盗難されるなどの被害にあう。


先に示したhtml.gifは内容はhtmlテキストだけという素朴なものだが、イメージそのもの、パレット、コメントなどにhtmlやJavaScriptを仕込む手法が知られている(*1)。

対策の検討

IEがコンテンツの種類をどのように判別しているかは公開されていないようだが、たとえば2007年10月10日に公開されたマイクロソフト セキュリティ情報 MS07-057には以下のような記述がある。
PNGs が MIME スニッフィング アルゴリズム の jpg/gif と同じ状態に昇格されます。例: サーバーが png のコンテンツの種類を送信した場合、Internet Explorer は magic-byte (マジックバイト) のテストを実行し、成功した場合、権限のあるサーバーの MIME の種類として処理します。
意味のとりにくい日本語(機械翻訳?)だが、どうもコンテンツの種類判別にIEがマジックバイトを利用しているらしいことがわかる。

マジックバイトとは

マジックバイトとは、画像などのフォーマットを判別しやすいように、ファイルの先頭の数バイトに置かれた固定のデータである。GIF形式の場合であれば、"GIF87a"あるいは"GIF89a"で始まることになっており、先頭6文字を見ればGIF形式であることが容易に判別できる。下表に、主な画像形式のマジックバイトを示そう。
画像形式BMPGIFJPEGPNG
マジックバイトBMGIF8[79]a\xFF\xD8\x89PNG\x0D\x0A\x1A\x0A

IEのコンテンツ種類判別はContent-Typeとマジックバイトの組み合わせ

以上の情報などから、どうもIEのコンテンツ種類判別は、Content-Typeとマジックバイトの組み合わせにより決まるように思える。これを検証するために、主要な画像フォーマット4種(BMP、GIF、JPEG、PNG)のContent-Typeとマジックバイトの全ての組み合わせを試してみた。検証用に用いたサンプルデータは、マジックバイトとJavaScriptを組み合わせただけの簡単なものである。一例を下図に示す(GIF形式の偽画像)。
結果は下表の通りである。表で、「HTML」となっている欄はHTMLと判別され、JavaScriptが起動するもの、「画像」となっているのは、画像と解釈しようとしたが表示できないため「×」表示となることを示している。これらはリンクとなっていて、実際のサンプルデータで試すことが出来るようにした。

画像種類マジックバイトContent-Type
image/bmpimage/gifimage/jpegimage/png
BMPBMHTMLHTMLHTMLHTML
GIFGIF87aHTML画像HTMLHTML
JPEGxFFxD8HTMLHTML画像HTML
PNG\x89PNG\x0d\x0a\x1a\0aHTMLHTMLHTML画像(*2)
この表から分かることは以下の二点である Content-Typeとマジックバイトは同一の画像フォーマットに対応したものでなければならず、不一致だとHTMLとみなされる(可能性がある) BMP形式に関しては、Content-Typeとマジックバイトの両方を揃えても、なおHTMLとみなされる場合がある なお、マジックバイトのContent-TypeからPNG形式を判定するようになったのは、前述のようにMS07-057(2007年10月10日)適用後のことであるので、ちょうど2ヶ月前からのことである。

対策:ではどうすればよいか

従来、画像XSSの対策には、イメージを他の形式に変換してから元の形式に戻す、あるいは竹迫氏のmod_imagefightのように画像中にXSS防御文字列を挿入するなど、さまざまな手法が提案されている。しかしながら、対策として不十分であったり、画質や処理速度の低下を招くなどさまざまな副作用があった。 マイクロソフトがMS07-057にてPNG画像のコンテンツ種別判定を改善してくれたことから、Content-Typeとマジックバイトの判定のみで、画像XSSを予防できる可能性が高くなった。 これでもまだ、BMP形式については改善がされておらず、画像XSSの危険性が残っている。コレに対しては、BMP形式画像のアップロードを許容する場合はPNGに変換して公開するというガイドラインを提案したい。BMP形式は通常画像が圧縮されていないので圧縮して公開した方が好ましいこと、BMPの特性(色数、可逆性など)はPNGで全てカバーしていることがその根拠である。 結論としては以下のようになる。 画像のマジックバイトとContent-Typeが同一の画像フォーマットに対するものであることを確認する MS07-057を適用する(ようにユーザに促す) 例えば、BMPを避ける。あるいはPNGに変換して公開する。

謝辞:
画像埋め込みのHTML/JavaScriptについては、 はせがわようすけ氏からさまざまな情報を教授いただいた。記して、感謝の意を表する。

脚注
*1:JavaScriptを埋め込んだ画像を作ってみました参照
*2:MS07-057を適用していないとHTMLと解釈される

参考文献:
マイクロソフト セキュリティ情報 MS07-057
2007-07-15 - T.Teradaの日記 - 画像へのPHPコマンド挿入
TAKESAKO @ Yet another Cybozu Labs: LL魂お疲れ様でした[LLSpirit]
yohgaki's blog - 画像ファイルにPHPコードを埋め込む攻撃は既知の問題

本日のツッコミ(全3件)

□ はせがわ (2007年12月10日 13:54)
はてなでの2004年の対策は、MS07-034のmhtml:に対する対策だったはずです。mhtml:をURLに付与することで、あらゆるリソースがRFC557のMHTML形式のドキュメントであると強制可能だったため、画像でも何でもbase64なスクリプトが埋め込み可能でした(なのでスタイルシート内でboundaryを削除している)。IEがContent-Typeを無視するというのとは、表面的・結果的には似ていますが、原理的には異なるものでした。

□ hoshikuzu (2007年12月23日 16:26)
まさしくアレはmhtml対策でした。はてなに報告した人より。

□ えむけい (2008年08月30日 05:49)
IE8でサンプルデータを試してみたらBMPはすべてtext/plainで(JavaScriptは起動せず)、GIF/JPG/PNGはすべて壊れた画像アイコンで表示されました。
ようやく画像XSSも過去のものになりそうです。

2007年11月26日月曜日

DNS Rebinding ~今日の用語特別版~

補足

この記事は旧徳丸浩の日記からの転載です。元URLアーカイブはてなブックマーク1はてなブックマーク2
備忘のため転載いたしますが、この記事は2007年11月26日に公開されたもので、当時の徳丸の考えを示すものを、基本的に内容を変更せずにそのまま転載するものです。
補足終わり


楽天テクノロジーカンファレンス2007にて、カーネギーメロン大学日本校武田圭史先生の講演を聴講して、DNS Rebindingの説明がとても分かりやすかったので、ここに再現を試みる(文責は徳丸にある)。

DNS Rebindingとは

DNS Rebindingは、DNSの返すIPアドレスを巧妙に変化させることにより、JavaScriptやJavaアプレットなどのsame origin policyを破り、インターネットからローカルネットワーク(通常外部からはアクセスできない)などに対してアクセスする手法をいう。

攻撃に必要なもの

攻撃者は、自分のコントロール可能なドメイン名(以下の例ではtokumaru.orgを用いる)を持っているおり、かつそのドメイン名のWebサーバー(以下、罠サーバーと呼ぶ)があること。

攻撃のシナリオ

DNS Rebindingは受動攻撃の一種であり、攻撃者は、攻撃対象にアクセス可能なユーザに対して、メールなどにより罠サーバー(ここでは、tokumaru.org)に誘導する。罠サーバーに誘導された後の流れは以下の通り。

画像の説明 (1)↑tokumaru.org のIPアドレスをDNSに要求
(2)↓210.188.204.136 (TTL=5秒)を返す
(3)↑HTMLコンテンツを要求
(4)↓HTMLコンテンツを返す
(5) 10秒後に、HTML中に埋められたJavaScriptがtokumaru.orgのコンテンツを要求(same origin policyには違反しない)
(6)↑すでにDNSのキャッシュは無効なので、再びtokumaru.org のIPアドレスをDNSに要求
(7)↓今度はプライベートアドレス 172.21.21.11(TTL=1日)を返す
(8)←JavaScriptは、ローカルネットワーク内のWebサーバー(172.21.21.11)にコンテンツを要求する
(9)→ローカルサーバー上のコンテンツが返る
(10)↑form.submit()などを用いて、(9)の情報を罠サーバー(210.188.204.136)に返す

上記のように、DNSのTTLを極端に短く(上記例では5秒)設定することにより、同一ドメイン上のコンテンツを要求する際にDNSサーバーへの問い合わせを再度実行させ、二番目の問い合わせに対しては、攻撃対象(外部からはアクセスできない)のIPアドレスを返すことがミソである。これにより、見かけ上はsame orign policyに抵触することなく、特定のユーザを中継することで、ローカルネットワークなどの資源にアクセスすることができる。

DNS Rebindingについては、金床氏の研究が有名で、Black Hat Japan 2007にて、金床氏の研究が発表された。これによると、JavaアプレットやFLASHのSocketを利用することにより、任意のプロトコルに対しても、ローカルネットワークに対する情報の収集や攻撃などが可能である。 金床氏による、DNS Rebindingのデモページはこちら

対策(DNS Pinningなど)は後で。 tokumaru.orgは単なる例なのに、徳丸浩までなんか悪い奴に見えますね。しまった。

自作コンパイラの部屋 > 用語集 > DNS Rebinding

追記(2010/03/01)

携帯電話の「かんたんログイン」に対するDNS Rebindingによるなりすまし脆弱性については「iモードIDを用いた「かんたんログイン」のDNS Rebinding脆弱性」を参照されたい。また、現実に発見された脆弱性については「ケータイtwitter(twtr.jp)においてDNS Rebinding攻撃に対する脆弱性を発見・通報し、即座に修正された」で報告している。

本日のツッコミ(全7件)

□ yamagata21 (2007年11月26日 11:00)
(7) で レスポンスを TTL=1日にしてしまうと、(10) の情報の送信先が 172.21.21.11 になってしまう気がしないでもありませんw (サブドメインを使い分けるか、TTL を短くするかしないとダメかも知れませんね~。)

□ 金床 (2007年11月26日 11:59)
そこはサブドメ使い分けでしょう(゜д゜)
なんならまったく別ドメでもおk(゜д゜)

□ 徳丸浩 (2007年11月26日 12:28)
金床さんからもコメントありますが、データの戻しにform.submit()を使うという前提であれば、別ドメインでもいいし、IPアドレスでもいいですよね。Formのaction先はsame orign policyの制約を受けませんので。

□ yamagata21 (2007年11月26日 13:15)
図と説明が、罠サーバー(tokumaru.org)宛になってたのでツッコミ入れてみただけです。(^-^;

□ 徳丸浩 (2007年11月26日 20:55)
(10)の中で、罠サーバーにIPアドレスを付記しました。

□ yamagata21 (2007年11月26日 23:44)
お手数をおかけしました~。ありがとうございます♪

□ Hash (2012年11月18日 05:03)
> (7)↓今度はプライベートアドレス 172.21.21.11(TTL=1日)を返す
とありますが, 長い時間を指定するのは何故でしょうか? 5秒から変更する意義がわかりませんでした.

2007年7月17日火曜日

JavaScriptを埋め込んだ画像を作ってみました

補足

この記事は旧徳丸浩の日記からの転載です。元URLアーカイブはてなブックマーク1はてなブックマーク2
備忘のため転載いたしますが、この記事は2007年7月17日に公開されたもので、当時の徳丸の考えを示すものを、基本的に内容を変更せずにそのまま転載するものです。
補足終わり


寺田さんの日記に触発されて、JavaScriptを埋め込んだPNG画像を作ってみました。
注意:この画像にはJavaScriptが埋め込んであります。
私の娘が書いた絵ですここをクリックすると、JavaScriptが発動し、あなたのクライアント上でCookieの値を表示します(IE限定です)

追記 ImageMagic の convertコマンドでPNG→GIF→PNGと変換しても、JavaScriptは削除されませんでした。これは、T.Teradaさんの解説の追試に過ぎませんが、一応ご報告まで。

追記(2007/10/10)

MS07-057のパッチを適用したところ、PNG画像は正しく画像として認識されるようになり、JavaScriptも起動しなくなることを確認しました。手元のIE7にて確認しております。


本日のツッコミ(全3件)

_ はせがわ (2007年07月17日 16:43)
GIF/JPEGでもこのようなXSSする画像はありますでしょうか?

_ 徳丸浩 (2007年07月17日 18:18)
はせがわさんのブックマークを見て、公開をすることにしました:-)なんだ、もう公開されているんだ、って。
GIFは試しましたが、画像として表示されました。
JPEGはこの方法だと駄目だと思います。JPEGにはカラーパレットがありませんので。

_ はせがわ (2007年07月17日 22:52)
ありがとうございます。そうですね、2年前とは状況が変わってますね。また時間のあるときにいろいろ書いてみます。


フォロワー

ブログ アーカイブ