2012年3月30日金曜日

処理開始後の例外処理では「サニタイズ」が有効な場合もある

このエントリでは、脆弱性対処における例外処理について、奥一穂氏(@kazuho)との会話から私が学んだことを共有いたします。セキュアプログラミングの心得として、異常が起これば直ちにプログラムを終了することが推奨される場合がありますが、必ずしもそうではないというのが結論です。

はじめに

Webアプリケーションの脆弱性対策では、脆弱性が発生するのはデータを使うところであるので、データを使う際の適切なエスケープ処理などで対処するのがよいと言われます。しかし、処理内容によってはエスケープができない場合もあり、その場合の対処についてはまだ定説がないと考えます。

エスケープができない場合の例としては、以下があります。
  • SQLの数値リテラルを構成する際に、入力に数値以外の文字が入っていた
  • メール送信しようとしたが、メールアドレスに改行文字が入っていた
  • 入力されたURLにリダイレクトしようとしたところ、URLに改行が入っていた

これらの場合、エスケープはできないので、以下のいずれかの対処をすることになります。
  • バリデーション:受け付けられない文字があった場合はエラーとして処理を終了する
  • サニタイズ:受け付けられない文字を削除するか、他の文字に置き換えて処理を継続する

ここで前提として、入力値のバリデーションはしているのだが、何らかの原因でバリデーション処理に漏れがあって、そのデータを使う(たとえばメール送信)際に、上記が分かったというシナリオです。つまり、本来上記のような異常値は来ない前提だが、セーフティネットとして、処理の段階で対処をしておくということです。

私は従来、このような場合はバリデーションがよいと考えていました。サニタイズは入力値を改変することになるので、いかなる場合でも好ましくないと考えていました。

奥一穂氏との会話

