モバイル/デスクトップアプリケーションにおける証明書ピンニング
HPKPやTrustKit風のピンニングを実装し、ピンニングに伴う運用上のリスクを理解します。
「モバイル/デスクトップアプリケーションにおける証明書ピンニング」はCoddyKit上の無料Cryptology Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはCryptology Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Cryptology Academyコースには全4レッスンが含まれています。
証明書ピンニングが存在する理由
標準的なTLSでは、OSにあらかじめインストールされた約150のroot CAのいずれかによって署名された証明書を信頼します。いずれかのroot CAが侵害または強制されると、攻撃者は任意のドメインの証明書を取得してTLSトラフィックを傍受できます。証明書ピンニングは、どのCAが署名したかにかかわらず、特定の証明書または公開鍵だけに信頼を限定します。ピン留めを行ったアプリケーションは、サーバーが想定どおりの証明書または鍵を提示しない限り、サーバーへの接続を拒否します。この保護は、ユーザーがネットワークトラフィックを検査できず、企業のMDMソリューションによってエンタープライズCAのroot証明書がインストールされる可能性もあるモバイルアプリで特に有効です。
ピンの種類:証明書、公開鍵、SPKI
ピンニングには、3つの粒度があります。(1) 完全な証明書ピン — DERエンコードされた証明書全体が一致する必要があります。証明書を更新するだけで機能しなくなるため、最も壊れやすい方式です。(2) 公開鍵ピン — SubjectPublicKeyInfo(SPKI)のバイト列だけを比較します。同じキーペアを保持していれば、証明書を更新しても利用できます。(3) SPKIのハッシュ — 生の鍵ではなく、SHA-256(SPKI)を保存します。これはHTTP Public Key Pinning(HPKP)およびAndroid Network Security Configで採用されている方式です。公開鍵またはSPKIのピンニングが推奨されます。CAのローテーションや証明書の更新に対応しながら、異なるキーペアを使用したMITM攻撃を検出できるためです。
Android Network Security Config
Android(API 24以降)では、Network Security Config XMLを介して宣言的にピンニングを設定できます。res/xml/network_security_config.xmlファイルで、ドメインごとのピンを指定します。pin-setにdigest="SHA-256"と、Base64エンコードされたSPKIハッシュを設定します。アプリはAndroidManifest.xmlでandroid:networkSecurityConfigを指定して、このファイルを参照します。Androidは、標準のHttpsURLConnectionおよびOkHttp(プラットフォームのtrust managerを使用する場合)を介して行われるすべてのHTTP接続に対してピンを適用します。プライマリキーが侵害された場合のロックアウトを防ぐため、pin-setには少なくとも1つのバックアップピン(別の鍵またはCAピン)が必要です。ピンの有効期限(expiration属性)を設定すると、ピンが古くなる前にアプリを更新するよう強制できます。
iOS / macOSの証明書ピンニング
iOSアプリでは、NSURLSessionのデリゲートでピンニングを実装します。URLSession(_:didReceive:completionHandler:)デリゲートメソッドは、サーバーのtrustオブジェクトを受け取ります。アプリはSecTrustEvaluateWithErrorを呼び出してチェーンを検証し、SecTrustGetCertificateAtIndex(trust, 0)でリーフ証明書を抽出し、そのSPKIバイト列を取り出してSHA-256でハッシュ化し、保存済みのピンと比較します。TrustKit(オープンソースライブラリ)は、設定ベースのピンニングでこのパターンをラップし、複数のピン、サブドメインへの適用、レポートのみのモードをサポートします。AppleのApp Transport Security(ATS)はピンニングとは別の機能です。ATSはTLSバージョンの最低要件を適用しますが、鍵のピンニングは行いません。
HPKP:HTTP Public Key Pinning(非推奨)
HTTP Public Key Pinning(HPKP、RFC 7469)は、HTTPレスポンスヘッダーによってWebブラウザーにピンニングを追加しようとした仕組みです。Public-Key-Pins: pin-sha256="base64=="; max-age=5184000; includeSubDomains。ブラウザーはmax-ageの期間、ピンを記憶し、鍵が一致しない接続を拒否します。HPKPは、壊滅的な障害につながる可能性があったため、2017年にChromeで非推奨となり、2019年に削除されました。設定を1つ誤るか鍵を失うだけで、復旧手段なしにユーザーをWebサイトから永久に締め出す可能性があったためです。HPKPは現在、Webブラウザーでは事実上廃止されています。一方、アプリの更新で新しいピンを配布できるため、モバイルアプリにおけるアプリケーションレベルのピンニングは引き続き有効です。
OkHttpでのピンニング
OkHttp(Androidで広く使用されています)は、CertificatePinnerを介してピンニングをサポートします。CertificatePinner.Builder().add("api.example.com", "sha256/AAAA...==", "sha256/BBBB...==").build()。2つ目のピンがバックアップです。OkHttpは、サーバーの証明書チェーン内にある証明書(リーフ、中間、root)のいずれかが少なくとも1つのピンと一致することを検証します。これにより、中間CAへのピンニング(リーフ証明書のローテーションに対応)や、root CAへのピンニング(中間CAのローテーションに対応)が可能になります。OkHttpはSSLPeerUnverifiedExceptionをスローし、サーバーが実際に使用しているSPKIハッシュを一覧にしたわかりやすいメッセージを表示するため、開発中にピンを簡単に抽出できます。
ピンニングの回避:攻撃者の手法
ピンニングによってトラフィック傍受の難易度は上がりますが、突破できないわけではありません。モバイルで一般的な回避手法には、次のものがあります。(1) Fridaフック — アプリプロセスにJavaScriptを注入し、ピン検証メソッドをフックして常にtrueを返します。(2) SSLUnpinningツール — 一般的なピンニングライブラリ(TrustKit、OkHttp、ネイティブのSecTrust)を対象とする自動化されたFrida/Objectionスクリプトです。(3) カスタムROM — デバイスをroot化し、TLSスタックを変更します。(4) リパッケージ — APKを逆コンパイルし、ピンニング設定を変更して、新しい証明書で再パッケージします。(5) メモリパッチ — 実行時に検証用バイトコードを書き換えます。対策としては、root化・脱獄検知、コード難読化、整合性チェック(SafetyNet/App Attest)などがあります。
バックアップピンと災害復旧
証明書ピンニングにおける最大の運用リスクは、自分でアプリを締め出してしまうことです。本番キーを紛失した場合や、証明書の有効期限が切れてバックアップを利用できない場合、アプリの更新が配布されるまで(数日から数週間)ユーザーはサービスにアクセスできません。ベストプラクティスは次のとおりです。(1) 常に少なくとも2つの鍵をピン留めします。現在の鍵と、オフライン(HSMまたはエアギャップ環境)に保存した事前生成済みのバックアップキーです。(2) ピンの有効期限を設定し、期限前にアプリの更新を配布します。(3) 適用を開始する前に、レポートのみのモードでピンの失敗を監視します。(4) ピンのローテーションに関するインシデントに備え、緊急アプリ更新のパイプライン(審査の迅速化)を維持します。(5) リーフではなく中間CAのレベルでピン留めし、アプリの更新なしでリーフ証明書をローテーションできるようにします。
デスクトップアプリケーションのピンニング
Electron、Qt、またはネイティブコードで作成されたデスクトップアプリケーションは、TLSスタックのAPIを使用してピンニングを実装できます。Electronアプリでは、app.on("certificate-error")イベントとsession.setCertificateVerifyProc()を使用してカスタム検証を実装します。Qtのネットワークコードでは、カスタム検証コールバックを備えたQSslSocketを使用します。.NETアプリケーションでは、ServicePointManager.ServerCertificateValidationCallbackを使用します。ネイティブWindowsアプリでは、WinHTTPを使用して証明書を手動で検査します。デスクトップアプリケーションには、さらに別の課題があります。企業プロキシによるOSレベルのTLSインターセプトが一般的で、ユーザーはプロキシ機能が動作することを期待する場合があります。そのため、ピンニングを特定のエンドポイントだけに適用するかどうかをポリシーとして決める必要があります。
CI/CDと自動テストにおけるピンニング
証明書ピンニングは、自動テストとCI/CDパイプラインを複雑にします。ステージングサーバーに実際のHTTPSリクエストを送る統合テストでは、SPKIハッシュをテスト設定にピン留めしたテスト証明書を使用する必要があります。方法には次のものがあります。(1) Build flavors — デバッグまたはステージングビルドにはステージングサーバーのピンを含め、リリースビルドには本番環境のピンを設定します。(2) Network Security Configのオーバーライド — Androidでは、デバッグ専用のピン設定が可能です。(3) モックサーバー — TLSの前にHTTPクライアント層でインターセプトし、ピンニングを完全に回避します。(4) CI用の自己署名CA — テストビルドでのみ信頼されるCI CAからテスト証明書を発行します。本番環境でピンニングを無効にしたビルドを出荷してはいけません。
ポスト量子時代のピンニングに関する考慮事項
証明書ピンは通常、RSAまたはEC公開鍵のハッシュです。ポスト量子暗号への移行が始まると、サーバーはML-DSA(CRYSTALS-Dilithium)またはハイブリッド鍵へ移行します。鍵の種類とエンコード方式が変わるため、ピン留めされたSPKIハッシュも変わります。リーフ証明書または公開鍵をピン留めしているアプリでは、移行を調整して行う必要があります。(1) サーバー移行の前に、ポスト量子SPKIハッシュをバックアップピンとして含む新しいアプリバージョンを配布します。(2) サーバーの移行を完了します。(3) 古い古典暗号のピンを削除する更新を配布します。移行期間には慎重な調整が必要です。中間CAまたはroot CAをピン留めしているアプリでは、影響が小さくなります。変更されるのはCAの鍵だけであり、必ずしもリーフ証明書と同じタイミングで変更されるとは限らないためです。
証明書ピンニングのクイズ
完全な証明書ではなく、SubjectPublicKeyInfo (SPKI) のハッシュをピンニングする方が好ましいのはなぜですか。
証明書ピンニングのまとめ
証明書ピンニングは、TLSの信頼を特定の証明書または公開鍵に制限し、CAの侵害やMITMから保護します。SPKIハッシュピンニング(SubjectPublicKeyInfoのSHA-256)は、更新に対する耐性があるため、完全な証明書のピンニングよりも好まれます。AndroidではNetwork Security Config XMLを使用し、iOSではSecTrust APIを使うURLSessionデリゲートを使用します。OkHttpはCertificatePinnerをサポートしています。自己ロックアウトを防ぐため、必ずバックアップピンを含めてください。HPKP(ブラウザーのHTTPヘッダー)は非推奨です。ピンニングは、FridaフックやROMの変更によってバイパスされる可能性があります。ポスト量子鍵への移行では、SPKIハッシュを更新するために、アプリの更新を連携して行う必要があります。
よくある質問
「モバイル/デスクトップアプリケーションにおける証明書ピンニング」レッスンは無料ですか?
はい。「モバイル/デスクトップアプリケーションにおける証明書ピンニング」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Cryptology Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Cryptology Academyコースには全4レッスンが含まれています。
「モバイル/デスクトップアプリケーションにおける証明書ピンニング」で何を学びますか?
HPKPやTrustKit風のピンニングを実装し、ピンニングに伴う運用上のリスクを理解します。 ブラウザで直接実行するハンズオンコードでCryptology Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Cryptology Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのCryptology Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「モバイル/デスクトップアプリケーションにおける証明書ピンニング」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このCryptology Academyレッスンでコードを書いて実行できますか?
はい。すべてのCryptology Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- TLS 1.3:0-RTT、Early Data、セッション再開
- 相互TLS(mTLS)の実装パターン
- モバイル/デスクトップアプリケーションにおける証明書ピンニング
- TLSのパフォーマンス:QUICとHTTP/3