Certificate Pinningとは:メカニズムと固定方法

著者: IT Sectr 公開日: 2026-03-09 読了時間: 8 分

Certificate Pinningは、サーバーの証明書または公開鍵を固定するメカニズムであり、アプリケーションは既知のフィンガープリントを使用してHTTPS接続を検証します。CAを介した標準的な信頼チェーンとは異なり、ピンニングは、たとえ侵害された認証局でもドメインの偽造証明書を発行できないことを保証します。OWASP MSTG(2025)によると、Certificate PinningはL2保護レベルのアプリケーションに必須のコントロールリストに含まれています。実装には、コード内での証明書ハッシュの保存と、各リクエスト時の検証が含まれます。

重要なポイント

  • Certificate Pinning — アプリケーションが事前に既知のフィンガープリントを持つ証明書のみを信頼し、CAチェーン全体を無視する技術
  • Public Key Pinning — 公開鍵のみを固定する代替手段で、証明書変更時のローテーションを簡素化
  • HPKP(HTTP Public Key Pinning) — HTTPヘッダーレベルの非推奨標準、新規プロジェクトには非推奨
  • バックアップピン — メイン証明書の変更または期限切れ時に接続の継続性を確保する予備フィンガープリント
  • 実装 iOSではSecTrustEvaluate、AndroidではOkHttpのCertificatePinnerまたはTrustManagerを使用

Certificate Pinningとは?

Certificate Pinningは、アプリケーションが信頼する証明書のフィンガープリントを保存し、HTTPS接続を確立するための唯一の基準として使用するセキュリティ技術です。標準的なTLSモデルでは、クライアントはサーバーの証明書が信頼できるルートCAによって署名されていることを検証します — システムにプリインストールされた数百の認証局のいずれかです。Certificate Pinningはこのチェーンを直接検証に置き換えます。証明書は保存されたサンプルと一致するか、期待される公開鍵を含んでいなければなりません。

標準モデルの問題は、CA侵害インシデント — DigiNotar(2011)、Comodo(2011)、TrustCor(2022)— の後に明らかになりました。CAがドメインに対して偽造証明書を発行すると、ブラウザやアプリケーションはそれを有効として受け入れます。Certificate Pinningはこの攻撃を防止します。完全に署名された偽造証明書でも、そのフィンガープリントがアプリケーションに固定されたものと一致しないため、拒否されます。

ピンニングという用語は、pin(「ピン」または「固定具」)に由来します。開発者は信頼する証明書を固定し、それからの逸脱は接続をブロックします。Mitre CWE-295の調査によると、不適切な証明書検証はモバイルアプリケーションにおける最も危険なセキュリティエラーのトップ10の1つであり、Certificate Pinningはそれを防止する直接的な方法です。

Certificate Pinningの歴史と進化

当初、Certificate PinningはHPKP(HTTP Public Key Pinning)メカニズムを通じてブラウザで使用され、RFC 7469で標準化されました。開発者は期待されるキーのハッシュを含むHTTP Public-Key-Pinsヘッダーを送信し、ブラウザは指定された期間それらを保存しました。しかし、HPKPは危険であることが判明しました。設定ミスが1つあるだけでサイトを数ヶ月間ブロックする可能性がありました。2018年、ChromeはHPKPのサポートを終了し、現在の標準はクライアントサイド実装 — モバイルアプリケーションまたはブラウザ拡張機能内 — になりました。

Certificate Pinningの仕組み

Certificate Pinningのプロセスには、フィンガープリントの計算、接続の検証、エラー処理の3つの主要な段階が含まれます。準備段階で、開発者は本番サーバーの証明書または公開鍵のSHA-256ハッシュを取得します。GDPRおよびPCI DSS準拠のアプリケーションでは、チェーン内の中間CAのフィンガープリントも固定する必要があります。

HTTPSリクエストのたびに、アプリケーションはTLS認証コールバックをインターセプトし、サーバーの証明書を抽出してそのSHA-256ハッシュを計算します。このハッシュは、保存された信頼できるフィンガープリントのリストと比較されます。一致するものがあれば接続が続行されます。ない場合、アプリケーションは接続を終了し、攻撃者に実装の詳細を明かさずにエラーを報告する必要があります。