そこで、大垣さんの寄稿記事「なぜPHPアプリにセキュリティホールが多いのか? 第44回 セキュリティ対策が確実に実施されない2つの理由」に関連して、大垣さんの寄稿に肯定的なコメントとして、facebookで以下の投稿をしました。
徳丸 浩 出力時のバリデーションが必要な局面は確かにあって、(1)(好ましくはないが)SQLを動的に組み立てる際に、数値パラメータに数値以外の文字がある場合、(2)メール送信時に、メールアドレスや件名に改行が含まれていた、ようなケース。とくに(2)のケースは、メール送信用のラッパー関数を作って改行やメールアドレスの改行チェックをするとよい。本来は入力時のバリデーションで弾かれているはず(べき)だが、万一漏れていた場合の安全装置として出力時のバリデーションも組み込んでおくと効率よく安全性を高めることができる
これに対して、奥一穂氏から以下のコメントを頂戴しました。
Kazuho Oku 安全装置なら処理を停止よりはサニタイズすべきなのかなと思ったりします。昔、Twitter ライクなサービスが出力時バリデートしてて、ある人が不正な UTF-8 シーケンスを含むツイートを投稿した結果、多くのユーザーのタイムラインが表示 (ry
私はこの「事件」の詳細を知らないのですが、以下の現象だったと憶測しています。
  • 某Twitterライクなサービスはtwitterのように、フォローしている利用者のつぶやきが時系列的に表示される
  • ユーザのH氏が誤って(?)不正なUTF-8シーケンスを含むツイートを投稿した
  • そのつぶやきは入力時のバリデーションを通過してDBに格納された
  • そのつぶやきを表示する際に、不正UTF-8シーケンスが含まれているため、例外が発生した
  • 例外処理の結果、当該のツイート以降空となり、多くのユーザのタイムラインが影響を受けた

H氏が、自身の不正なツイートで不利益を受けるのは仕方ありませんが、他のユーザにまで影響が及ぶことは問題です。入力時のバリデーション不備が根本原因ではありますが、フェールセーフのつもりの例外処理があだとなって、影響が大きくなってしまいました。奥氏のコメントの意図は上記のようなものでしょう。

これに対する私のコメントと奥氏のリプライ。
徳丸 浩 奥さん、この問題を引き続き考えていたのですが、要約すると、処理が走り始めてから入力値に問題があったことがわかった場合、処理を中途半端に打ち切るのではなく、サニタイズしてでも最後まで走らせた方が安全(な場合が多い)ということでしょうか? 例外を発生させることとの選択になると思いますが
Kazuho Oku はい。そのように考えます。例えば、上のコメントにあげたケースは、処理を最後まで走らせずに例外をあげた結果、サービスにDoS脆弱性が発生していた、と理解すべき事象だと思います。
はせがわさん(@hasegawayosuke)も参入
Yosuke Hasegawa 「例外の粒度」的な観点ていうのはないんでしょうか。粒度というかスコープというのか。上の例でいえば、例外によって、不用意な発言した人の該当tweet *だけ* が消え去るのなら問題ないんでしょうけど、現実には広告を含め該当tweet以降のHTMLがすべて消えちゃったから問題になったわけで。
Kazuho Oku 不正なUTF-8シーケンスをうっかりtweetしちゃうとかハッカーこわいwww というのはさておき、「例外の粒度」という観点は同意です。ただ、例えばツイート単位で消しちゃうと、そのデータを UI から編集したり削除したりすることもできなくなるので、できるだけ小さな規模での「書き換え」(つまりサニタイズ)が望ましいと僕は考えます。
徳丸 浩 とても刺激的なお話です。便乗してもう一つお聞きしたいのですが、前提として、DB更新が途中まで走った後でもロールバックすればいいという教科書的な話に対して、現実の大規模な環境だと、ロールバックによって、きれいに「なかったことにする」ことも難しいのだろうと想像しているのですが、そういう理解であっていますか?
Kazuho Oku 現実には入力値検証を完了する前にDB更新を始めることはないでしょうから、脆弱性の話とはずれる気がしますが、大規模な環境だと「きれいに「なかったことにする」」のが難しいことは多いと思います(ただ、そのような回収もれのゴミはユーザーからは見えないようにするべきですが)。
徳丸 浩 ありがとうございます。前から気になっていました。そう言う前提がないと、(セキュリティはさておいたとしても)入力値検証を絶対やりなさいと言う動機付けという点で柔い気がしたものですから
上記のような具合に、私のウォールで豪華メンバーに参入いただき、楽しいディスカッションとなりました。

先のTwitterライクなサービスの場合で言うと、以下のような対処案が考えられます。

1.例外を発生させる(元々の仕様)

表示時に異常データがあれば処理を打ち切り、ファイルクローズなど必要な後始末の後プログラムを終了する。
この方法だと、先述のように、関係ないユーザまで被害を受けるので、よくありません。

2.当該のツイートのみ表示しない

異常なデータを含むツイートの単位で表示しないという方法です。
前項よりはよいですが、UIから異常ツイートを削除するのが不便であり、画面上も不自然な表示になる可能性があります。


3.異常データのみを削除するか、他の文字(「〓」など)に書き換えて表示する

(狭義の)サニタイズです。異常データの影響はもっとも軽微になります。
はせがわさんから指摘されていた「例外の粒度」はもっとも細かくなりますが、粒度が細かいことが好ましい例と言えます。

まとめ

結論は以下の通りです。
  • 処理が始まる前のチェック処理では、厳格なチェック処理と即座の停止をして良い(利用者に適切なメッセージを表示する前提で)
  • 処理(更新、表示など)が始まってから異常が発覚した場合は、後々の影響を考えた軟着陸をする必要がある
  • その際にサニタイズが有効な場合もある
サニタイズというと「サニタイズ言うなキャンペーン」を連想してしまい、「サニタイズは絶対にやってはいけない」と考える人もおられるような気がしますが、私たちはその段階をそろそろ卒業しなければならないように思います。「サニタイズ言うなキャンペーン」は、本質的な脆弱性対処を考える上で、いったん「サニタイズ」という用語を使わないで考えてみようという問いかけだったと理解していますが、本質的な脆弱性対処をした上でさらに例外時の処理にまで配慮する場合は、サニタイズが有効な場合もあるということです。

謝辞

上記はクローズドな場の議論でしたので、奥一穂さんとはせがわようすけさんに引用のご許可をいただきました。快諾いただきまして、ありがとうございます。

2012年3月11日日曜日

はてなブックマークボタンを外しました

この数日間問題になっている「はてなブックマークボタン」ですが、当日記およびHASHコンサルティングオフィシャルブログにも、当該ボタンがついていました。何が問題であるかは以下が詳しいですが、要は、はてなの管理下でない当サイトで、はてなのブログパーツが読者の皆様のトラッキングをしていることが問題です。

参考:
私は、2006年11月に、はてなダイアリーで日記を書き始めて以来、一貫してはてなのサービスを利用してきましたので、当ブログにも「はてなのボタンもつけとかなきゃな」程度のノリでボタンをつけておりました。その時点では、上記問題は知られておらず、また公式には始まってもいなかったようですので、知りようのないことではありましたが、それでも私の責任はあると認識しております。

私の本のP62には以下の記述があります。

第三者のJavaScriptを許可する場合
XSSは悪意の第三者によるJavaScript実行が問題でしたが、意図的に第三者のJavaScriptを実行させる場合があります。セキュリティ上の問題に対しては、サーバー運営者あるいは閲覧者が第三者を信頼する形で実行されます。

◆ サイト運営者が第三者を信頼して実行するJavaScript

サイト運営者が第三者の提供するJavaScriptを自サイトに埋め込む場合があります。典型的には、アクセス解析、バナー広告、ブログパーツなどです。このケースでは、サイト運営者が意図的にJavaScriptの提供元である第三者(以下提供元と表記)のJavaScriptを埋め込みます。
このように埋め込まれたJavaScriptに悪意があると、情報漏洩やサイト改ざんの危険性があります。このため、提供元が信頼できることが条件になりますが、実際には以下のような脅威があり、セキュリティ上の問題が何度も発生しています。
  • 提供元が意図的に個人情報を収集す
  • 提供元サーバーに脆弱性があり、JavaScriptが差し替えられる
  • 提供元のJavaScriptに脆弱性があり、別のスクリプトが実行させられる
バナー広告などのJavaScriptとXSSの結果動作するJavaScriptには、技術的に見れば同一の脅威があります。両者の違いはサイト運営者が提供元を信頼して意図的に埋め込んでいるかどうかです。従って、意図的に埋め込むJavaScriptについても、提供元の信頼性を十分調査した上で、保守的で慎重な判断が求められます

例示の箇条書きのところで「(JavaScriptの)提供元が意図的に個人情報を収集する」という箇所があてはまります。引用部の最後の箇所「意図的に埋め込むJavaScriptについても、提供元の信頼性を十分調査した上で、保守的で慎重な判断が求められ」るにも関わらず、私自身が実行できておりませんでした。

という状況でしたので、当該のブックマークボタンは昨日撤去致しましたことを報告します。
また、はてなの日記の方でも、一時ついカッとなって類似のボタンを撤去しましたが、今は戻しています。その理由は以下の通りです。
  • はてなの管理下のサイトではこの問題はそもそも関係ない
  • はてなのサイトでは、 トラッキングはボタンとは別の方法で実装できるので ブックマークボタンだけを目の敵にしても意味がない

ですが、当面の間、はてなのサービス(ブックマーク、日記)は少なくとも新規の更新をやめようと思います。読者の皆様にはご不自由をお掛けしますが、代替として以下のサービスをご覧下さい。


以上、ご報告致します。

フォロワー