書籍転載:[iOS/Android対応]HTML5ハイブリッドアプリ開発[実践]入門(8)
addJavascriptInterface方式(後編)/ネイティブからJavaScriptへ値を渡す
Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブとの通信の仕組みを解説。書籍転載の8本目。
オープンソースのフレームワーク「Apache Cordova」(Adobe版:「 PhoneGap」)を用いると、HTML5でiOSとAndroid向けのアプリをまとめて作成できます。この連載記事(=書籍転載)の第1回~第6回で、その開発方法を一通り解説しています。また、第7回からは、「JavaScriptコード」と「iOS/Andoridネイティブ機能」をつなぐ仕組みを説明しています。
書籍転載について
ご注意
本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。
前回は「JavaScriptからネイティブの機能を呼び出す方法/addJavascriptInterface方式(前編)」を説明しました。本稿はその続きです。
■
11.3.4 適用範囲の制限
この方法では、addJavascriptInterfaceメソッドで設定したWebViewで読み込んだすべてのHTMLでブリッジが提供されます。また、iframe内で読み込んだHTMLからもこのオブジェクトのメソッドを利用できます。つまり、WebView内で読み込んだHTMLのすべてのJavaScriptからJavaのオブジェクトを呼び出せます。
逆に、addJavascriptInterface方式の場合、特定のドメインを持つHTMLにのみブリッジを提供するということができません。外部の信頼できないHTMLを読み込んだときに、ブリッジを不正に利用されて情報を盗まれることもあり得ます。
したがってセキュリティ上、このWebView内では外部の信頼できないHTMLを読み込むことができません。通常のページ遷移以外にもiframeで外部のURLを読み込むこともできません。
通常、addJavascriptInterfaceメソッドを用いる場合は、信頼できないHTMLを必ずブロックするようにします。AndroidのWebViewでは、リスト11.7のように、shouldOverrideUrlLoadingメソッドをオーバーライドしたWebViewClientオブジェクトを設定することで、信頼できない外部のHTMLの読み込みを制限できます。
package com.example.hybridappsandbox;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class ShouldUrlOverrideExample extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView webView = new WebView(this);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new MyWebViewClient());
webView.loadUrl("file:///android_asset/shouldUrlOverrideExample.html");
setContentView(webView);
}
class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// assetsディレクトリ以下以外のファイルには遷移しないようにする
return !url.startsWith("file:///android_asset/");
}
}
}
|
11.3.5 セキュリティ上の懸念
addJavascriptInterface方式にはセキュリティ上の懸念があるため、ハイブリッドアプリフレームワーク内部での扱いは限定的です。
Android 4.2未満では、addJavascriptInterfaceメソッドで公開したオブジェクトに対して、JavaScriptからリフレクションAPIを通じて内部の公開されていないプロパティにアクセスできます(リスト11.8)。
<html>
<head></head>
<body>
<script type="text/javascript">
var klass = Bridge.getClass();
var field = klass.getDeclaredField('context');
field.setAccessible(true);
// Contextオブジェクトを盗まれる
var context = field.get(Bridge);
</script>
</body>
</html>
|
リスト11.8の例では、Contextオブジェクトを盗み出しています。Contextオブジェクトは、そのAndroid端末にある多くの情報にアクセスできます。XSSや信頼できないHTMLを読み込んでしまった場合にContextオブジェクトを盗まれると、その端末の様々な情報が漏洩することにつながります。
11.3.6 公開するメソッドを制限する
Android 4.2 Jelly Bean以降では、addJavascriptInterfaceメソッドの仕様に変更が加わり、前述したようなリフレクションAPIなどを用いて公開されていないプロパティやメソッドにアクセスすることはできなくなりました。JavaScriptに公開されるのは、 @Javascript Interfaceというアノテーションが付いたメソッドのみとなります。
AndroidのビルドターゲットをJelly BeanのAPIバージョンである17以上に設定し、AndroidMenifest.xmlのtargetSdkVersionも17以上に設定します。今までのaddJavascriptInterfaceメソッドを使った例が動かなくなっていることがわかると思います。
リスト11.9のように、JavaScriptに対して公開したいメソッドに @JavascriptInterfaceアノテーションを追加します。
package com.example.sandbox;
import android.webkit.JavascriptInterface
class MyCustomHander {
// このメソッドのみを公開する
@JavascriptInterface
public void doSomething() {
// ...
}
}
|
これにより、JavaScript側に対して公開するプロパティやメソッドを制限できます。リフレクションAPIを用いてプライベートなプロパティやクラスにアクセスすることも防げるようになります。
ただし、Android 4.2未満の端末は執筆時点(2 013年)ではまだ半数以上が利用されています。また、相変わらずaddJavascriptInterface方式では、ブリッジを提供するHTMLに制限をかけることができません。このことを鑑みると、 @JavascriptInterfaceアノテーションによる制限によってaddJavascriptInterface方式が完全にセキュアになるとは考えないほうがよいでしょう。
11.4 ネイティブからJavaScriptへ値を渡す
ブリッジの方法には、JavaScriptからはネイティブの機能を呼び出すことができるけれども、ネイティブからJavaScriptに対して結果を返すことができないものもあります。この点を補うには、ネイティブからJavaScriptへ値を渡す方法が別途必要になってきます。
ここでは、WebViewから動的にJavaScriptを実行するAPIを通じてJavaScriptへ値を渡す方法を紹介します。
11.4.1 iOSの場合
iOSのUIWebViewクラスには、UIWebViewクラスで読み込んでいるクラスに対してJavaScriptを動的に実行するstringByEvaluatingJavaScriptFromString:というメソッドがあります。
このメソッドを呼び出すと、引数として渡したJavaScriptが実行されます。またこのメソッドでは、その評価したJavaScriptのコードの返り値を取得できます(リスト11.10)。
NSString *result = [webView
stringByEvaluatingJavaScriptFromString: @"alert('hoge');"];
|
11.4.2 Androidの場合
Androidでは、iOSとは違ってWebViewに対してJavaScriptを実行するメソッドはありません。その代わり、loadUrlメソッドにjavascriptスキームのURLを読み込ませることで、JavaScriptをネイティブから動的に実行することができます(リスト11.11)。
webView.loadUrl("javascript:alert('hoge');");
|
iOSではstringByEvaluatingJavaScriptFromString:メソッドを用いてJavaScriptを評価すると、その返り値を取得できましたが、Androidでのjavascriptスキームを使った方法では、JavaScriptは実行できますが、その評価された結果の値を得ることはできません。
11.4.3 値を渡す
iOSとAndroidでネイティブ側から動的にJavaScriptを実行する方法を説明しました。
これを利用してネイティブ側からJavaScriptに値を渡すには、JSONライブラリを用いてJavaScriptのグローバル変数に値を代入します(リスト11.12、リスト11.13)。
[webView evaluateJavaScript: @"window.objectFromNative = 'foobarvalue';"];
|
webView.loadUrl(
"javascript:window.objectFromNative=" + new JSONObject().toString());
|
■
次回は「カスタムURLスキーム方式/iOSで特定のページの読み込みを制限する」を説明します。
久保田光則(くぼたみつのり)
東京都在住。アシアル株式会社に所属するUI/UXデザイナー兼ソフトウェアエンジニア。社内では,HTML5ハイブリッドアプリの開発に多数関わる。優れたデザインとエンジニアリングを両立したオーバークオリティなアプリケーションの開発を実現するために日々,頑張る。
アシアル株式会社(あしあるかぶしきがいしゃ、Asial Corporation)
PHPなどのサーバサイドの技術と,PhoneGapなどのスマートフォン関連を中心とした開発を手がける技術ベンチャー。HTML5ハイブリッドアプリをブラウザ上で開発できるMonacaや,PhoneGapの日本語情報を配信するPhoneGap Fanなどのウェブサービスを手がける。
※以下では、本稿の前後を合わせて5回分(第6回~第10回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
6. Android向けのCordovaプラグインを実装する
アプリの一部をネイティブで記述するには、プラグインの実装が必要。そこでAndroid向けにCordovaプラグインを実装する方法を解説。今回で「Cordovaアプリ開発の基礎」に関する部分の転載完了。
7. JavaScriptからネイティブの機能を呼び出す方法 ― addJavascriptInterface方式(前編)
Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブとの通信の仕組みを解説する。書籍転載の7本目(「Part 2《実践編》 第11章 JavaScriptとネイティブとのブリッジ」より)。
8. 【現在、表示中】≫ addJavascriptInterface方式(後編)/ネイティブからJavaScriptへ値を渡す
Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブとの通信の仕組みを解説。書籍転載の8本目。
9. カスタムURLスキーム方式/iOSで特定のページの読み込みを制限する
JavaScriptからネイティブ側へ命令を投げる方法(Android&iOS対応)を紹介。またiOSでセキュリティのために、特定のドメインのページの読み込みを制限する方法も紹介。書籍転載の9本目。
10. JavaScriptとネイティブの通信の仕組み ― JsAlert方式/ローカルHTTPサーバ方式
ついに書籍転載最終回。Apache Cordova/Adobe PhoneGapによるハイブリッドアプリ内での、JavaScriptとネイティブの通信の仕組みの解説が完結する。