kotlin
fun validateCertificate(certificate: X509Certificate,
    expectedHash: String): Boolean {
    val digest = MessageDigest.getInstance("SHA-256")
    val hash = Base64.encodeToString(
        digest.digest(certificate.publicKey.getEncoded()),
        Base64.DEFAULT
    ).trim()
    return hash == expectedHash
}

この関数はサーバーからX509Certificateオブジェクトと期待されるハッシュを受け取ります。最初に証明書の公開鍵を抽出し、SHA-256ハッシュを計算してBase64でエンコードします。結果は期待されるフィンガープリントと比較されます。本番環境では、ローテーションをサポートするために2〜3個のフィンガープリントの配列に対する検証を追加する必要があります。

Certificate Pinning vs Public Key Pinning

ピンニングを実装する際、どの暗号オブジェクトを固定するかを選択する必要があります。Certificate PinningはX.509証明書自体 — そのシリアル番号、有効期間、チェーン全体 — にバインドします。Public Key Pinningは証明書内の公開鍵のみを固定し、他のフィールドを無視します。この選択は運用コストに大きく影響します。

基準Certificate PinningPublic Key Pinning
固定対象X.509証明書全体RSA/ECDSA公開鍵
ローテーション再発行のたびに更新が必要同じ鍵での証明書更新時に変更なし
セキュリティ最大精度のバインディング詳細にあまり影響されない
柔軟性低い — 証明書は1〜2年ごとに変更高い — 鍵は5〜10年使用可能
推奨更新が制御されたクリティカルシステム向けほとんどのモバイルアプリケーションとAPI向け

Public Key Pinningはほとんどのプロジェクトで推奨される選択肢です。証明書の再発行時、サーバーの公開鍵は通常変更されません — 会社は古い鍵を新しい証明書で署名するだけです。つまり、鍵ペアが変更されていなければ、証明書変更後にアプリケーションの更新は不要です。一方、Certificate Pinningは、開発者がサーバーとクライアントコードの両方を完全に制御するシナリオ(厳格な更新サイクルを持つエンタープライズアプリケーションなど)で推奨されます。

Trust On First Use(TOFU)

TOFUは、Certificate Pinningを事前に設定せず、サーバーへの最初の接続時に証明書を記憶する戦略です。このアプローチは、事前にどのサーバーに接続するかわからないアプリケーションに便利です。欠点は最初の攻撃に対する脆弱性です。最初の接続がインターセプトされると、偽造証明書が信頼できるものとして受け入れられます。TOFUはSSH接続や一部のP2Pプロトコルで使用されています。

iOSとAndroidでの実装

両方のプラットフォームで、Certificate PinningはネットワークスタックレベルでTLS接続をインターセプトして実装されます。iOSでは、URLSessionデリゲートまたはAlamofire ServerTrustManagerが使用されます。Androidでは、推奨される方法はOkHttp CertificatePinnerであり、これは人気のあるHTTPクライアントに組み込まれ、ドメインごとに複数のフィンガープリントを設定できます。

