フロントエンドWeb戦略室

第4回危険性が理解されにくいネイティブアプリ内XSS(3)

<(1)はこちら、(2)はこちらから。>

バグがあっても平気なコード

さて、HTMLサニタイズ処理について書きましたが、これらは実はあまり重要ではないと考えています。それよりも高い優先度で行うべきことは、⁠HTMLサニタイズ処理にバグがあっても平気なようにする」ことです。

埋め込みWebViewのセキュリティを考える

バグのないコードを書くことよりも、真っ先に「バグがあっても平気なようにする」ことを考えなければなりません。コードが複雑化したり、大規模になるにつれて、必ずバグは発生するからです。次のような点に気を付けることが大切です。

  • そもそもJavaScriptを無効にしてしまう
  • Same originと見なされるファイルを制限するなど、読み込めるファイルを制限する

そもそもJavaScriptを無効にしてしまう

一番簡単な解決策は、WebViewを使う際に、そもそもJavaScriptを無効化してしまうことでしょう。そもそもスクリプトの除去を行う必要がなくなります。何をどうしようと、どんなHTMLをレンダリングさせようとも、あらゆるJavaScriptやプラグインが実行されなければ安心です。あるいはHTML5でサポートされているiframeのsandbox属性を使うのもよいでしょう。ただし、この場合、アプリケーション側で用意されているJavaScriptを使ったアクションなども動作しないことになってしまいます。純粋に文書としてHTMLを表示したい場合はこのアプローチを取ることができるでしょう。

読み込めるファイルを制限する

2つ目は、Same originと見なされるファイルを制限することです。たとえばWebのコンテキストで表示している場合、ローカルファイルが読み取られることはあり得なくなります。

または、すべてのHTTPリクエストにフィルタ処理を入れる方法です。これは各プラットフォームごとに固有の対応が必要になります。iOSであれば、NSURLProtocolを上書きして、読み込み可能なファイルを制限する方法が知られています。

ローカルファイルにおけるSame origin policy

ローカルファイルにおけるSame origin policyの取り扱いには長い歴史があります。覚えておくべき重要なポイントは、⁠ブラウザごとにポリシーが異なる」ということです。ブラウザごとのポリシーを確認してみましょう。

Internet Explorer

IEの場合、IE6/Windows XP SP2以降、ローカルコンピュータゾーンのロックダウンという処理が導入されました。デフォルトでスクリプトの実行は禁止され、通知バーで警告が出ます。許可した場合は、ローカルのHTMLファイルからローカルファイルの読み取りはもちろん、Webページの取得も可能になります。

Firefox

かつては、上位ディレクトリも含めて任意のファイルを読み取ることができました。ローカルファイル経由でユーザのプロファイルが保存されているディレクトリをスキャンし、Cookieを保存しているファイルを盗み出す、といったことが可能でした。Firefox 3以降では、ディレクトリのファイル一覧の読み取りは制限され、同一ディレクトリかサブディレクトリに存在するファイルしか読み取れなくなりました

Google Chrome

Google Chromeでは、ローカルHTML上でのアプリケーションが動かなくなることや、開発が不便になることを承知のうえで、現在開いている「自分自身」のURL以外は別originになるように変更を加えられます。ChromeはFirefoxよりも厳しいポリシーを採用していると言えるでしょう。

たとえばCD-ROMやDVDに収録されたコンテンツを閲覧するような場合に、Firefoxであればトップレベルに置かれたindex.htmlからそのディスク上に置かれたファイルの内容を読み取ったり、フレーム間で跨ってwindowオブジェクトや変数を参照できます。Google Chromeの「自分自身以外は別originと見なす」ポリシーはこうした場合に、不具合が生じることがあります。

Firefoxのポリシーの場合、ユーザのホームディレクトリやデスクトップ上にダウンロードされたHTMLファイルを開くと、同じディレクトリに重要なファイルがあることが既知である場合、たとえば、デスクトップに⁠password.txt⁠といったファイル名でパスワードをメモしているということが既知であれば、デスクトップに保存されたHTMLファイルを経由してパスワードを盗み出すことが可能になってしまいます。

Safari

Mountain LionのSafari 6.0.1に関しては、Webサイトからダウンロードされたファイルには⁠com.apple.quarantine⁠というメタ情報がアプリケーション内部に設定され、この属性が付いたHTMLファイルを表示する際は、自分自身以外はSame origin と見なさず、Google Chromeと同等の、制限された状態で開かれるようになりました。ただし、com.apple.quarantineの付与されていないHTMLファイルに関しては、今までと同等にローカルファイルの読み込みができる状態となっています。

