2008-08-22 XSS
●今こそXSS対策についてまとめよう
沢出水(さわ いずみ)さんからトラックバックを頂戴した。
元々はホワイトリスト方式の優位は神話というエントリでホワイトリストはどう作る?を引用(批判)した事が発端の模様です。
一見真っ向対決しているようなので興味深く読ませていただいたのですが、正直、両者の主張の違いがわかりません。
どちらもXSS等インジェクション系の対策としてはアプリケーションで入力値が正しい形式の範囲内かチェックし、出力時に必要なエスケープ処理を行う、という結論に思えるんですけど…
[ホワイトリストとブラックリストより引用]
ご指摘の通りで、XSS対策は入り口でのバリデーションと表示(HTML組み立て)時のエスケープだ。しかし、元ブログの主題はホワイトリストとブラックリストの比較なので、「ただ、表面的に文章を追っただけでは『何をホワイトリストと呼ぶのか』という部分がだいぶ違う印象を受ける」というところこそが論点だったのだ。
しかし、本当に大切なことはXSS対策をどうするかなので、この機会にXSS対策についてまとめてみようと思う。
XSS対策の基本はバリデーション+エスケープ
「安全なウェブサイトの作り方改訂第3版」などでも指摘されているように、XSS対策の基本はバリデーションとエスケープだ。このうち、根本対策はエスケープの方で、バリデーション(検査)の方は保険対策となる。
入力値チェックはアプリケーションの仕様に従う
昨日の日記(プログラミングではホワイトリスティングが基本ではない)でも書いたように、入力値チェックはアプリケーションの仕様に従っておこなうしかない。その理由については以下のエントリで検討している。
当たり前のことなのだが、入力値検査を仕様よりも厳しく行うことはできない。そして、たいていのWebアプリケーションには、自由形式の入力欄がある(氏名や住所など)。だから、入力値検証だけでは、XSS対策として十分ではない。
XSS対策としてのエスケープが本質だがバリエーションが多い
入力値検査はXSS対策としては保険的な対策であるので、出力時のエスケープが本質的な対策となる。このテーマについては、過去さまざまな機会で取り上げてきた。
- 第2回 対策遅らせるHTMLエンコーディングの「神話」:ITpro
- 第3回 まだまだあるクロスサイト・スクリプティング攻撃法:ITpro
- XSS対策:どの文字をエスケープするべきなのか
- XSS対策:JavaScriptなどのエスケープ
- XSS対策:JavaScriptのエスケープ(その2)
- XSS対策:JavaScriptのエスケープ(その3)
- XSS対策:JavaScriptのエスケープ(その4)
- 画像ファイルによるクロスサイト・スクリプティング(XSS)傾向と対策
上記1.と2.では、エスケープがXSS対策の本質であることを説明したかったのだが、読者の便宜のためにXSS攻撃のバリエーションを多数紹介したところ、攻撃手法の方に関心が移ってしまったのではないかと懸念が生じた。それを補足するために、「XSS対策:…」で始まるエントリでは、XSSが発生する根本的な原理に立ち戻り、どの文字をどのようにエスケープするべきなのか、その考え方も含めて説明した。また、7.では文字エンコードに付随する問題に対する議論、8.では画像XSSに対する対策を説明を行っている。とくに8.は、とても手間の掛ったエントリなので、もっと広く読んでいただけたらなと思う(私はブログでも手抜きはしない流儀だ)。
ホワイトリストもプロアクティブも出てこない
過去のエントリを引用する形で、XSS対策の考え方を説明した。バリデーションは(ホワイトリストではなく)仕様に従って行う、エスケープはホワイトでもブラックでもない、ということで、プログラミング上のXSS対策にはホワイトリストもブラックリストも出てこない。プロアクティブという用語にしても、抽象的かつあいまいな用語で、セキュリティ業界ではバズワードと化しているし、そのような「怪しげな」用語を用いずに、具体的で意味の分かりやすい言葉を選んで説明すればすむことだ。
大切なことは原理から理論的に考えること
私が一貫して訴えていることは、脆弱性の根本原因を探り、そこを対策すべきということだ。ITproでの連載や、最近ではWASForumのカンファレンスでも説明したことだが、インジェクション系脆弱性の根本原因はリテラル(定数)が枠をはみ出し、命令として解釈されることだ。であれば、リテラルとしての正しいエスケープ方法を勉強して、リテラルの枠をはみ出さないようにすればよい。
大垣さんのブログには、「出力時に過剰とも言えるエスケープ処理」とあるが、多くの文字をエスケープすれは安全になるというものでもない。HTML上の場所に応じた適切なエスケープが必要だ
例を挙げよう。以下は、onloadイベントハンドラを動的生成している例である。
#!/usr/bin/perl
use strict;
use CGI;
my $query = CGI->new();
my $a = "abc');alert(document.cookie)//";
my $ae = $query->escapeHTML($a);
print <<END;
Content-Type: text/html; charset=UTF-8
<html>
<script>
function a(b) {}
</script>
<body onload="a('$ae')">
</body>
</html>
END
PerlのescapeHTMLメソッドでエスケープしているし、上記CGIが生成するソースは、
<body onload="a('abc');alert(document.cookie)//')">
と一見よさそうに見えるかもしれないが、上記はalert関数が実行され、Cookie値が表示される。その理由は、JavaScriptに処理が渡る前にHTMLデコードされ、以下のようなスクリプトが実行されるからだ。
a('abc');alert(document.cookie)//')
正解は、いったんJavaScriptの文字列リテラルとしてエスケープしてから、HTMLのエスケープを行うことだが、よりよいガイドラインは、イベントハンドラやSCRIPT要素など、JavaScriptの動的生成を行わないことだ。
上記は、JavaScriptとしてのエスケープを怠っていることが原因であるので、HTMLエスケープをいくら「過剰」に行っても意味はない。
原理から正しく学ぶということは、HTMLなりJavaScriptの文法をしっかり学ぶことでもある。またエスケープを正しく行わないと、セキュリティ上の問題以外に、表示がおかしくなるなどの不具合が生じる。これはプログラマとして必須の知識なのだ。
攻撃技術の進歩に備えるためにこそ、原理からの対策を
大垣さんの指摘のように、攻撃側の技術は年々進歩している。だからといってXSS Cheat Sheetなどの攻撃手法から逆算的に防御方法を学ぶことはできない。仮にそうすれば、どうしても対症療法的になるので、大垣さんの言われる「プロアクティブな」防御法にはならない。対症療法だからこそ、「技術革新の早いWebシステムで具体的なホワイトリストの作成方法を書いても無意味となる可能性がある」のだろう。
そうではなく、原理から対策方法を学ぶことで、攻撃技術の進歩に一喜一憂する必要はなくなるのだ。そして、XSS Cheat Sheetなどの攻撃手法のドキュメントは、原理にもとづく対策方法に抜けや勘違いがないかをチェックする目的で使用すればよいだろう。たとえば、文字エンコードの問題が考慮から漏れていたとして、攻撃方法を学ぶことは、そのような抜けを見つけるのに役立つ。しかし、考慮不足が判明したら、その際には再び原理に立ち戻って対策を考えるのだ。そうすれば、XSS対策:JavaScriptのエスケープ(その4)で指摘しているように、文字エンコードの問題に「過剰なエスケープ」などで対症療法するのではなく、文字エンコードの誤りそのものをチェックするという正しい対策が生まれるのだ。
早速、大垣さんと徳丸さんから
前回のエントリに対するフォローをいただきました。
・ホワイトリストとブラックリスト - Proactiveセキュリ..