出力をホワイトリストで処理することを真剣に考えてみる
2008年10月5日(日曜日)
出力をホワイトリストで処理することを真剣に考えてみる
第02回まっちゃ445の懇親会の帰りに考えていた事なのですが、メモし忘れていたのであらためて。
HTMLの中に変数を出力する際、何も考えずに文字列をそのまま出力すると、HTMLのマークとみなされる文字が含まれていた場合にまずいことが起こります。たとえば#PCDATAの中に出力する場合、「<」や「&」がそれぞれSTAGOやEROとして解釈されますので、これらをそのまま出力しないようにする必要があります。以下のように文字実体参照に置き換えるのが一般的です。
- < → <
- & → &
同様に、二重引用符で括られた属性値リテラルに出力する場合は「"」と「&」がマークとして解釈されます。XHTMLの場合は「<」も書けないことになっていますので、これも置き換える必要があります。
- " → "
- < → <
- & → &
※#PCDATAの中で「"」を変換しても問題は起きないので、多くの場合は後者の処理が前者にも使い回されます。一重引用符の属性値リテラルやコメントの中ではまた違った処理が必要なのですが、そのような場所に変数を出力するようなことは滅多にないでしょう。
さて、これらの変換処理は、「マークとして解釈され得るため、そのまま出力してはまずい文字」を列挙して、それらだけを置き換えるという処理になっています。これはブラックリストによる処理だと言えるでしょう。
ところで、まっちゃ445では「変数の出力時にホワイトリストを適用する」という言葉が出ていました。前述の処理をブラックリストではなく、ホワイトリストで行うことはできるだろうか……と考えてみると、もちろんできます。「マークとしては解釈され得ないため、そのまま出力して問題ない文字」を列挙しておき、それら以外を全て文字参照に置き換えてしまえば良いのです。
たとえば、まあ [0-9a-zA-Z] は安全だろうと考えて、それ以外を全て変換するとします。そして「><s>test&テスト++」という文字列をp要素の内容として出力する場合、以下のようになります。
<p>><s>test&テスト++</p>
あまり見慣れない処理ではありますが、考えてみればこれでも問題はないのですね。そして、この方法にはメリットもあります。
この処理、日本語の文字などは全て文字参照にしてしまい、ASCII文字だけを出力します。そのため、文字コード関係の攻撃にめっぽう強いのです。具体的には、以下のような耐性があります。
- 多バイト文字の片割れなども文字参照として出力されるので、壊れた多バイト文字による攻撃が無効
- + や - も文字参照として出力されるので、UTF-7の攻撃が無効
- ソーシャルな手法によるcharsetの突破も不可能 (ユーザに手動で文字コード変換をさせても無駄)
最後の項目は意味不明かもしれませんので説明しておきます。妥当なcharsetパラメータを出力していれば文字コード関係の攻撃は受けないと思われがちですが、たとえば罠サイトに以下のような記述があったらどうでしょうか。
以下のサイトには、実はものすごい情報が隠されています。
http://target.example.com/?%XX%XX……
(普通にアクセスしても見えませんが、「エンコード」を「EUC」にすると……)
charsetパラメータを出力していても、ユーザが手動でエンコードを変更すれば変更できてしまいます。罠サイトの指示に従ってエンコードを変えると発動してしまう、という攻撃が考えられるわけです。が、全部を文字参照にしてしまえば、こういう攻撃も無効になります。
※ASCII非互換でものすごく変な文字符号化方式があったりしたら分かりませんが……聞いたことないので。
また、さらに地味なメリットとして、この手法は「単引用符で括られた属性値の中やコメントの中でもそのまま使いまわせる」という汎用性も持ち合わせています。一つの処理で使い回しOKというのは、意外に心強いメリットかもしれません。
……というわけで、まじめに考えてみると、「出力時にホワイトリスト」というのも意味があるのかもしれないなぁと思う次第です。
※とは言っても、無条件にオススメしたりはしませんし、私も今のところ実践するつもりはないのですが。
- 「出力をホワイトリストで処理することを真剣に考えてみる」へのコメント (8件)
- 前(古い): ゲームセンターCX 番外編
- 次(新しい): 粘菌の研究でイグ・ノーベル賞