(この記事は BRYAN ELLIS が 2017年5月23日に英語版 Monaca x Onsen ブログに投稿した Enhanced Cordova App Security with SSL Certificate Pinning の翻訳です。)

静的アプリ(コンテンツを表示するだけのアプリなど)は動的なアプリ(ユーザーの操作に応じてインターネット上からデータの取得などを行うアプリなど)に比べセキュリティ上の脆弱性は一般的には少なくなります。静的・動的アプリに共通するセキュリティ上の脆弱性として、たとえば、悪意のある第三者によるソースコードの盗聴(盗み取り・盗み読み)が挙げられます。対策としてはアプリ側のコンテンツを暗号化して配信することが有効です。

インターネットを経由したデータの送受信時はセキュリティ上最も攻撃にさらされやすいタイミングです。ユーザーとアプリのどちらにも危険が及びます。ハイブリッドアプリ/ネイティブアプリに関わらず最優先で対処すべきことは、データの完全性をいかに保証するかという点です。攻撃手法としては、本来の送信元になりすまし悪意のある第三者がデータを送信すること、送受信中にデータが改ざんされることなどが考えられます。これらの攻撃は、総じて、中間者攻撃(man-in-the-middle attack)と呼ばれています。

準備するもの

  • Monacaアカウント (カスタムCordovaプラグインが使用できるProプラン以上への加入が必要です)
  • カスタムビルド版Monacaデバッガー

「SSLのピン留め」の使用方法

前述のような攻撃を防ぐための最低限の対策は、エンドポイントURLへの接続にHTTPS/SSLを使用することです。この対策を取ればアプリとサーバー間で送受信されるデータは暗号化されます。

2つ目の対策としては、証明書内のフィンガープリントを確認して信頼性を検証することが挙げられます。こちらが「証明書のピン留め」と呼ばれているものです。この一連の処理を行ってくれるCordovaプラグインがいくつか提供されています(信頼性の検証は、サーバー側の公開鍵のフィンガープリントまたは証明書を確認することにより行われます)。

※ Cordovaでは、本当の意味での「証明書のピン留め」はサポートしていません。これは、AndroidではSSL接続を傍受してサーバー側の証明書を確認するためのAPIが存在しないためです。

今回は、上述のCordovaプラグインの一つである SSLCertificateChecker-PhoneGap-Plugin プラグイン を使用してみます。このプラグインは、予期(指定)していた値と証明書のフィンガープリントが一致しているか確認してくれます。

最初に、このプラグインをプロジェクトに追加します。MonacaクラウドIDE にログイン後、「Cordovaプラグインの管理」画面を開き、[Cordovaプラグインのインポート]ボタンをクリックします。

次に、[URL もしくはパッケージ名を指定します]オプションを選択し、https://github.com/EddyVerbruggen/SSLCertificateChecker-PhoneGap-Plugin.git を入力して、[OK]ボタンをクリックします。

[圧縮された ZIPパッケージ をアップロード]オプションを使用する場合にはプラグインのパッケージ(ZIP 形式)を GitHub からあらかじめダウンロードしておきます。

インポートが完了すると[有効なプラグイン]の一覧上に表示されます。

これで、証明書内のフィンガープリントを確認して信頼性を検証するためのAPIが使用できます(リクエストの発行前に、JavaScriptで検証処理を行います)。

サンプルアプリでは、ボタンをタップすると Monaca 配信のニュースとリリース情報(最新の50本)を表示します。

ボタンをタップすると、あらかじめ入手しておいたフィンガープリントを使用して、接続先の証明書が信頼できるかが判定されます。信頼できると判定された場合にはリクエストを発行してコンテンツを取得します。

<script> タグ 内には次の変数を定義します。

var url = 'https://monaca.mobi/en/api/news/list?type=news_and_release&limit=50';
var fingerprint = '67 C2 0A 07 6F D9 55 23 38 03 E4 78 2E 0C B5 CC 24 0C A8 B8';

変数「url」には、検証対象となるサーバーのURL(データのリクエスト先)を指定します。変数「fingerprint」には、monaca.mobi で使用している公開鍵のフィンガープリント(検証時に使用)を指定します。

次に、window.plugins.sslCertificateChecker にこれらの変数を引数として設定します。また、このAPIには成功時と失敗時のコールバックも設定します(下記サンプルコードの Success Callback と Error Callback)。

コードの概要は次のとおりです。

window.plugins.sslCertificateChecker.check(
  // Success Callback
  function success(message) { },

  // Error Callback
  function error(message) { },


  // URL to test
  url,

  // URL Public Key Certificate Fingerprint
  fingerprint
);

信頼できると判定された場合には成功時のコールバックが実行されます。成功時のコールバックではデータを取得して表示する処理を行います。

信頼できないと判定された場合には次のような原因が考えられます。

  • 接続先の公開鍵(フィンガープリント)が新しいものに置き換わっている。
  • コード内に記述したフィンガープリントが正しくない。
  • サーバーへアクセスできないまたは応答がタイムアウトしている。
  • 中間者攻撃が実際に行われている。

このプラグインでは、データを取得するためのリクエストを発行する前に信頼性の検証が行われます。よって、攻撃への対応策となりえますが次のような欠点があります。

欠点

  • すべてのリクエストの信頼性を保証したい場合、リクエスト毎に検証処理を都度行う必要があります。
  • 仮に起動時に検証を行った場合、その時点での接続は安全だと判定されます。しかしアプリの起動後、たとえばWiFiの接続先が変わった場合には信頼性は保証できなくなります。

こういった問題はアプリの作り込み次第で軽減することもできます。理想的には、すべてのリクエストの検証を行い、リクエストを重複して送信しないように処理を工夫する方が良いでしょう。

こちらのサンプルアプリは、SSL Certificate Checker Demo Repo から入手できます。

この記事がハイブリッドアプリのセキュリティ向上に少しでも役立つことを祈っております。

翻訳者紹介
渡部正和/Masakazu Watabe
フリーランスの翻訳家。インターネット・通信業界で10年ほど翻訳を経験したあと、2013年からフリーランスに。直訳ではない、わかりやすい翻訳を心がけています。校正など翻訳全般に関するご相談がありましたら watabe08@gmail.com(渡部) まで。