swift
func validate(serverTrust: SecTrust,
    pinnedHash: String) -> Bool {
    guard let certificates = SecTrustCopyCertificateChain(serverTrust)
        as? [SecCertificate] else { return false }

    for certificate in certificates {
        let data = SecCertificateCopyData(certificate)
        var hash = Data(repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
        data.withUnsafeBytes {
            CC_SHA256($0.baseAddress,
                CC_LONG(data.count), &hash)
        }
        if hash.base64EncodedString() == pinnedHash {
            return true
        }
    }
    return false
}

Swift関数では、serverTrustから証明書チェーンが抽出され、各証明書のSHA-256ハッシュが計算され、結果が期待値と比較されます。チェーン内のすべての証明書を反復処理することで、中間CAレベルでのピンニングが可能になります — 中間証明書が一致すれば接続が受け入れられます。これにより、リーフ証明書のローテーション時の柔軟性が提供されます。

Android向けカスタムTrustManager

アプリケーションがOkHttpを使用しない場合、カスタムX509TrustManagerを介してCertificate Pinningを実装できます。この方法はより多くのコードを必要としますが、検証プロセスを完全に制御できます。TrustManagerはcheckServerTrustedメソッドをオーバーライドし、開発者が手動でサーバー証明書を検証して信頼するかどうかを決定します。これはOkHttpライブラリが利用できない特定のシナリオでのみ推奨されます。

Certificate Pinning実装のよくある間違い

最も一般的な間違いはバックアップピンの欠如です。開発者が単一の証明書フィンガープリントのみを含め、それが期限切れになると、ユーザーは一斉に接続を失います。最小限の許容構成は2つのフィンガープリントです。現在の証明書とバックアップ用です。理想的には3つ:現在のもの、バックアップ、そしてフォールバックとしてルートCAのフィンガープリントです。

2つ目の間違いは、ピンをコードにプレーンテキストで保存することです。APKやIPAにアクセスできる攻撃者は、フィンガープリントを簡単に抽出して置き換えることができます。ハッシュの難読化が推奨されます。文字列を分割し、暗号化されたリソースに保存するか、実行時に計算します。Androidでは、文字列定数の難読化を備えたProGuardが効果的です。

3つ目の間違いは、開発用証明書レベルでのピンニングです。開発用と本番用の証明書は通常異なりますが、開発者はリリースビルド時にピンの切り替えを忘れることがよくあります。結果として、本番アプリケーションがサーバーに接続できなくなります。解決策は、BuildConfigまたはフレーバー固有のリソースを介して、デバッグとリリースで個別のピン設定を行うことです。

  • 証明書チェーンの無視 — 中間CAを考慮せずにリーフ証明書のみをチェックし、ローテーション時に接続が切断される
  • ハードコードされた日付 — 更新後に変更されない証明書の有効期限日がハードコードされている
  • 監視の欠如 — Certificate Pinningエラーに関するアラートがなく、問題がユーザーからしか発見されない
  • 検証なしのTOFU — 追加検証なしでTrust On First Useを使用し、最初のMITM攻撃で偽造証明書が固定される可能性がある

よくある質問

Certificate PinningとSSL Pinningの違いは?

SSL PinningはSSL/TLS証明書にバインドする一般的な用語です。Certificate Pinningは、公開鍵だけでなくX.509証明書自体を固定する具体的な実装です。違いはバインド対象にあります:証明書 vs 鍵。

アプリケーションで証明書フィンガープリントを安全に保存するには?

ProGuard(Android)による難読化を施したリソースへの保存、またはKeychain(iOS)による暗号化保存が推奨されます。暗号化なしでstrings.xmlやInfo.plistにプレーンテキストでピンを保存することは避けてください。

固定されたフィンガープリントはどのくらいの頻度で変更すべき?

サーバー上の証明書が変更されるたびに。現在の証明書の有効期限の3〜6ヶ月前に新しいフィンガープリントをバックアップピンとして追加し、ローテーション後に古いものを削除することを推奨します。少なくとも1つのバックアップピンが必須です。

デバッグ用にCertificate Pinningを無効にできますか?

はい、条件付きコンパイルによって可能です。デバッグビルドではピンニングを無効にし、リリースビルドでは有効にします。切り替えにはAndroidのBuildConfig.DEBUGまたはiOSの#if DEBUGを使用してください。ユーザーがアクセス可能なランタイムフラグでこれを行わないでください。

証明書が侵害された場合はどうすれば?

直ちに新しいフィンガープリントを含むアプリケーションアップデートをリリースし、ストアに公開してください。強制更新メカニズムを使用します。バックアップピンに予備CAのフィンガープリントが含まれている場合、異なる証明書を持つ別のドメインに一時的に切り替えることができます。

まとめ

  • Certificate Pinning — 偽造CAを介したMITM攻撃から保護するために信頼できる証明書またはその公開鍵を固定する
  • 2つのアプローチ — certificate pinning(厳格、証明書用)とpublic key pinning(柔軟、公開鍵用)
  • バックアップピン必須 — 証明書ローテーション時の継続性を確保するために最低2つのフィンガープリント
  • OkHttp CertificatePinner — 複数のピンをサポートするAndroidでの標準実装方法
  • URLSessionDelegate — 手動SecTrust検証とSHA-256ハッシュを使用するiOSでの主要な方法
  • よくある間違い — バックアップピンの欠如、難読化なしの保存、デバッグ/リリース設定の混同
  • 推奨 — ほとんどのプロジェクトではpublic key pinningを、クリティカルシステムにのみCertificate Pinningを使用

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください