| « ブログらしくブックマークなどに登録してもらう為のリンク追加 | 企業ユーザはPHP4からPHP5への移行は慎重にすべき » |
ホワイトリストはどう作る?
追記:
この文章を書いたときに少し意地悪で解り辛いかも、と思った事を覚えています。本当に誤解されてしまっている方もいるので追記します。
私はいつも基本的に能動的なセキュリティ対策を選択するようにお勧めしています。能動的なセキュリティ対策とは全ての入力値の厳格なバリデーション処理であり、出力時に過剰とも言えるエスケープ処理です。いわゆるホワイトリスティングと呼ばれるような対策です。
(過剰過ぎるエスケープ処理はセキュリティ問題の原因になるので2重エンコーディングやエスケープはしないように。念のため)
このエントリの書き方が意地悪な書き方であるのは「ホワイトリストはどう作る?」と題して、その書き方を解説していない事です。しかし、それは実際にXSS Cheat Sheetを読み、理解してもらい、その上で安全なホワイトリストはどう作るか自分で考えて欲しかったからです。
その後にホワイトリストとして作ったチェックが、簡単な変更でブラックリストになってしまう事を紹介したのも誤解の一因でしょう。
ホワイトリストの作り方にはケースバイケースで幾つもの指針が有りますが、最も重要と言える指針は
「許容する入力・出力は必要最低限だけ留め、許容した入力・出力は確実に安全である事を保証し、もし不正な入力があった場合は必ず記録し、必要であればプログラムの実行を停止する」
です。これは私の本でも書いている事です。稀に誤解されるので書きますが「プログラムの実行を停止する」とは入力間違いで実行を停止するのではなく、不正な入力で実行停止する事を意味します。
もう少し具体的に、テキスト出力に対するXSS対策で有れば、出力するテキストの文字エンコーディングが正しい事を検証し、かつ文字エンコーディングに合ったエスケープ方法で全ての特殊文字をエンコーディングする事です。HTMLの属性をユーザが指定可能にするのであれば、その属性に対して許可する値を厳格に定め、それ以外の入力は拒否する事です。同じHTML出力するから、といって同じように取り扱えません。テキストならテキスト、属性ならその属性、スタイルならスタイル、JavaScriptならJavaScript、E4XならE4X、DBMSならDBMS、メールシステムならメールシステムの為の「正しいホワイトリストの作り方」があります。
このエントリであえて「ホワイトリストをどう作るか?」書いていない理由は技術革新の早いWebシステムで具体的なホワイトリストの作成方法を書いても無意味となる可能性があるからです。さらに、一つのシステムについてどのようにホワイトリストを作るか解説しても片手落ちです。それよりも最も防御が難しいXSSの攻撃方法を紹介したXSS Cheat Sheatを参照し、自分で最新の攻撃方法を知った上で自分でホワイトリストの作り方を知った方が何倍も価値があり、時間の経過と共に訪れる情報の陳腐化も防げると考えているからです。
ところで、WAFをホワイトリスト形式で設定してWebアプリケーションの安全性を向上させる事も可能ですが本末転倒です。アプリケーションの入出力バリデーションをホワイトリスト化する方が筋であり、順番が逆です。アプリケーションがホワイトリスト方式でのバリデーションを行えば、WAFなどはブラックリスティングのみで利用しても構わないですし、高価なWAFを購入して速度を落とす必要もありません。簡易WAFと動的フィルタリングを組み合わせる方がコスト的にも合理的です。
PCIDSS(Payment Company Industry Data Security Standard)では「WAFまたはコード監査」をコンプライアンスの必要条件としています。WAF(Web Application Firewall)の利用が認められているのは、コード監査が間に合わないユーザに対する救済措置の意味合いがかなり強いと考えています。
WAFはゼロデイ攻撃からシステムを防御する仕組みとして欠かせません。私もWAFの必要性は認めています。しかし、PCIDSSで「WAFおよびコード監査」の両方を要求していません。PCIDSSを策定したセキュリティ専門家も私と同様にWAFの効果を限定的と評価している事の現れでしょう。(将来的には両方必要要件になるかも知れません。しかし、その場合はWAFの機能要件は緩和される可能性が高いと思います)
コード監査+簡易WAFの方がコストメリットが高い、とする意見に疑問を持たれた方の為に記載します。まずコード監査だけではコストが高く付くケースもあります。例えば、アプリケーション設計自体に問題がありセキュアな設計になっていない場合、コード監査後の修正と検証にWAF導入より多くのコストが必要となる場合があります。
しかし、一旦セキュアな設計+コード監査+簡易WAF(ブラックリスティング方式)でシステム構築を行えば、態々設定ミスがありがちなホワイトリスティング方式によるWAF設定など必要なくなります。ホワイトリスティング方式よるWAFの場合、アプリケーションのちょっとした改修でもWAF設定の修正と設定の検証が必要になります。ホワイトリスト方式のWAFは導入すればOKという物ではなく、常にメンテナンスが必要なシステムです。しかも、このエントリでも紹介しているようなメンテナンス時のちょっとしたミスでホワイトリストがブラックリストになってしまう危険性もあります。
総合的なコストが"セキュアな設計+コード監査+簡易WAF"と"ホワイトリスティング方式のWAF"の導入とどちらが低くなるかはケースバイケースですが、直感的に多くのシステムで"セキュアな設計+コード監査+簡易WAF"の方が長期的なコストは確実に安く、リスクも少なくなる可能性が高い、事が解ると思います。
この追記に対応するエントリも作りました。よろしければこちらもどうぞ。
http://blog.ohgaki.net/-13
以下から元のエントリです。
スクリプトインジェクション(XSS)防止にブラックリストが機能しない事は明らかです。ホワイトリストはどう作れば良いか参考となるリンクです。どう作るか書いておいても古くなる可能性が高いので、どこを参考に作れば良いか参考URLを書いておきます。
以下のリンクの情報からスクリプトのインジェクションがどのように行えるかを参考にホワイトリストを作れば概ね間違いないと思います。
Follow up:
XSS Cheat Sheet
スクリプトインジェクション手法の中でも有名な手法を集めているサイトです。XSSロケータと呼ばれている文字列はスクリプトインジェクション脆弱性検出に重宝します。よくある脆弱性であればこの文字列で簡単に検出できます。
ロケータ1
';alert(String.fromCharCode(88,83,83))//\';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//\";alert(String.fromCharCode(88,83,83))//--></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
ロケータ2
'';!--"<XSS>=&{()}
sla.ckers.orgのXSSフォーラム
http://sla.ckers.org/forum/list.php?2
今気が付いたのですが日本のXSSコンテストの情報も掲載されていますね。
いろいろな方が集まって変わった手法でスクリプトを実行する方法を記載しています。例えば、現時点で以下のスレッド
http://sla.ckers.org/forum/read.php?2,15812,20302#msg-20302
の最後にはdata URIを使用したサンプルが載っています。
Data urls tricks:-
<a href=data:text/html;base64,PHNjcmlwdD5hbGVydCgvWFNTLyk8L3NjcmlwdD4>test</a>
<a href= data:text/html;base64,PHNjcmlwdD5hbGVydCgvWFNTLyk8L3NjcmlwdD4>test</a><iframe src=data:text/html;base64,PHNjcmlwdD5hbGVydCgvWFNTLyk8L3NjcmlwdD4>
This one shows that FF reads base64 and ignores every character after until the payload:-
<a href=data:text/html;;base64a,PHNjcmlwdD5hbGVydCgvWFNTLyk8L3NjcmlwdD4>Test</a>Another example:-
<a href=data:text/html;;base64ANYTHING,PHNjcmlwdD5hbGVydCgvWFNTLyk8L3NjcmlwdD4>Test</a>
スクリプトインジェクションに多少詳しい方ならdata URLを使ったフィルタすり抜けのテクニックはよくご存知だと思います。
正規表現は控えめに
下手な正規表現だとホワイトリストのつもりでもスクリプト実行が出来てしまう場合があるので注意が必要です。可能な限り厳格かつ間違いを防ぐため正規表現の使用は控えめにすると良いと思います。
どのようなデータが利用されるか決まっている場合は、データリストを作成してリストと一致するか確認するようにします。例えば、CSSスタイルならstyle_a, style_b, style_cだけ許可するなら名前のリストを作って一致するものだけ許可するようにします。
正規表現を使ってこのスタイルをチェックするために、"style_" + 任意の一文字となる
style_.
を使うとスクリプトインジェクションに脆弱になってしまう可能性があるのは言うまでもありません。
そもそも、任意の一文字なら
style_?
でしょう。とか、aからcだけなので
style_[a-c]
でしょう、と考えられるかも知れません。コード監査時には正規表現も重点的な監査対象です。正規表現は少ないに越したことはありません。「デフォルトでエスケープする」と同じで監査し易いコードはより安全なコードになります。
最初に書いたプログラマは正規表現(+セキュリティ)のエキスパートで[a-c]と書いておいても、いつ他の正規表現に不慣れなプログラマが「.」に書き換えてしまうか分かりません。「.」くらいならまだ良いですが、
style_.*
「.*」に書き換えられたらどうしようもありません。