UIWebView の取り扱いによってはjavascript経由でサンドボックス外のファイルシステムにアクセスできてしまうという話と対処法

この記事はiOS 5以前の問題について書かれました

XSS in Skype for iOS « Superevr
https://superevr.com/blog/2011/xss-in-skype-for-ios/
Skype XSS Explained « Superevr
https://superevr.com/blog/2011/skype-xss-explained/
Boxcarに学ぶプラットフォーム別セキュリティリスク - 金利0無利息キャッシング – キャッシングできます - subtech
http://subtech.g.hatena.ne.jp/mala/20110428/1304005287

これの話。
どういう実装をすると、このような現象が起きるのか。またどのような対策を施せば攻撃を防げるのか興味があったので自分がわかる範囲で調べた。
書こうと思ってからずいぶん時間がたってしまったし、全体的に知識不足・調査不足なのでツッコミ歓迎。

概要

file://, about://(applewebdata://) などの特権的なURLリクエストでロードされたUIWebView からjavascriptを利用してデバイスのアドレス帳、着信履歴などのデータファイルにアクセスできる為、
開発者が意図しないスクリプトを第三者に実行され、アプリユーザーのデータを盗まれてしまう可能性がある

Phil Purviance の報告によると ローカルのURL file:// を明示しないとこれは起きないという風に読めるけど、
俺がmalaさんに聞きながら確認してたケースではURLを無指定な状態 about:// (applewebdata://) でも起きることが確認できた(サンプルコードをあげておいたのでやってみて)。

サンドボックス外へアクセスできるケース

  1. Web APIなどから取得したデータを利用してHTMLを整形し、UIWebView でビューをつくっている
  2. ローカルのHTMLファイルをUIWebViewで読み込んだ状態から外部のデータを読み込んでjavascriptでDOMを生成している

UIWebView 以外にも PhoneGAP で作ったアプリで外部データを動的に読み込んでいるもの(file:// になってる)訂正

BoxacarとかSkypeとかがたぶんあてはまる。あと他のiTunes App Storeで公開中のアプリにも該当しそうなものが結構ある。

おそらくされない

  1. baseURLを適切に設定してある
  2. UIWebView にhttp[s]:// 〜 のスキームのリクエストを渡して単純な埋め込みウェブブラウザとして利用している
  3. Titanium Mobile の Titanium.UI.createWebView で作ったWebView (app://${Application Id}/hoge というデフォルトURLになった)

実行URLの簡単な確認方法

UIWebViewDelegate で現在のURLを表示してみる

    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
      NSLog(@"Request URL: %@", [webView.request URL]);
      NSLog(@"DOM URL: %@", [webView stringByEvaluatingJavaScriptFromString:@"document.URL;"]);
    }

アプリに含まれたHTMLファイルを読みこんでUIWebView に読み込ませるケースはこんなかんじ

    NSURL* URL = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:@"index.html"];
    [_webView loadRequest:[NSURLRequest requestWithURL:URL]];

    Request URL: file:///var/mobile/Applications/9CC77062-D748-4E16-92B4-7A043494D739/UIWebView+XSSSample.app/index.html
    DOM URL: file:///var/mobile/Applications/9CC77062-D748-4E16-92B4-7A043494D739/UIWebView+XSSSample.app/index.html

あとabout://(applewebdata://) になる例というのは例えばよくある「HTML文字列をその場で整形してUIWebView に読み込ませる」というやり方で画面を実装していたとする

    [_webView loadHTMLString:@"<h1>Hello World!</h1>" baseURL:nil];

こういうの。

その時の上記のログ表示はこうなる。

    Request URL: about:blank
    DOM URL: applewebdata://6A65BE08-DD5B-477B-9C78-888803146C52

これはローカルファイルへのリクエストのかわりに、ローカルファイルを文字列として読み込んでUIWebView に渡すことも同等になる。

    NSString* src = [[[NSString alloc] initWithContentsOfURL:[[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:@"index.html"] 
                                                    encoding:NSUTF8StringEncoding 
                                                       error:nil] autorelease];
    [_webView loadHTMLString:src baseURL:nil];

簡単な対処方

baseURL のパラメータをセットして file:// スキームへのアクセスを抑制できる。

    [_webView loadHTMLString:src baseURL:[NSURL URLWithString:@"http://"]];  
    /** 
    * file://, applewebdata:// だと抑制できない。
    * hoge:// とか適当なのスキームにするとWebView自体のロードエラーがでるはず。なので http://〜 がよさそう
    */

    Request URL: http://
    DOM URL: http://


ただこの場合HTMLの中で <img src="imapge.png"> のようなURLでバンドルリソースのルートを読み込むのを期待していても http://imapge.png というURLに解釈されしまうので動的にフルパスに置き換えないといけなくめんどうくさい。

あと、本来のアプリの実装でXMLHttpRequest を発行している場合リスエクトが失敗してしまう。その時はbaseURL を http://api.example.com まで指定すれば api.example.com にはアクセスできるようになる。

サンプルコードでたいていそうなので baseURL:nil になっているコードは結構ありそう、

loadHTMLString baseURL:nil lang:objectivec - Code Search

http://www.google.co.jp/codesearch#search/&q=loadHTMLString%20baseURL:nil%20lang:objectivec&type=cs

HTMLのエスケープ処理

あとはGoogle Toolbox for Mac やらウェブにHTMLエスケープするスニペットやらがあるので入力を描写する際に逐一チェックするというウェブ開発と同じような作法が……

http://code.google.com/p/google-toolbox-for-mac/

今回使用したサンプルコード+いろいろ

https://github.com/laiso/iPhone-UIWebView-XSSSample

まとめ

前提: 外部からの入力のあるアプリでは

  • loadRequest でfile:// を読まない
  • loadHTMLString はbaseURL を設定する
  • 入力値をチェックする

知りたいこと

  • もっとスマートな対処法
  • Osfooraは結局どういう実装だったと推測されるの?(file:// で動いてる所とは別に、javascript が実行できた部分はbaseURL が効いている?)
  • mala さんはiOS開発を専門としていないし、俺はソフトウェア開発自体の経験が浅いので今現在Cocoaアプリ/iPhoneアプリ開発を一線でやってる人たちがこの話題をどう認識しているのか
  • これに関する議論が過去にあったのか。あればその参照先
  • Skype脆弱性で話題になったし。アップルはこれを改善する? 仕様でこのまま?
  • applewebdata:// って一体なんなの……

更新: Fri Oct 7 17:43:37 JST 2011

リソースの安全な読み込みについて、

カスタムリソースにはNSURLProtocolを使えば良いのじゃないか? — sklave

のNSURLProtocol でカスタムスキーマの挙動を定義する方法が理想っぽい。参考になる。

更新:Fri Oct 14 20:11:50 JST 2011

iOS5 SDK のリリース版に更新してみたけどかわらずなので、

Bug Reporting Best Practices - Apple Developer
http://developer.apple.com/jp/bugreporter/bugbestpractices.html

に送った

→ "Closed" で処理された。次のバージョンでは直ってるのかな。

更新:Mon Oct 24 23:45:23 JST 2011

PhoneGap について 1.1.0 のバージョンを再確認したところ、
file:// スキームで動作していたものの、リモートのJavascript ファイルを読み込ませる部分は動作しなかった。
どういう実装なのか確認しているけど、上記NSURLProtocol で中間処理をしている為っぽい。

更新

NSURLProtocol を定義してUIWebView で安全にローカルのリソースを読み込む - laiso - iPhoneアプリ開発グループ
http://iphone-dev.g.hatena.ne.jp/laiso/20111130/1322649990