前回「XSS脆弱性により起こる被害とその対策」は、XSS対策の重要なことの1つとして見落としがちな「入力チェック」があると解説した。今回は、その入力チェックとして特殊文字のサニタイジング(無害化)を中心に、セキュリティホールの対策について解説する。
特殊文字のサニタイジング(無害化)
前回までの入力チェックを確実に行っていれば、かなりセキュリティレベルの高いアプリケーションが出来上がっているはずである。しかしまだ完全ではないので、最後まで気を抜かないでいただきたい。ここでは、XSSを防ぐために不可欠なサニタイジングについて説明する。
XSSは、入力値をHTTPやHTMLの一部として出力する際に生まれるセキュリティホールである。これは入力値に含まれるいくつかの文字がHTTPやHTMLで特殊文字として定義されていて、それらの文字をそのまま出力してしまうことにより起こる。
XSSとは関係ないが、特殊文字のサニタイジングが必要となる例としてCSV形式のファイルを挙げる。CSV形式とは、「,」(カンマ)によってフィールドを区切られた形式になっているもので、次のように保存される。
姓,名,年齢
木村,靖,29
国分,裕,25
後藤,久,31
ここでは「,」が特殊文字の1つとして挙げられる。もし入力値が「,」を含み、
姓 | 坂,恵理子,18 |
---|---|
名 | 恵理子 |
年齢 | 20 |
となっていて、これをそのまま出力すると、
姓,名,年齢
木村,靖,29
国分,裕,25
後藤,久,31
坂,恵理子,18,恵理子,20
となる。特殊文字は特殊文字の扱い方に問題があると、セキュリティの問題のみならずアプリケーションの動作にまで影響を与えることになる。特殊文字を無害化をすることをサニタイジングという。この例では「,」を無害化することがサニタイジングとなる(本当はもう2つサニタイジングしなければならない文字があるのだが、それは考えていただきたい)。
サニタイジング処理をしなければならないものは使用する個所によってさまざまである。「SQL 文を実行するときの特殊文字は?」「OS コマンドを実行するときの特殊文字は?」から、実生活の中でも「お弁当を温めるときに電子レンジに入れてはいけないものは?」「○○さんにいってはいけない言葉は?」など、それぞれの場合に特別な処理が必要になるものがあるだろう。
本稿ではXSSに関連したサニタイジングについてのみ説明するが、それぞれの場合にサニタイジングが必要なものは何かということを熟慮し、処理し忘れないようにしていただきたい。
●HTML
HTMLは、タグと本文に分類される。
<font color="red">ようこそkokubuさん</font>
タグはブラウザの画面に表示されない「<font color="red">」と「</font>」の部分で、本文は、ブラウザに表示される「ようこそkokubuさん」の部分である。それぞれについて処理しなければいけない特殊文字が異なる。
- 本文の場合
本文で特殊文字となる文字は、「<」「>」「&」の3つである。「<」と「>」は本文とタグを区切る開始と終了という特別な意味を持つ特殊文字で、「&」は文字参照(character references)を記述するための開始文字という意味を持つ特殊文字である。
本文の部分にユーザー入力値をそのまま出力してしまうとどうなるかは、前編の図3、図4のとおりである。これらの3つの文字をサニタイジングするには、それぞれ「<」「>」「&」に変換する。
こうすることで、「<script>alert("test");</script>」という入力がなされた場合には、「<script>alert("test");</script>」というHTML が出力される。ブラウザの画面上は「<script>alert("test");</script>」と表示されているが、タグが無効となる。
- タグの場合
すべてのタグは次のような形式をしている。
<input type="hidden" name="namae" value="kokubu">
「input」の部分(要素名と呼ぶ)と、「type」「name」「value」の部分(属性名と呼ぶ)を動的に生成することはあまりないだろう。もし要素名と属性名部分に入力値を使うとしても、これらの部分に使ってよい値はHTMLの仕様として決まっているので、入力チェックの段階でエラー処理ができる。
よく見られるのは、上記「"hidden"」「"namae"」「"kokubu"」の部分(属性値と呼ぶ)に入力値を入れる場合で、正しくサニタイジングをする必要がある。まず、属性値は必ず「"(ダブルクオーテーション)」または「'(シングルクオーテーション)」で囲む。これはW3Cが推奨しているのと同時に、サニタイジングが必要な特殊文字を限定することになるので、忘れずに行ってほしい。
「"」で囲った場合の特殊文字は「"」だけとなる。同様に「'」で囲った場合は「'」だけとなる。それぞれ、「"」と「'」に変換することでサニタイジング完了となる。
こうすることで、「"><script>alert("test");</script>」という入力がなされても、「<input type="hidden" name="namae" value=""><script>alert("test");</script>">」となり、タグが無効となる。
ちなみに、ここまでのサニタイジングでは入力文字と“同じ”文字を無害に表示するための変換例を挙げたが、同じである必要がなければほかの方法もあり得る。
害の有無 | 処理方法 | 変換例 |
---|---|---|
有害 | 無処理 | <script>alert('XSS');</script> |
無害 | 「<」と「>」を「&lt;」と「&gt;」にする | &lt;script&gt;alert('XSS');&lt;/script&gt; |
無害 | 「<」と「>」を消してしまう | scriptalert('XSS');/script |
無害 | 「<」と「>」を全角「<」「>」にしてしまう | <script>alert('XSS');</script> |
無害 | 「<」と「>」を「.(ピリオド)」など無害な別の文字に変換 | .script.alert('XSS');./script. |
入力文字を無害にするそのほかの方法 |
要するに、特殊文字が含まれなければよいということである。
● HTTP
ここまではHTMLに入力値を出力する話だったが、ちょっと視野を広げてHTTPまで踏み込んでみよう。
http://www.example.com/hello.cgi?name=kokubuにアクセスすると、username=kokubuというcookieを発行するCGIがあるとする。この場合、サーバが返すHTTPレスポンスは次のようになっているだろう。
HTTP/1.1 200 OK Date: Fri, 15 Nov 2002 13:19:41 GMT Server: Apache Last-Modified: Tue, 29 Oct 2002 07:36:42 GMT ETag: "d902-1cc7-3dbe3a8a" Accept-Ranges: bytes Content-Length: 7367 Connection: close Set-cookie: username=kokubu; path=/ Content-Type: text/html <html> <head> <title>index</title> (以下略)
HTTPヘッダとHTTPボディ(つまりHTML部分)の境目は空行で分けられる。スクリプトはHTMLに含まれないと動作しないので、この場合にスクリプトを挿入しようとすると攻撃者は次のようなリクエストを発生させる。
http://www.example.com/hello.cgi?name=kokubu%0D%0A%0D%0A%3Chtml%3E%3Chead%3E%3Cscript%3Ealert('XSS');%3C/script%3E%3C/head%3E%3C/html%3E
URLエンコードしてあるため呪文のような文字列だが、CGIによってデコードされた結果出力されるHTTPレスポンスは次のようになる。
HTTP/1.1 200 OK Date: Fri, 15 Nov 2002 13:19:41 GMT Server: Apache Last-Modified: Tue, 29 Oct 2002 07:36:42 GMT ETag: "d902-1cc7-3dbe3a8a" Accept-Ranges: bytes Content-Length: 7367 Connection: close Set-cookie: username=kokubu <html><head><script>alert('XSS');</script></head></html>; path=/ Content-Type: text/html <html> <head> <title>index</title> (以下略)
%0D%0Aが改行をエンコードしたものであるため、強制的に空行が挿入され、スクリプトを含んだHTMLが出力されてしまうことになる。つまり、HTTPヘッダに入力値を含む場合にもサニタイジングが必要になる、ということである。
HTTPヘッダとHTTPボディを分けるのは空行であり、それを行うのが改行文字である。HTTPヘッダに入力値を含む場合は、最低限改行文字をサニタイジングする。ほとんどの場合、消してしまって問題ないだろう。どうしても改行文字が必要な場合は、改行文字をエンコードして「%0D」や「%0A」として出力すればよい。ただ、Set-Cookie ヘッダの場合は「;」が属性の区切りを表す特殊文字であるなど、ヘッダによってさまざまな特殊文字が存在するので、それらの処理も忘れずに行う必要がある。
HTTPヘッダを出力する場合は、入力チェックの段階で英数字のみに制限してしまうか、英数字以外の文字はすべて%xxという形式で出力した方がよいだろう。
基本的な対策を確実に
3回にわたってXSSとその対策について説明を行った。XSSはプログラミング工程の中で潜り込むセキュリティホールで、プログラマのちょっとした注意によって防ぐことができるセキュリティホールである。対策方法についても高度な技術が要求されているわけではない。
XSSのみならずWebアプリケーションにかかわるセキュリティホール全般にいえることであるが、基本的な対策を確実に行っていればセキュリティホールは激減することになる。
ミスや見落としは人間のやっていることなのである程度仕方のないことだが、「知らなかったから」とか「時間がないから」といった理由でセキュリティホールのあるシステムなどを次々と開発していくことはやめていただきたいと切に願う。
Profile
国分 裕(こくぶ ゆたか)
三井物産株式会社GTIプロジェクトセンタ勤務。セキュリティコンサルタントとして、不正アクセス監視やセキュリティ検査 などに従事している。金融機関、官公庁、大手製造業などへのセキュリティシ ステムの導入、セキュリティ検査などの実績を持つ。
三井物産株式会社GTIプロジェクトセンタ(現:三井物産セキュアディレクション株式会社)
主に、不正アクセス監視サービス、セキュリティ検査、セキュリティポリシー策定支援などのサービス提供している。また、セキュリティに関する教育サービスも実施中。
Copyright © ITmedia, Inc. All Rights Reserved.