React Nativeで学ぶSSL Pinningの基礎と実装手法解説

目次

はじめに

本書の目的

この文書はReact NativeアプリにおけるSSLピンニングの概要と実装方法をわかりやすく説明します。通信の安全性を高めたい開発者や運用担当者に向けています。

SSLピンニングとは

SSLピンニングは、アプリが通信相手の証明書をあらかじめ確認して、偽物のサーバーと接続しないようにする仕組みです。例えば銀行アプリが公共Wi‑Fi上で中間者攻撃(通信の盗み見や改ざん)を防ぐために使います。

なぜ重要か

標準の証明書検証だけでは、端末側で証明書が盗用されたり不正な証明書が信頼されるリスクがあります。ピンニングを導入すると、アプリは許可した証明書だけを受け入れるため攻撃の面を狭められます。

本書の構成

第2章で概念を詳述し、第3章で実装方法の選択肢を比較、第4章で具体的な手順を示します。実例と注意点を交えて進めます。

SSL Pinning in React Native

概要

SSLピンニングは、アプリが特定の信頼できるサーバーとだけ通信するように証明書や公開鍵を事前に組み込む手法です。通常の証明書検証だけでは、正規の認証局(CA)発行の証明書を悪用した中間者攻撃(MITM)を防げない場合があります。ピンニングはそのリスクを減らします。

仕組みの簡単な説明

アプリ内にサーバーの証明書や公開鍵のハッシュを持ち、接続相手の証明書と照合します。一致すれば通信を許可し、一致しなければ遮断します。例として、プロキシツールが正しいCA証明書を使っても、ピンされた情報が合わなければ接続は拒否されます。

種類と特徴

  • 証明書ピンニング:サーバー証明書そのものをピンします。短期では確実ですが、証明書更新時にアプリ更新が必要になることがあります。
  • 公開鍵ピンニング:公開鍵をピンします。証明書を更新しても同じ鍵を使えば問題なく動作します。

React Nativeで注意する点

React NativeはJavaScriptとネイティブ両方で動くため、ピンニングは通常ネイティブ側で実装し、ブリッジで呼び出します。ライブラリを使えば手間を減らせますが、プラットフォームごとの動作差や証明書更新時の運用を必ず検討してください。

利点と限界

利点はMITM防止の強化と悪意あるプロキシの検出です。限界は証明書更新運用の負担や、端末ルート証明書追加など高度な回避手段には必ずしも無力な点です。運用ポリシーと組み合わせて導入を検討してください。

Implementation Approaches

Native Platform Configuration

ネイティブ側で設定する方法です。Androidではres/xmlにnetwork_security_config.xmlを作り、許可する証明書やアンカーを定義してAndroidManifest.xmlで参照します。たとえば特定ドメインの証明書だけを信頼する設定が可能です。iOSではTrustKitなどを使い、ドメインごとに公開鍵ハッシュ(public key hash)を指定してピン留めします。ネイティブで行うとOSのネットワーク層で検証するため堅牢です。

Third-Party Libraries

既成のライブラリを使う方法は手軽です。たとえばreact-native-ssl-pinningはAndroidでOkHttp、iOSでAFNetworkingを内部で使い、証明書ピンや公開鍵ピンをサポートします。使い方はfetchに似た関数へオプションを渡すだけで、例: methodやpkPinning、pinnedCertsなどを設定します。短時間で導入でき、React Native側の実装が簡単になりますが、ライブラリの保守状況を確認してください。

OkHttpClient Factory Pattern (Android)

Android向けに細かく制御したい場合はOkHttpClientを自前で構成します。CertificatePinnerにSHA-256ハッシュを登録し、OkHttpClientFactoryを通じてアプリ全体に適用します。MainApplication.javaでファクトリを登録すると、ネイティブモジュールから常にピン付きクライアントが使われます。細かい設定やカスタムハンドリングが必要な場合に適しています。

Key Implementation Steps

まずの流れ

実装は大きく4つに分かれます。1) サーバ証明書とSHA-256フィンガープリントの取得、2) 証明書ファイルの配置、3) 証明書ハッシュまたは公開鍵でのピニング設定、4) アプリ起動時の登録とテスト、の順で進めます。

1. 証明書とフィンガープリントの取得

サーバ証明書を取得し、SHA-256フィンガープリントを作成します。例:
– サーバからcert.pemをダウンロード
– SHA-256ハッシュ取得(例):
openssl x509 -in cert.pem -noout -fingerprint -sha256
公開鍵のハッシュを使う場合は次のように抽出します:
openssl x509 -pubkey -noout -in cert.pem | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

2. 証明書ファイルの配置

  • Android: android/app/src/main/res/raw/ に .crt/.cer を置くか、サードパーティが要求する場合は assets に置きます。
  • iOS: Xcodeでプロジェクトに .cer を追加し、”Copy Bundle Resources” に含めます。

3. ピニング設定(ハッシュか公開鍵)

ドメインごとに許可するハッシュ配列を用意します。例:
– pins = { ‘api.example.com’: [‘sha256/BASE64==’] }
ライブラリが証明書固定(certificate pinning)か公開鍵固定(public key pinning)かをサポートするか確認してください。

4. アプリ初期化での登録とテスト

アプリ起動時にピニング設定を登録します。擬似例:
– importして設定を登録
– すべてのネットワークリクエストが検証されるようにミドルウェアやHTTPクライアントをラップします。

テスト方法:
– 正しい証明書で接続が成功するか確認
– 証明書を差し替え(意図的に無効化)して接続が拒否されるか確認

注意点:
– 証明書を更新したらハッシュや公開鍵も更新してください。ロールアウト計画を立てると安全です。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

目次