はじめに
情報漏洩の対策として、Webviewに格納されるキャッシュを削除するCordovaプラグインを作成しました。Androidアプリのみに対応しています。
以前の記事において、cordova-plugin-ionic-webviewを改修することで、Webviewのキャッシュを削除する方法を紹介していますが、今回のプラグインでも同様の方法を採用してキャッシュを削除しています。
関連記事
以前書いたWebviewのキャッシュ削除についての記事になります。
【Cordova】【Android】 Webviewのキャッシュを消す - 中安拓也のブログ
環境
今回作成したCordovaプラグイン、cordova-plugin-cache-deleteは、下記バージョンのIonic(Angular, Cordova)で作成したAndroidアプリ上で動作確認しています。
ionic info
コマンドの実行結果
$ ionic info Ionic: Ionic CLI : 6.11.8 (/usr/local/lib/node_modules/@ionic/cli) Ionic Framework : @ionic/angular 5.6.6 @angular-devkit/build-angular : 0.1102.11 @angular-devkit/schematics : 11.2.11 @angular/cli : 11.2.11 @ionic/angular-toolkit : 3.1.1 Cordova: Cordova CLI : 10.0.0 Cordova Platforms : android 9.1.0 Cordova Plugins : cordova-plugin-ionic-keyboard 2.2.0, cordova-plugin-ionic-webview 4.2.1, (and 5 other plugins) Utility: cordova-res : not installed native-run : not installed System: ios-deploy : 1.9.2 ios-sim : 6.1.2 NodeJS : v12.13.1 (/usr/local/bin/node) npm : 6.14.12 OS : macOS Catalina Xcode : Xcode 12.4 Build version 12D4e
Cordovaプラグイン作成
今回のCordovaプラグイン開発は以下のフォルダ構成で行います。
. ├── LICENSE.txt ├── README.md ├── package.json ├── plugin.xml ├── src │ └── android │ └── CacheDelete.java └── www └── CacheDelete.js
LICENSE.txt
とREADME.md
は、Cordovaプラグインの機能と直接関係があるファイルではないため、説明を割愛します。
package.jsonの作成
今回作成したプラグイン、cordova-plugin-cache-deleteのpackage.json
になります。
package.json
{ "name": "cordova-plugin-cache-delete", "version": "1.0.0", "author": { "name": "Takuya Nakayasu", "email": "l08084.1989@gmail.com", "url": "https://github.com/l08084" }, "homepage": "https://github.com/l08084/cordova-plugin-cache-delete", "cordova": { "id": "cordova-plugin-cache-delete", "platforms": [ "android" ] }, "repository": { "type": "git", "url": "git+https://github.com/l08084/cordova-plugin-cache-delete.git" }, "description": "This is a WebView cache plugin for Cordova supporting Android. It allows to delete the cordova webview cache.", "license": "Apache-2.0", "keywords": [ "cordova", "webview", "cache", "delete", "Code Cache", "android", "HTTP Cache", "cordova-android" ] }
npm package.json 日本語版 取扱説明書 に従って項目を設定していきます。後述するplugin.xml
と記述内容の不一致が発生しないように注意する必要があります。
plugin.xmlの作成
続いて、plugin.xmlファイルを作成します。
<?xml version="1.0" encoding="UTF-8"?> <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-cache-delete" version="1.0.0"> <name>Cordova Cache Delete</name> <description>This is a WebView cache plugin for Cordova supporting Android. It allows to delete the cordova webview cache.</description> <license>Apache 2.0</license> <keywords>cordova, webview, cache, delete, Code Cache, android, HTTP Cache, cordova-android</keywords> <repo>https://github.com/l08084/cordova-plugin-cache-delete.git</repo> <js-module src="www/CacheDelete.js" name="CacheDelete"> <clobbers target="CacheDelete" /> </js-module> <platform name="android"> <config-file target="res/xml/config.xml" parent="/*"> <feature name="CacheDelete"> <param name="android-package" value="jp.l08084.plugin.CacheDelete"/> </feature> </config-file> <source-file src="src/android/CacheDelete.java" target-dir="src/jp/l08084/plugin/" /> </platform> </plugin>
plugin.xmlファイルの設定内容について説明していきます。
xmlns
とxmlns:android
はxmlns:属性であり、XMLドキュメント内で名前空間の機能を提供して、要素名や属性名の競合を防ぐ機能があります。
別のCordovaプラグインを作成する場合でもxmlns
もxmlns:android
はURIを含めてそのままコピーアンドペーストで問題ないと思います。
<js-module>
はネイティブコードとのIFとして使用されるJavaScriptファイルの設定です。jsファイル内でmodule.exportsしたオブジェクトは、clobbers
のtarget
に設定したオブジェクトで参照できるようになります。
<platform name="android">
はAndroidに関する設定です。android-package
のvalue
はパッケージ名、target-dir
の値はビルド後にソースコードが配置されるディレクトリになるため、それぞれ対応した値を設定する必要があります。
plugin.xmlファイルに関して言うと、私は<param name="android-package" value="jp.l08084.plugin.CacheDelete"/>
に設定されているパッケージ名と<source-file src="src/android/CacheDelete.java" target-dir="src/jp/l08084/plugin/" />
に設定しているtarget-dir
の値(ソースコードが配置されるディレクトリ)が一致していないせいでCordovaプラグインが起動しないバグに苦しみました。皆様もお気をつけ下さい......
JavaScriptファイルの作成
モバイルアプリからネイティブコード(Android)を呼び出す時のIFになってくれるJavaScriptファイルを作成します。
www/CacheDelete.js
var exec = require("cordova/exec"); module.exports = { deleteCache: function (success, error) { exec(success, error, "CacheDelete", "deleteCache", []); }, };
Webviewのキャッシュを削除するメソッド、deleteCache
を定義しています。
plugin.xmlファイルで<clobbers target="CacheDelete" />
と設定しているため、アプリ側から、上記のdeleteCache
メソッドを呼び出す時は、下記のように呼び出す必要があります。
CacheDelete.deleteCache();
メソッド(deleteCache
)内で呼び出している、exec(success, error, "CacheDelete", "deleteCache", [])
メソッドは、ネイティブコード(Angular, iOS)などの処理を呼び出すメソッドになります。execメソッドの内容としては下記の通りになります。
- 第一引数は処理の成功時に呼び出したいコールバック関数。アプリ側から渡したものをそのままネイティブコード側に渡しているため、上記のJSファイルには内容は記載されていません
- 第二引数は処理の失敗時に呼び出したいコールバック関数。。アプリ側から渡したものをそのままネイティブコード側に渡しているため、上記のJSファイルには内容は記載されていません
- 第三引数はネイティブコード側で呼び出すサービス名です。今回の例では、
CacheDelete
クラスを呼び出したいため、"CacheDelete"
を設定しています - 第四引数は、ネイティブコード側に渡されるアクション名です。ネイティブコード側では、渡されたアクションとマッピングするこで対応するメソッドを呼び出します。今回は
"deleteCache"
という文字列を渡しています - 第五引数は、ネイティブコード側に渡したい引数になります。今回は渡したい引数がないため、空の配列(
[]
)を渡しています
ネイティブコード(Android
)の作成
下記のネイティブコード(Android
)で、キャッシュファイルをFile.delete()
メソッドで全て削除する処理を実装しています。
src/android/CacheDelete.java
package jp.l08084.plugin; import java.io.File; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.PluginResult; import org.json.JSONArray; import org.json.JSONException; import android.util.Log; public class CacheDelete extends CordovaPlugin { private static final String TAG = "CacheDelete"; private static final String DELETE_CACHE_MESSAGE = "Cordova CacheDelete.deleteCache() called."; private static final String ERROR_MESSAGE = "Failed to delete the cache, error"; @Override public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException { Log.v(TAG, DELETE_CACHE_MESSAGE); if("deleteCache".equals(action)) { deleteCache(callbackContext); return true; } return false; } private void deleteCache(final CallbackContext callbackContext) { File cacheDir = cordova.getActivity().getApplicationContext().getCacheDir(); clearCacheFolder(cacheDir, callbackContext); } private void clearCacheFolder (File dir, final CallbackContext callbackContext) { try { if (dir != null && dir.isDirectory()) { for (File child : dir.listFiles()) { if (child.isDirectory()) { clearCacheFolder(child, callbackContext); } child.delete(); } } callbackContext.success(); } catch (Exception ex) { Log.e(TAG, ERROR_MESSAGE, ex); callbackContext.error(ERROR_MESSAGE); } } }
上記のネイティブコードだと、execute(String action, JSONArray args, final CallbackContext callbackContext)
メソッドがキーポイントになります。execute
メソッドはJSファイルのexec
からコールされています。execute
メソッドとexec
メソッドは下記の通り対応しています。
execute
メソッドのaction
引数にはexec
メソッドの第四引数で渡した文字列がセットされます。今回は"deleteCache"
がセットされますexecute
メソッドのargs
引数にはexec
メソッドの第四引数で渡した配列がJSONArray
としてセットされます。今回は使用していませんexecute
メソッドのcallbackContext
引数にはexec
メソッドの第一、二引数の成功/失敗時のコールバック関数がCallbackContext
としてセットされます
キャッシュファイルの削除処理が正常完了したら、callbackContext.success();
を呼び出して成功時のコールバック処理を実行し、キャッシュファイルの削除処理が失敗した場合はcallbackContext.error(ERROR_MESSAGE);
で失敗時のコールバック関数を呼び出しています。
プラグインのテスト
上記で作成したcordova-plugin-cache-deleteをIonicプロジェクトに取り込んで、正常に動作するかテストしていきます。
まず、下記のコマンドでIonic(Cordova)のプロジェクトcordovaPluginWebviewCacheTest
を作成します。
$ ionic start cordovaPluginWebviewCacheTest tabs --cordova
続いて、下記のコマンドでIonicプロジェクトにcordova-plugin-ionic-webviewをインストールします。
$ cordova plugin add https://github.com/l08084/cordova-plugin-cache-delete.git
下記のエラーCordovaError: Current working directory is not a Cordova-based project.
が発生してインストールに失敗しました。
(node:18236) UnhandledPromiseRejectionWarning: CordovaError: Current working directory is not a Cordova-based project. at Object.cdProjectRoot (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/src/cordova/util.js:170:15) at /usr/local/lib/node_modules/cordova/node_modules/cordova-lib/src/cordova/plugin/index.js:36:40 at _fulfilled (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:787:54) at /usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:816:30 at Promise.promise.promiseDispatch (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:749:13) at /usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:810:14 at flush (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:108:17) at processTicksAndRejections (internal/process/task_queues.js:75:11) (node:18236) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:18236) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
上記のエラーを修正するため、下記の記事を参考にルートに空のwww
フォルダを作成します。
再度cordova plugin add
コマンドを実行すると、今度はインストールに成功します。
続いて、Ionicプロジェクトからcordova-plugin-ionic-webviewを呼び出すソースコードを書きます。
src/app/tab1/tab1.page.ts
import { Component } from '@angular/core'; import { Platform } from '@ionic/angular'; declare var CacheDelete: any; @Component({ selector: 'app-tab1', templateUrl: 'tab1.page.html', styleUrls: ['tab1.page.scss'], }) export class Tab1Page { constructor(private platform: Platform) {} public ngOnInit(): void { this.platform.ready().then(() => { if (this.platform.is('android')) { CacheDelete.deleteCache(this.successCallback, this.errorCallback); } }); } private successCallback(): void { console.log('success'); } private errorCallback(): void { console.log('error'); } }
TypeScript(Ionicのプロジェクト)からJSのライブラリ(Cordovaプラグイン)を呼び出すという形になるため、declare var CacheDelete: any;
でアンビエント宣言をすることで、CacheDelete
オブジェクトを利用可能な状態にしています。
Webviewのキャッシュを削除する処理を呼び出している箇所はCacheDelete.deleteCache(this.successCallback, this.errorCallback);
になります。
続いて、下記のコマンドでIonicプロジェクトからAndroidアプリを作成して、動作確認をしていきます。
$ ionic cordova build android
上記のビルドコマンドで作成されたAndroidプロジェクトを見ていきます。
plugin.xmlの設定通り、src/main/res/xml/config.xml
にCacheDelete
の設定が反映されていることがわかります。
plugin.xmlの<source-file src="src/android/CacheDelete.java" target-dir="src/jp/l08084/plugin/" />
で設定した通りの箇所にソースファイルが配置されていることも確認できます。
エミュレーターでIonicのAndroidプロジェクトを立ち上げます。
上記AndroidのサンプルアプリからCacheDelete.deleteCache()
を呼び出すと、下記の通り、Webviewのキャッシュファイルが全て削除されることが確認できました。
参考サイト
Ionic Native Pluginを自作する - Qiita
Native APIs - Ionic Framework 日本語ドキュメンテーション
GitHub - rehiy/cordova-plugin-cache-clear: WebView cache plugin for Cordova
【ionic】Ionic native pluginの作成|ブログ|West Wind Corporation
Capacitor - Webアプリをクロスプラットフォームに展開
Cordova plugin.xmlの内容について - Qiita
エンジニアが最低限理解しておくべきOSSライセンスの基礎知識 | フューチャー技術ブログ
ApacheライセンスのソースコードをGitHubにあげるまで - Qiita
Android Plugins - Apache Cordova
TypeScript|アンビエント宣言(declare)と型定義ファイル(.d.ts) - わくわくBank
アンビエント宣言(declare) - TypeScript Deep Dive 日本語版
ionic3のNative pluginを作る #ionic #cordova | 最初の4つの音の先へ
IPA ISEC セキュア・プログラミング講座:Webアプリケーション編 第5章 暴露対策:プロキシキャッシュ対策
重要情報の漏えいにつながるスマホアプリのキャッシュ問題と対策 | セキュリティ対策のラック