AWSで発行した無料サーバー証明書を使って公開鍵ピンニングする時のメモです。
公開鍵ピンニング(HPKP)とは
アクセスしたWEBページが妥当なサーバ証明書を持っているか、不正に変更されていないかという確認を行うための仕組みです。2回目以降の接続時に、以前の接続の時にピン留めしたものとサーバ証明書が同じであるかを確認します。
逆に言えば間違った公開鍵情報をピンニングしてしまうと正しい証明書を持つサーバーにアクセスできなくなるので、挿入の際は慎重になる必要があります。
公開鍵ピンニングの実装
ACMで発行されるサーバーの証明書は比較的短期間で更新され、その時点で古い公開鍵をピンニングしていると上で述べたようにサーバーにアクセスできなくなります。そういった問題があるのでAWS公式でACMを利用して公開鍵ピンニングする場合は
- 自分で発行した証明書をACMにインポートする
- ピンニングするのは中間認証局以上のサーバ証明書にする
のいずれかの手段を取ることが推奨されています。
Troubleshoot Certificate Pinning Problems - AWS Certificate Manager
ACMで完結させたいので、今回は後者の手法で進めます。
中間認証局やルート認証局のサーバ証明書を取得する
AWSの中間認証局やルート認証局の証明書は以下のページで見ることができますが、どれが自分の持っている証明書の親に当たるのかわかりにくいかと思います。
Amazon Trust Services Repository
ということで特定の証明書の先祖を全て辿るopensslコマンドのオプションがあるので、今回はそれを用います。
$ echo | openssl s_client -connect <domain_name>:443 -showcerts
-showcerts
オプションで先祖を全て辿ってくれます。出力結果が
...
-----BEGIN CERTIFICATE-----
...
...
...
...
-----END CERTIFICATE-----
...
のようなブロックの繰り返しになるかと思います。下に行くにつれてルート認証局に近づきます。このBEGIN CERTIFICATE
とEND CERTIFICATE
で囲われた部分が公開鍵で、これを.pem
形式で保存します。
-----BEGIN CERTIFICATE-----
...
...
...
...
-----END CERTIFICATE-----
証明書をBase64エンコードする
最後にヘッダーを付与するときに使用します。先ほどのpemファイルに対して
$ openssl x509 -in <先ほどのpemファイル> -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
として得られた結果の文字列をそれぞれ記録します。
最後にヘッダーを記述する
PHPであれば以下のように書くだけです。
header('Public-Key-Pins: pin-sha256="<Base64エンコードされた中間認証局以上のサーバ証明書1>"; pin-sha256="<Base64エンコードされた中間認証局以上の証明書2>"; max-age=5184000; includeSubDomains; report-uri="<report_uri>"');
-
pin-sha256
Base64でエンコードされた証明書の公開鍵情報。保険で二つ書いておく。 -
max-age
で公開鍵をピン留めする期間を指定しする -
includeSubDomains
サブドメインに関してもピンニングを行うか -
report-uri
改ざんを報告する送信先
先ほども述べたとおり、公開鍵情報が間違っているとmax-age
で指定した期間サイトにアクセスできなくなるので注意が必要です。
保険として二つの公開鍵情報を書く、max-age
を短めにする、Public-Key-Pins
ではなくPublic-Key-Pins-Report-Only
ヘッダーを用いるといった予防策がとれます。ただHPKPの意味上max-age
は長い方が好ましいです。
参考
HTTP Public Key Pinning (HPKP) - Web セキュリティ | MDN
Troubleshoot Certificate Pinning Problems - AWS Certificate Manager