なんで普通のTLS検証では不十分なの?
通常のTLS検証はOSやブラウザが持つ認証局リストを信頼する。でもCAが不正に証明書を発行したり、端末にルートCA証明書を追加されたりすると偽の証明書でも検証を通過してしまう。証明書ピンニングは「このサーバーはこの証明書しか使わない」と明示するから、偽CAの証明書を排除できるんだよ
どうやって実装するの?
①サーバー証明書の公開鍵のSHA-256ハッシュを計算、②アプリに埋め込む、③TLS接続時にサーバーから受け取った証明書のハッシュとピン留めした値を比較、④一致しなければ接続拒否。iOSならTrustKit、AndroidならOkHttpのCertificatePinnerが使えるよ
運用の課題は?
証明書はLet's Encryptなら90日、商用CAなら1年で更新が必要。ピン留めした証明書が変わるとアプリが通信できなくなる。対策は①バックアップピンを含める(現在の証明書+次の証明書)、②中間CA証明書にピンニングする(エンドエンティティより変更頻度が低い)、③アプリの強制アップデートの仕組みを用意する
最近はピンニングしない方がいいの?
実はAppleがATSでピンニングの代替を推奨し、ChromeもHTTP Public Key Pinning(HPKP)を廃止した。運用ミスで大規模障害を起こした事例が相次いだためだよ。代わりにCertificate Transparency(CT)ログで不正な証明書発行を監視する方向に移行している。金融アプリなど高セキュリティ要件では今でもピンニングは有効だけどね