| SQLインジェクション対策はおすみですか? 開発開始時点からのコンサルティングから、公開済みWebサイトの脆弱性検査、 脆弱性発見後の適切な対策まで |
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)で指摘しているように、文字エンコードの問題に「過剰なエスケープ」などで対症療法するのではなく、文字エンコードの誤りそのものをチェックするという正しい対策が生まれるのだ。
2008-08-20 ホワイトリスト
●プログラミングではホワイトリスティングが基本ではない
大垣さんのブログにて反論をいただいた。
長文のエントリで、「暇な方だけお付き合いください」と書かれている。あいにく締め切りを抱えていて暇ではないのだが、名指しで反論されている以上、放置するのも失礼なので、簡潔にコメントしたい。
私は、一応セキュリティの専門家の端くれのつもりで、XSS Cheat Sheetをはじめ、大垣さんや金床さんの本にも目を通している。しかし、大垣さんの上記2エントリは、私にはさっぱり理解できない。以下、プログラミングの局面で、XSS向けのホワイトリストが作成可能なのかというポイントに絞って議論する。
プログラミングの基本は仕様に忠実であること
大垣さんが具体的な「ホワイトリストの作り方」を提示してくれないので、どのようなホワイトリストをイメージしてよいのか分からない。私の主張は、以前ブログに書いている内容(アプリケーションの先頭で行う入力値検証は業務要件により行うべし)の通りだ。つまり、仕様に忠実に入力値のバリデーションを行えというだけであって、まさかこれが理解できないソフトウェア・エンジニアはいないと思う。それだけだと、XSSの対策にならないという指摘には、はいその通りと答えるしかない。それゆえ、入力値のバリデーションはXSSの保険的対策にはなっても、根本対策にはなりえない。XSS対策としては、それぞれの局面で最適なエスケープなどにより行うのが正解と考える。
大垣さんと金床さんの主張はまったく異なる
次に、大垣さんは、(私がかつて引用した)金床さんの本と大垣さんの主張は同じだという意味のことを書いておられるが、本当にそうだろうか?
金床本の3章XSS ウェプアプリケーション側の対策(P61)には、以下のような記述がある。
【前略】まずはホワイトリスト形式でのアプローチを検討するべきである。
電話番号や年齢、メールアドレスなどのように、使用可能な文字を限定できる項目について考える。これらの項目ではホワイトリスト形式でデータの確認を行った上でHTML中に埋め込むことで、確実なXSS対策を行うことが可能になる。
一方、名前や住所に代表されるような、やや自由度が高い項目も存在する。これらの項目についてはホワイトリスト形式を採ることは難しいため、ブラックリスト形式でのアプローチ、つまりエスケープ処理を行う必要がある。
これならよく分かる。しかも、表現方法は違っていても、現実的なプログラミングレベルでは、徳丸方式と金床方式は実質的な差はない。アプリケーションの仕様として数値や英字などに限定できる場合は、その文字種であることをチェックする(=業務要件チェック)が、たまたまホワイトリスト検査にもなっているというだけのことである。
そして、金床氏が書かれているように、汎用的なホワイトリストを書くことは困難であるので、仕様上自由度の高い入力欄についてはブラックリスト(あるいはエスケープ)するということで、徳丸のやり方と実質的な差はない。強いえて言えば、徳丸方式は、ホワイトリスト検査が可能なパラメタについてもエスケープを要求しているので、より念がいっているわけだし、大垣さんがご本(P26など)に書かれている「多重のセキュリティ」という考えにもかなうだろう。
一方、大垣さんの主張はこうだ。
ホワイトリストの作り方にはケースバイケースで幾つもの指針が有りますが、最も重要と言える指針は
「許容する入力・出力は必要最低限だけ留め、許容した入力・出力は確実に安全である事を保証し、もし不正な入力があった場合は必ず記録し、必要であればプログラムの実行を停止する」
です。これは私の本でも書いている事です。稀に誤解されるので書きますが「プログラムの実行を停止する」とは入力間違いで実行を停止するのではなく、不正な入力で実行停止する事を意味します。
[ホワイトリストはどう作る?より引用]
「入力間違い」と「不正な入力」をどう区別をつけるのだろう。しかもホワイトリストで。大垣さん、私は大垣さんのご本も持っていますので、長文の説明が大変であれば、本の何ページ何行目に書かれているか、ご教授いただけませんか?
それと次の内容は気になったのだが、
私はいつも基本的に能動的なセキュリティ対策を選択するようにお勧めしています。能動的なセキュリティ対策とは全ての入力値の厳格なバリデーション処理であり、出力時に過剰とも言えるエスケープ処理です。いわゆるホワイトリスティングと呼ばれるような対策です。
[ホワイトリストはどう作る?より引用]
入力値のバリデーションはよいとして、出力時のエスケープ処理は普通ホワイトリストと呼ばないのではないか?金床さんの本が、エスケープをブラックリストと説明していることに私は賛成ではないのだが、強いて言えばブラックリスト寄りの考え方だと思う。大垣さんの本でもhtmlspecialcharsを使った対策を説明されている(P144)。これは、「安全な文字を列挙する」アプローチではない。
大垣さんは、セキュリティの専門家としてXSSのホワイトリストの実物を示すべきだと思う。書き下ろすのが大変であれば、書籍などの参照でもよいので示していただきたい。
2008-08-19 PHP
●[php]session_set_save_handlerのパストラバーサルで任意コマンドの実行が可能
昨日の日記(session_set_save_handlerリファレンスマニュアルのサンプルにパス・トラバーサル脆弱性 )で、PHPの公式リファレンスマニュアルに出ているsession_set_save_handlerサンプルにはパストラバーサル脆弱性があることを報告しましたが、その影響度について書き漏らしていて、影響度を過小に受け取られることに気がつきましたので補足します。
このパストラバーサルは情報漏えいよりは書き込み・破壊の影響の方が現実的というのはその通りなのですが、Web公開領域のファイルを書き換えられるというリスクを報告していなかった。ここで、HTMLやJavaScript、PHPスクリプトを書き込み、実行できるという問題があります。
以下のコード(a.php)で検証してみました。
// session_set_save_handlerのサンプルコード
// 以下は呼び出し部分
session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
session_start();
$_SESSION['a'] = $_GET['a'];
echo "<body>done<body>";
?>
ご覧のように、クエリストリングaの値をそのままセッションに保存しています。非常に単純化していますが、現実のWebアプリケーションを極小化したモデルです。
ここで、Cookie PHPSESSIDの値を以下のようにセットします。b.phpの部分は、このサーバー上に存在するファイル名を指定します。
PHPSESSID=/../../../../../var/www/html/php/b.php
この状態で、以下のURLでa.phpを起動します
http://host-name/php/a.php?a=<script>alert(document.cookie);</script>
セッションデータはb.phpとして格納され、内容は以下のようになります。
a|s:40:"<script>alert(document.cookie);</script>";
すなわち、今後b.phpにアクセスしたユーザは、ブラウザ上でJavaScriptが起動されることになります。
同様にして、PHPのスクリプトを書き込むこともできます。例えば、以下のようにa.phpを呼び出します。
http://host-name/php/a.php?a=<%3Fphp+echo`find`;%3F>
%3Fは、「?」を表します。すなわち、PHPスクリプト中で、バッククォートによりfindコマンドを実行するスクリプトが書き込まれたことになります。攻撃者はこのb.phpにアクセスすることにより、findコマンドを実行でき、同様にして、ターゲットのwebサーバー上で任意のコマンドを実行できることになります。
このように、書き込み可能なパストラバーサルは極めて危険な脆弱性であり、該当するアプリケーションは直ちに対策をとることをお勧めします。
2008-08-18 PHP
●PHP:session_set_save_handlerリファレンスマニュアルのサンプルにパス・トラバーサル脆弱性
PHPのsession_set_save_handlerのリファレンスを眺めていて、ふと、これはパス・トラバーサルの脆弱性があるのではないかと思いました。
function read($id)
{
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id"; // ← ファイル名の組み立て
return (string) @file_get_contents($sess_file);
}
function write($id, $sess_data)
{
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id"; // ← ファイル名の組み立て
if ($fp = @fopen($sess_file, "w")) {
$return = fwrite($fp, $sess_data);
...
session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
...
ここで、readはセッションデータを読み出す関数、writeはセッション値を保存する関数で、session_set_save_handlerでセットしておくものです。コメントで「ファイル名の組み立て」と示している部分でファイル名をセットしていますが、変数$idの値(セッションID)の値が未検証のまま使われています。
問題は、PHP処理系にてセッションIDの値がどの程度チェックされるかです。よく知られているように、PHPにはSession Adoptionの問題があり、素のままの状態では外部からCookie PHPSESSIDにより指定されたセッションIDをそのまま受け入れます。私が色々な文字で試した範囲では、「<」、「>」、「'」、「"」に関してはチェックが行われており、これらの文字がPHPSESSIDに含まれていた場合には、セッションIDの再設定が行われました。一方、それ以外の文字、とくに「/」、「.」、「\」などは特にチェックされないまま素通ししてしまうので、パストラバーサルの脆弱性となります。
このサンプルを流用しているようなケース、あるいは類似の処理を行っている場合(session_set_save_handlerにて、ファイルによるセッションデータ保存を行っている場合)には、この問題の影響を受けます。
この問題の影響範囲ですが、情報漏えいの可能性は低いと考えられます。パス・トラバーサルの技法で任意のファイル名を指定することは可能ですが、たまたまPHPのセッション保存形式と適合する形式のファイルでなければ、読み出しは行われないからです。そのようなファイルがたまたまWebサーバー上に存在し、かつそのファイル名が類推できる場合に限られますが、そのようなケースは想定しにくいと考えます。
一方、ファイルの破壊(書き込み)については、権限さえあれば任意のファイルを指定して破壊できるので、ある程度の影響が考えられます。UNIX系のOS上でPHP(Apache)を実行するユーザの権限で書き込みが可能なファイルは一般的には限定されますが、権限設定がゆるい場合には影響を受けます。Windows上でPHPが稼動している場合には、影響はもう少し広いと考えられます。
対策について。Webアプリケーション側でこの問題に対応するには、さしあたっては、セッションIDの妥当性確認を行えばよいと思います。セッションIDが英数字のみで構成されているか、あるいは16進文字列として妥当であるかをチェックすれば、パストラバーサルは防げます。
また、この問題はPHPがSession Adoptionの問題があることに起因していますから、Strict Sessin Patchを適用すれば、上記問題も解消されると思います。しかし、その場合でも、防衛的意味でパス・トラバーサル対策としての文字種チェックはしておくべきでしょう。
session_set_save_handlerを使わない状態のPHPでは、パス・トラバーサルの問題は起きないようです。前述の中途半端な文字種チェックといい、session_set_save_handlerを使う場合と使わない場合の挙動の違いといい、ちょっと「イラっ」と来たことを告白します。
なお、この問題を一応脆弱性情報としてIPAに届出ましたが、独立したソフトウェア製品ではないという理由で不受理となりましたので、ここに公開し、PHPの開発者に注意を喚起するものです。
Windows上のPHP 5.2.6およびCentOS 5.2上のPHP 5.1.6で検証しました。
2008-07-22 そろそろWAFに関して一言いっとくか
●三重苦を乗り越えてWAFが普及するための条件とは
PCIデータセキュリティ基準(PCIDSS)がWAF(Web Application Firewall)について言及していることなどから、最近再びWAFへの関心が高まっている。一方、WAFは、一部のユーザや専門家に非常に評判が悪い。なぜ、そのようなことになるのか。本稿では、WAFの基本機能を説明した上で、その限界と運用上の問題を指摘し、今後のWAFの使い方について私見を述べる。
今回とりあげるWAFの基本機能は、以下の三種類である。
- 入力値検査
- 画面遷移のチェック
- hiddenフィールド操作の防止
WAFの機能(1)入力値検査
すべてのWAFの備える基本機能は入力値検査である。これは、パラメタ(クエリストリング、POSTパラメタ、Cookieなど)に対するホワイトリストあるいはブラックリストによる検査を行うものだ *1。SQLインジェクションやXSSなどインジェクション系の脆弱性対策は、この入力値検査が基本となる。
入力値検査-ホワイトリスト検査
IPS(Intrusion Prevention System)が基本的にブラックリスト検査のみであるのに対して、WAFがホワイトリスト検査もできることは、WAFの特徴といえるだろう。ホワイトリスト検査は、Webアプリケーションの入力パラメタ一つ一つに対して、取りうる値の集合を正規表現などで定義し、検査の結果取り得る値から外れていたらリクエストをブロックするというものだ。前述のIPSに対するWAFの「優位」としてベンダーが大々的に宣伝することが多いのがこれだが、実際のサイトに適用するとなると以下のように問題が多い。
- ホワイトリスト検査が可能なパラメタは限られている
- ホワイトリスト検査の対象パラメタと検査内容は個別に指定する必要があり煩雑である
- ホワイトリスト検査は本来アプリケーションで行うべきもの
- ユーザビリティの低下を招く可能性
以下、順に説明しよう
ホワイトリスト検査が可能なパラメタは限られている
ホワイトリスト検査が可能なパラメタは、数値や英字など特定の文字種のみを受け入れるパラメータに限られる。具体的には郵便番号、電話番号、クレジットカード番号、ユーザID、メールアドレスなどだ。これら以外の自由記述形式のパラメタに対してはホワイトリスト検査はできない。
ホワイトリスト検査の対象パラメタと検査内容は個別に指定する必要があり煩雑である
前のところで説明したようにホワイトリスト検査可能なパラメタは限られており、かつパラメタ毎に文字種や文字列長などが異なっている。このため、WAFの機能として、URLごとのパラメタを列挙した上で、パラメタ毎に検査方法を定義できるようになっている場合が多い。中規模以上のWebアプリケーションには膨大な数のパラメタがあるため、この設定はかなり煩雑となる。
また、パラメタ毎の文字種などの仕様書があればよいが、なければアプリケーションをリバースエンジニアリングの手法で調べた上でWAFの設定を行う必要がある。文字種の制限が仕様書に記載されていることは期待できない。なぜなら、仕様書に書いてあるくらいならアプリケーション側で文字種チェックが実装されているはずであり、わざわざ手間を掛けてWAF側で設定する必要はないからだ。
最近の高機能WAFは「学習機能」と称してホワイトリストの自動設定ができるとうたっている場合が多いが、私の経験および見聞きした範囲では、学習機能がうまく動いて設定が自動化できたという話は聞いたことがない。最終的には人手によるチェックと修正が必要と考えた方がよいだろう。
ホワイトリスト検査は本来アプリケーションで行うべきもの
先にも少し触れたように、ホワイトリスト検査は本来アプリケーション側で行うべきものだ。郵便番号欄を例に説明すると、3桁および4桁の入力欄を用意して、数値以外の文字が入力されていればエラー表示して再入力を促すことになる。それくらいはアプリケーション側でやるべきだし、WAFでチェックすると後述のようにユーザビリティ的に問題となる可能性が高い
このように書くと、いやWAFで検査するのはラジオボタンなど選択式の入力欄に想定外の文字が入っている場合が主であって、郵便番号はアプリケーション側の検査でよく、この使い方であればユーザビリティ上の問題はないという反論がくるかもしれない。しかし今度は、ホワイトリスト検査が可能なパラメタのうち、どれがアプリケーションでの検査、どれがWAFでの検査とするかを詳細に調べ上げなければならない。これを学習機能で自動することは相当難しいだろう。というわけで、人手で設定するのは面倒だし、自動化には困難が伴う。
ユーザビリティの低下を招く可能性
前に書いたことと関連するが、ユーザの誤入力がWAFのホワイトリスト検査に引っかかった場合、ユーザに適切なナビゲーションを提供することは難しいだろう。そのためにユーザビリティが低下する可能性がある。
入力値検査-ブラックリスト検査
Webアプリケーションのパラメタのうち、ホワイトリスト検査ができない(しない)パラメタについてはブラックリスト検査をすることになる。この場合のブラックリスト検査の目的は、「アプリケーション要件としては禁止されていないが、脆弱性に対する攻撃(SQLインジェクションなど)が疑わしい入力をブロックする」ことになる。このため、ブラックリスト検査は、過剰検知と検知漏れの両方のリスクがある。
過剰検知の問題
ここでいう過剰検知(False Positive)とは、実際には攻撃目的の入力ではないのに、WAF攻撃とみなしてリクエストをブロックする場合を指す。Webアプリケーションの場合、アプリケーション要件的に禁止されていないものをブロックする*2わけだがら、ユーザにとっては不満の原因になる可能性が高い。
検知漏れの場合
検知漏れ(False Negative)とは、実際には攻撃を受けているにもかかわらず、見逃してリクエストを受け付ける場合を指す。すべての攻撃パターンを正規表現などで表現することは元々不可能であるし、検知漏れを少なくしようとすると、今度は過剰検知が増えるという結果になる。
ネガティブか、ポジティブか……それが問題だ
というわけで、LAC川口氏のコラムを引用させていただくことになるわけだが、脆弱性スキャナやIDSなど検査・監視系ツールの場合と異なり、過剰検知はユーザの実行を妨げ、ユーザビリティの低下に直結するため、少し緩めの(ブラック側に倒した)ルールにせざるを得ない。
そもそも入力時点検査での対応では無理がある - サニタイズ言うな
ここまで、ホワイトリスト検査とブラックリスト検査の特徴を説明してきた。ホワイトリスト検査は設定の手間が掛かることと、すべてのパラメタに適用可能ではない。ブラックリスト検査は過剰検知や検知漏れの可能性があり、総合的に判断して、入力値検査で完全な脆弱性対策とするのは無理がある。これは、セキュアWebアプリケーション開発の方法論としては既に結論が出た内容であって、XSSにせよ、SQLインジェクションにせよ、入力時ではなく、出力時(その値を使う時)に適切なエスケープなどにより対策すべき問題であるのに対して、WAFは出力時対応ができないからだ。言い換えれば、WAFはサニタイズ方法論で対応するものであって、元々限界があるのだ。
WAFの機能(2)画面遷移のチェック
すべてのWAFが備えているわけではないが、商用WAFの多くが、なんらかの画面遷移のチェック機能を持っている。これは、大きく分けて二種類あり、(1)外部からの入り口となるページをチェックする、(2)Webアプリケーション内の全ての遷移をチェックする、という方法がある。
まず、(1)の外部からの入り口のチェックについて説明する。右図は、Webで実装された業務システムを想定した画面遷移図である。入り口のページにログイン画面があり、その後は画面遷移に従って、業務メニューから各種の機能に遷移するという典型的な業務システムである。このようなアプリケーションの場合、外部からの入り口はログインページに限定すべきであり、その他のページに外部からダイレクトに遷移することは禁止するべきである。WAFによっては、この遷移制限の機能がある。
一見よさそうだが、このようなアプリケーションの場合、そもそも認証機能が正しく動いている限り、ログインページ以外には外部から遷移できないのであり、もしログインしていないユーザが遷移できたとすればそれは認証機能の重大な脆弱性である。脆弱性があるからWAFを導入するんだろうという突っ込みは予想できるが、こんな重要かつ基本的なところでWAFに頼らないと認証が正しく動かないのは問題であって、もし脆弱性があるならばWAFに頼らずに修正するしかないだろう。
一方、XSSやCSRFなど受動的攻撃への対策として、既にログインしている正規ユーザが外部から強制的に遷移させられるのを防止するという観点であれば、この機能に意味がある
次に、右の図は、ECサイトをモデル化したものである。この場合は、商品を選んでいる最中ではまだログインしておらず、かつ検索サイトなどからの遷移はむしろ歓迎するべきものであるので、WAFにより禁止するわけにはいかない。決済画面など一部のページは外部からの直接遷移を禁止してよい(禁止すべき)であるが、どのページは外部からの遷移を許可し、どのページは禁止するかという、一種のホワイトリストを定義することが煩雑となる。したがって、サイトの作りによっては、この「外部からの直接リンク禁止」機能を使える場合もあるが、多くのサイト(典型的にはECサイト)では、この機能を運用することはかなり労力が必要となる。
内部画面遷移をチェックするのも多大な労力が必要
画面遷移のチェック機能として、Webアプリケーション内部の遷移(狭義の画面遷移)をチェックできるものがある。そのためには、まず正しい画面遷移をホワイトリストとしてWAFに定義する必要がある。私は、コンサルタントとして非常に多くのWebサイトの検査に携わってきたが、画面遷移図がドキュメントとして完備しているサイトがそもそも少なく、タイムリーにアップデートされているサイトは皆無といってよかった。しかも、アップデートされた画面遷移図があったとしても、通常全ての遷移を網羅しているわけではなく、例外的な遷移(エラー時など)までは記述していないと思う。その理由は、例外的な遷移まで画面遷移図に書いていくと、非常に読みにくいものになってしまうからだ。仮に完璧な画面遷移図があっとしても、それをWAFに設定することは大変な労力であるし、Webアプリケーションの保守作業のたびにWAF側の設定をアップデートしないと、更新された機能が動かなくなる。
このような理由から、画面遷移のチェック機能は、現実には使われないケースが多い*3。
WAFの機能(3)hiddenフィールド操作の防止
これもすべてのWAFが備えているわけではないが、商用WAFの多くが備えている機能として、いわゆる「hiddenフィード改ざん」の検知とブロックがある。すなわち、hiddenフィールドやCookie、ラジオボタンなどの選択肢の値がクライアント側で改変されてないかチェックし、改変されていたらエラーとするものである。
この機能の実装方式としては、私の知る限り二種類ある。一つは、hiddenフィールドやCookieの値を暗号化して改変できなくする、あるいはハッシュ技術により、改変されたことを検知・ブロックすると言うものだ。この実装方式は、クライアント側のJavaScriptと相性が悪いことが多く、暗号化あるいはハッシュ値が付与された値をJavaScriptで正しく読み込みできなくなる場合がある。
もう一つの実装方式は、WAF側でセッション管理を行い、hiddenの元の値を覚えておき、ブラウザからのリクエストとWAF側で記録した値とを比較するというものだ。一見よさそうな機能だが、ユーザが戻るボタンを操作すると、WAFで覚えている値とhiddenの値の食い違いが発生しそうだ。前述の画面遷移チェックと組み合わせて使うとよいのかもしれないが、設定が大変なためお勧めできない。
また、最近のWebアプリケーションでは、JavaScriptによりHTMLの内容をダイナミックに書き換えることが普通になっているわけだから、hiddenの書き換えをブロックこと自体が、Webアプリケーションの進化の方向とは衝突するものであるように思う。
WAFの三重苦とはなにか
ここで、WAFの三重苦とは何かを説明しよう。
大半の商用WAFがホワイトリスト方式を標榜しているが、そもそもホワイトリストというものは、予め正しい内容を設定しておく必要があり、その元となる正しい仕様書がないからアプリケーション側の作りこみがもれるのだ。その肝心なところを直視しないでWAFに頼ろうとするユーザは、WAF購入後にたちまち現実に直面するし、WAF(最近はワフというらしいが)に対する不満を募らせることになるのだ。WAFが安いものであればまだしも、非常に高価な製品であるだけになおさらだ。というわけで、WAFの三重苦とは、こうだ。
割高で あてにならぬも 負担増
なんだか、はてなハイクのようになってしまったが、高価で、設定に手間が掛る割には、これで万全と言うものではないということだ。
三重苦を乗り越えWAFが普及するための私見
ここで、まとめとしてWAFに関する私見を述べよう。
まず、WAFはホワイトリスト方式であるという主張はやめた方がよい。そんな主張をするから誤解を招くのだ。ホワイトリストの元となる正しい仕様は、仕様書に書いてないといけないし、仕様書に書いてあることはアプリケーションとして実装しなければならないのだ。だとすれば、WAFとアプリケーションでホワイトリスト検査の二度手間をやることになってしまう。
一方、WAFのブラックリスト検査については、私は元々懐疑的だったが、その考えが少し変わってきた。「サニタイズ言うな」に基づく現代的なセキュアWebアプリケーション開発では、アプリケーション側ではブラックリスト検査をしない。だから、WAFとアプリケーションとで検査の重複はなくなるのだ。実運用に影響を与えない程度の控えめなブラックリストをチューニングして、それを全パラメータ共通で使用する。そうすれば、手間も掛らないし、機械化されたSQLインジェクション攻撃程度であれば十分な効力がある。それでも運用に影響が出るパラメタもある(ブログや掲示板など)が、そのパラメタだけはブラックリスト検査を除外して、その代わりしっかり脆弱性検査をして対策もアプリ側でとっておけばよい。
Webアプリケーションの脆弱性対策は、アプリケーションの作りこみで行うべきであるが、アプリケーションが大規模になるほど対策漏れの可能性は大きくなる。上記のようなWAFの運用であれば、アプリ側の対策漏れに対するセーフティネットとして働くことが期待できる。その理由は、WAFの細かい設定をしないからであり、WAFの細かい設定をすればするほど、WAF自体の設定漏れを心配しなければならなくなる。それは本末転倒であって、WAFに対して割り切った使い方をした方がかえって安全ともいえる。
また、WAFの低価格化は必須だ・・・というより、ホワイトリスト機能を使わないのであれば、安いWAFで十分だ。そうすると、割高、負担増の二重苦が消え、残りは「あてにならない」だけが残るが、セキュリティ製品なんてファイアウォール、IPS、ウィルス対策ソフト・・・いずれをとっても「あてになる」ものはないのだから、WAFにだけ完璧を期するわけにもいくまい。ブラックリストでの防御という点ではIPSと機能がかぶるが、ことWebアプリケーションの脆弱性に関しては、ブラックリスト機能だけ使ってもWAFのほうが検知能力が高い。ここに、WAFの存在価値が見出せると私は考える。
最後に控えめな宣伝を。WAFの選定・導入に関する相談はこちらまで。
*1 ホワイトリストとブラックリストについては、ホワイトリスト方式の優位は神話を参照されたい
*2 アプリケーション要件として禁止されているならホワイトリスト検査が可能だ
*3 これについては、WAFベンダーからも次のような証言がある。
APC Phase 2が最もセキュリティの高いものであることはいうまでもないが、すべてのページ遷移パターンまでもWAFに登録せねばならない。そうなると初期設定に相当の時間がかかり、運用開始後のコンテンツ変更時にも相当の労力と時間を要する。事実、BIG-IP ASMを採用している顧客の中でも、APC Phase 2を利用している顧客は、極めてコンテンツ変更の頻度が少ない数社しか存在しない。WAFのセキュリティレベルとパラメータ設定より引用
2008-07-16 ホワイトリストとブラックリスト
●ホワイトリスト方式の優位は神話
近々WAF(Web Application Firewall)の話題を取り上げたいと思っている(→WAFの話題はこちら)。WAFの説明には決まってホワイトリストとブラックリストという用語が出てくる。しかし、WAFの宣伝やブログなどのエントリを読んでいると、ホワイトリストやブラックリストという言葉に対する誤解があるように見受けられる。そのため、WAFの話題の前に、この二つの用語の説明をしておきたいと思う。
ごく大雑把に言って、ホワイトリストは「怪しくない人・モノ」を列挙したもの、ブラックリストは「怪しい人・モノ」を列挙したものだ。これらのうち、日常生活でなじみのある用語はブラックリストだろう。クレジットカードの支払いを延滞すると「ブラックリスト」に名前が載り以降しばらくカードが作れなくなるとか、テロ組織のメンバーの名前が書かれた一覧表も「ブラックリスト」と呼ばれる。
最近話題の携帯コンテンツのフィルタリングについても「ホワイトリスト」方式と「ブラックリスト」方式の是非が議論された。この場合は、青少年の閲覧に問題ないサイトの一覧が「ホワイトリスト」、問題が想定されるサイトの一覧が「ブラックリスト」となる。
さて、問題はこの二つの方法論の使い分けだ。冒頭に述べたようにセキュリティ業界ではなにかとホワイトリストの人気が高いようだが、この傾向はWAF分野において顕著だ。以下は、とあるWAFの宣伝文句であるが、ホワイトリスト方式の利点が高らかにうたわれている。
・・・のWAF機能は、ポジティブ・セキュリティと呼ばれるホワイトリスト方式を採用している。これはポリシーによって正しいと定義されたトラフィックのみに、Webアプリケーションへのアクセスを許可する方式。不正アクセスをブラックリスト方式で識別するIDC(不正侵入検知システム)やIDP(不正侵入防御システム)では常にリストを更新する必要があるが、ホワイトリスト方式なら頻繁な更新は不要で、新しい攻撃に対する防御能力も高い。 引用元
一例のみ紹介したが、このような題材を探すには苦労しない。そして、そのような説明の多くが、ブラックリスト=古くて劣ったもの、ホワイトリスト=新しくて優れたもの、という調子だ。引用した文もそうなっているが、ホワイトリスト方式=ポジティブ、ブラックリスト方式=ネガティブという用語も(こう呼ぶ意味はあるのだが)ホワイトリスト方式の優位を印象付ける。
しかし、である。本当にホワイトリストが優れていて、ブラックリストが劣っているのであれば、法律かなにかでブラックリスト方式を禁止し、今後はホワイトリスト方式のみを採用するようにすべてのセキュリティベンダに強制すべきではないのか。現実にはそんなことはできないのであって、例えばウィルス対策ソフトをホワイトリスト方式で実装することは不可能だ。ホワイトリストとブラックリストにはそれぞれ長所と短所があって使い分けをすべきものであり、どちらが優れているとか劣っているというものではないのだ。
右の図はホワイトリストとブラックリストの位置づけを概念的に示したものだ。図のように、ホワイトリスト(WL)は「まず安全と考えられるもの」を列挙したもの、「ブラックリスト(BL)」は「安全でない可能性がかなりあるもの」を列挙したものとなる。そして、ホワイトリストとブラックリストのどちらにも載ってない中間部分が、「白黒はっきりしない中間領域」すなわちグレイゾーンとなる。
この図からもわかるようにWLとBLで示すことのできる領域は全体として一部であり、どうしてもグレイゾーンが大きくなる。すなわち、WLは判断を安全サイドに倒して「安全という保証のないものは全て排除する」もの、BLはカバー範囲を広くとることを重視して「明らかに怪しいもの以外は受け入れる」方法ということになる。この関係を以下に表として示した。
| ホワイトリスト方式 | ブラックリスト方式 | |
| カバー範囲 | 狭い | 広い |
| 安全性 | 高い | 危険なものを受け入れる可能性あり |
これだけのことだ。ホワイトリスト方式の方が安全なことは確かだが、世の中全てホワイトリストで回せるはずがない。セキュリティの世界では守るべき対象の性質によってはホワイトリストが使える場合があって、その場合はぜひホワイトリストにしなさいというだけのことである。前述のように、ホワイトリストが使えない場合が現実には大半なので、(問題があるとは分かっていて仕方なく)ブラックリスト方式を使う。それだけのことだ。特に方法論自体の優劣とは関係ない。
ついでのように紹介して恐縮だが、大垣さんの書かれたホワイトリストはどう作る?は、ホワイトリスト神話の悪しき例と言わざるを得ない。
スクリプトインジェクション(XSS)防止にブラックリストが機能しない事は明らかです。ホワイトリストはどう作れば良いか参考となるリンクです。どう作るか書いておいても古くなる可能性が高いので、どこを参考に作れば良いか参考URLを書いておきます。
以下のリンクの情報からスクリプトのインジェクションがどのように行えるかを参考にホワイトリストを作れば概ね間違いないと思います。
Follow up:
XSS Cheat Sheet
スクリプトインジェクション手法の中でも有名な手法を集めているサイトです。XSSロケータと呼ばれている文字列はスクリプトインジェクション脆弱性検出に重宝します。よくある脆弱性であればこの文字列で簡単に検出できます。
[ホワイトリストはどう作る?より引用]
大垣さん、これではホワイトリストではなくて、ブラックリストそのものです。
一方、興味深いことに、金床さんの書かれたウェブアプリケーションセキュリティには、同じ題材を取り扱っているが、その記述はまるで異なる。
WAFを使用しブラックリスト方式のシグネチャマッチングによってXSS対策を行う場合、攻撃を完全に防ぐことは不可能である。これはXSSを引き起こす可能性のある文字列が非常に多岐にわたるためだ。このことは非常によく知られたドキュメントであるXSS Cheat Sheetを見るとよくわかる。【中略】望ましい対策として、パラメータごとにホワイトリスト式のチェックを行う方法が考えられるが、残念ながら多くのウェブアプリケーションではホワイトリストをきちんと定義することが難しい【中略】従って、WAFを使ってXSS攻撃を完璧に防ぐことは期待できない(P92〜P94)。
[ウェブアプリケーションセキュリティより引用]
同書の書評でも述べたが、オープンソースのWAFの開発者としてホワイトリストとブラックリストの両方に真剣に向き合ってきたからこそ書ける、正確かつ誠実な記述である。
金床さんのWAFの話題が出たところで、次回はWAFの説明に続く・・・徳丸浩の日記 - そろそろWAFに関して一言いっとくか - 三重苦を乗り越えてWAFが普及するための条件とは
| SQLインジェクション対策はおすみですか? 開発開始時点からのコンサルティングから、公開済みWebサイトの脆弱性検査、 脆弱性発見後の適切な対策まで |
★ yohgaki [入力値のバリデーションはよいとして、出力時のエスケープ処理は普通ホワイトリストと呼ばないのではないか?金床さんの本が..]