セキュリティ PR

証明書ピンニングのやり方とその重要性

記事内に商品プロモーションを含む場合があります

証明書ピンニングとは何か?

証明書ピンニング(Certificate Pinning)とは、特定のSSL/TLS証明書や公開鍵をアプリケーション側で固定し、信頼できるサーバーとだけ安全に通信を行うための技術です。これにより、中間者攻撃(MITM攻撃)を防ぎ、偽の証明書を使ったなりすましを防ぐことができます。

通常、TLS/SSL通信では、認証局(CA)によって発行された証明書をサーバーが提示し、クライアント(ブラウザやアプリ)がその証明書を信頼できるかどうかを確認します。しかし、万が一、悪意のある第三者が不正な証明書を取得してしまった場合、ユーザーは攻撃者のサーバーと通信してしまう可能性があります。

証明書ピンニングは、このリスクを低減するために、あらかじめ特定の証明書または公開鍵をアプリケーションに組み込んでおき、それ以外の証明書を拒否する仕組みです。


証明書ピンニングの仕組み

証明書ピンニングの基本的な仕組みは以下のようになっています。

  1. 公開鍵または証明書をアプリに埋め込む
    • アプリ開発時に、特定のサーバー証明書の公開鍵または証明書自体をアプリのコードにハードコードしておく。
  2. サーバーとの通信時に証明書をチェック
    • アプリがサーバーに接続すると、サーバーはTLSハンドシェイクの際に証明書を提示する。
    • アプリは提示された証明書の公開鍵やハッシュ値を、事前に埋め込まれた情報と比較する。
  3. 一致すれば接続を継続、不一致なら通信を遮断
    • 一致しない場合、アプリはそのサーバーとの通信を拒否することで、不正な中間者攻撃を防ぐ。

証明書ピンニングの実装方法(やり方)

証明書ピンニングを実装する方法はいくつかありますが、代表的な手法を紹介します。

Androidアプリでの証明書ピンニング

Androidでは、Network Security Configを使用して証明書ピンニングを実装できます。

手順

1. res/xml/network_security_config.xml を作成し、ピンニングする証明書情報を記述します。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">example.com</domain>
        <pin-set expiration="2025-01-01">
            <pin digest="SHA-256">base64_encoded_hash_of_public_key</pin>
        </pin-set>
    </domain-config>
</network-security-config>

2. AndroidManifest.xmlnetworkSecurityConfig を指定します。

<application
    android:networkSecurityConfig="@xml/network_security_config">
</application>

これにより、指定された公開鍵のハッシュが一致しない証明書を持つサーバーへの通信がブロックされます。


iOSアプリでの証明書ピンニング

iOSでは、NSURLSessionURLSessionDelegate を使用してピンニングを実装できます。

手順

class PinningURLSessionDelegate: NSObject, URLSessionDelegate {
    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if let serverTrust = challenge.protectionSpace.serverTrust {
            let cert = SecTrustGetCertificateAtIndex(serverTrust, 0)
            let certData = SecCertificateCopyData(cert!) as Data
            let localCertData = NSDataAsset(name: "server_cert")!.data

            if certData == localCertData {
                completionHandler(.useCredential, URLCredential(trust: serverTrust))
                return
            }
        }
        completionHandler(.cancelAuthenticationChallenge, nil)
    }
}

ここでは、アプリに埋め込んだ証明書(server_cert)と、サーバーから受信した証明書を比較し、一致しない場合は接続を拒否します。


Webサーバーでの公開鍵ピンニング(HPKP)

かつては、HPKP(HTTP Public Key Pinning)というHTTPヘッダーを使用したピンニングもありましたが、設定ミスのリスクが高いため、現在は推奨されていません。

Public-Key-Pins: pin-sha256="base64_encoded_hash"; max-age=5184000; includeSubDomains

現在では、代わりに 証明書透明性(Certificate Transparency, CT) や、HSTS(HTTP Strict Transport Security)を組み合わせることが一般的です。


証明書ピンニングの注意点(デメリット)

証明書ピンニングは強力なセキュリティ対策ですが、適用にはいくつかの注意点があります。

  1. 証明書の更新が困難
    • ピンニングした証明書が期限切れになったり、更新されるとアプリの通信がすべて失敗する可能性があります。
    • これを防ぐために、複数のバックアップ鍵をピンニングしておくのが望ましい。
  2. 緊急対応が難しい
    • 万が一、証明書が危殆化(漏洩)した場合、アプリのアップデートなしに修正する手段がない。
    • そのため、証明書の失効や緊急交換が必要な場合は、バックアップの公開鍵を用意する。
  3. 運用コストが増加
    • ピンニングを導入することで、開発と運用のコストが増えるため、適用する範囲を慎重に決める必要がある。

まとめ

証明書ピンニングは、サーバー証明書を特定のものに固定し、中間者攻撃やなりすましを防ぐ強力な手法です。しかし、証明書の更新や管理が難しくなるため、慎重に運用する必要があります。

推奨する対策:

  • 証明書透明性(CT)とHSTSを併用
  • 複数のバックアップ鍵を登録
  • 定期的な証明書更新の計画を立てる
  • 証明書ピンニングの必要性を十分に検討