ブラウザごとに異なるポリシー

これらのことから、ブラウザごとに異なるアプローチを採用していることがわかると思います。IEはデフォルトで安全なポリシーを採用する一方で、ユーザが許可した場合には強い権限を持ちます。Firefoxはおおむね安全なポリシーを採用しつつ、下位ディレクトリのファイルについてはSame originとみなしています。Google Chromeは最も厳しいポリシーを持っています。WebKitを使っていてもSafariとGoogle Chromeでポリシーが違うように、こういった、仕様変更にあたっての非互換が発生する場合、迂闊(うかつ)に仕様変更ができなくなるという状況が発生します。DOMやHTMLの仕様や、あるいはJavaScriptの実装については「標準化」が進められる一方で、どういう仕様が適切であるのか、明確な正解がない場合には、細かい部分でブラウザ間の差異が存在し続けることになるでしょう。

ブラウザやOSごとに歴史があり、設計思想があります。デフォルトで安全側に倒されていることもあれば、開発者、あるいはユーザ側が気を付けなければならないこともあります。Webアプリケーションの世界から見たら「たとえバグがあっても、ローカルファイルが読み取られるなんてことは絶対ない」ので、異常なことです。また、単なる「文書ファイル」のつもりでHTMLファイルを開いたのに、⁠ローカルファイルが読み取られて外部に送信される可能性がある」だなんて、一般ユーザには広く知られていないでしょう。

ローカルのHTMLファイルは、単なる文書ファイルであることもあれば、アプリケーションであることもあります。

開発者が注意すべきこと

バグがあっても平気なコードを作るうえで大切なことは、アプリケーション内にブラウザを組み込む場合、あるいはUIを構築するうえでHTMLを採用する場合に、⁠単なる組込みブラウザ」なのか、HTMLベースのアプリケーションを作るための「特権付きのWebView」なのかを意識しておく、ということです。ネイティブアプリケーションの場合、Webアプリケーションと違って「クロスドメイン通信が可能である」⁠ローカルファイルの読み取りが可能である」⁠アプリケーションの機能を呼び出すことが可能である」など、Webサイト上よりも強い権限を持って動作している可能性が高いのは、これまで見てきたとおりです。

何らかの脆弱性があった場合、そこから発生する脅威の見積もりは非常に難しいことで、そのプラットフォーム上で何ができるのか、悪用された場合にどういうコードが実行可能になるのかを完全に把握していなければ、正確に対処できません。HTML+JavaScriptがアプリケーション実行環境として広く普及していく中、Webアプリケーションで繰り返されてきたような、JavaScriptに起因する脆弱性が多く起きています。著名なソフトウェア開発者であっても、基本的な部分でミスをすることがあります。バグがある前提で、そもそも安全にする方法を考えることが肝要です。

安全なコードを書くために

多くの場合は既知の問題であり、対処方法がよく知られているものばかりです。では何が知られていないのかというと、⁠そこでコードが実行可能な場合にどの程度の影響があるか」といった感覚であると筆者は考えています。影響範囲がわからなければ、単なるバグであるのかそれともセキュリティホールになり得るものであるのか、判別できません。また、バグが発覚した場合に、どの程度の優先度で直さなければならない問題なのか、適切にハンドリングできなくなります。悪用方法が具体的に理解されていなければ、長時間放置されてしまうことも考えられます。筆者はたまたま、HTMLやJavaScriptやブラウザの挙動について多少人よりも詳しいだけです。リスクをより正確に把握するためには、iOS であればiOS の、Android であればAndroidのプラットフォーム固有の知識が必要になるでしょう。また、考えうる限りの邪悪なコードを書く能力を備え、OSの内部構造に詳しく、組み合わせて利用可能な既知のバグを大量に知っている必要があります。

しかし、安全なコードを書くために、これらの知識を網羅していなければならないかというと、そういうわけではありません。攻撃コードを書くためには、特定のOSや特定のブラウザの事情に詳しい必要がありますが、安全なコードを書くために必要な知識は、本来、ずっと少ないのです。

やや抽象的ですが、まとめとして次のようなことが言えるでしょう。

  • どういう権限でコードが動くのかを知っておこう
  • バグがあっても安全になる方法を考えよう
  • 将来に渡って安全な方式を採用しよう
  • 予想外の挙動や、リスクを増加させるような不適切な仕様は、いずれプラットフォーム側で修正されるはずです。普遍的な、少しの基本的な法則と、段階的な学習でカバーしていくのが望ましいでしょう。

おすすめ記事

記事・ニュース一覧