CT ログ(Certificate Transparency Log)
概要
CT ログ(Certificate Transparency Log) は、認証局(CA)が発行した SSL/TLS 証明書を追記専用のデータ構造で記録するパブリックなログサーバーです。RFC 6962(2013 年)で最初に定義され、現行仕様は RFC 9162(2021 年)です。
CT ログは Certificate Transparency(証明書透明性)の中核を担うインフラです。CA が証明書を発行する際、CT ログに証明書を登録し、ログサーバーから SCT(Signed Certificate Timestamp)を受け取ります。SCT は証明書に埋め込まれ、ブラウザーが CT 準拠を確認する際に使われます。
2018 年 4 月 30 日以降、Chrome はすべての新規証明書に CT ログへの登録を要求しています。CT ログに記録されていない証明書は NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED エラーで拒否されます。
現在、Google、Cloudflare、DigiCert、Let’s Encrypt、Sectigo など複数の組織が CT ログを運用しています。
仕組み
CT ログは Merkle ハッシュツリーで改ざん耐性を確保し、SCT の発行とブラウザーポリシーによってログ登録を強制します。
Merkle ハッシュツリー
CT ログは Merkle ハッシュツリーで管理されています。各証明書はツリーのリーフノードとして追加されます。Merkle ツリーの特性により、既存のエントリーを改ざんするとルートハッシュが変化するため、不正な変更は即座に検出できます。
ログサーバーは定期的に署名付きのツリーヘッド(Signed Tree Head、STH)を公開します。STH にはツリーのサイズとルートハッシュが含まれており、前回の STH と比較することでログの一貫性(consistency)を検証できます。
ログのライフサイクル
CT ログにはライフサイクルがあります。RFC 9162 では次の状態が定義されています。
- Usable — 新しい証明書の登録を受け付けている状態。CA は Usable 状態のログに証明書を送信する
- Read-only — 新規登録を停止し、既存エントリーの読み取りのみ可能な状態
- Retired — 運用を終了した状態。ログデータはアーカイブとして保持される場合がある
Google の CT ポリシーでは、SCT に使用できるのは Google が「Usable」と認定したログのみです。ログが Retired になると、そのログの SCT は Chrome の CT ポリシーの要件にカウントされなくなります。
SCT の発行と検証
CA が CT ログに証明書を登録する際の処理は次のとおりです。
- CA がプリ証明書(precertificate)をログサーバーに送信する
- ログサーバーが証明書を受理し、ログの秘密鍵で署名した SCT を返す。SCT にはタイムスタンプとログ ID が含まれる
- CA が SCT を証明書の X.509v3 拡張に埋め込んで最終的な証明書を発行する
SCT には「最大マージ遅延(MMD)」の概念があります。ログサーバーは SCT を発行した時点では、証明書をツリーに組み込んでいない場合があります。MMD は RFC 6962 で 24 時間以内と定められており、ログサーバーはこの期間内にツリーへの組み込みを完了する義務があります。
ブラウザーの CT ポリシー
Chrome の CT ポリシーでは、証明書の有効期間に応じて必要な SCT の数が異なります。
- 有効期間 180 日未満: SCT 2 つ(異なるログから)
- 有効期間 180 日以上: SCT 3 つ(異なるログから)
SCT は異なるログオペレーターのログから取得する必要があります。同じオペレーターが運営する複数のログからの SCT は 1 つとしてカウントされます。
確認方法
openssl で証明書に含まれる SCT を確認するには次のコマンドを使います。
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -text | grep -A 30 "CT Precertificate SCTs"
CT Precertificate SCTs:
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : 3B:53:77:75:3E:2D:B9:80:4E:8B:30:5B:06:FE:40:3B:
67:D8:4F:C3:F4:C7:BD:00:0D:2D:72:6F:E1:FA:D4:17
Timestamp : Apr 11 00:00:00.000 2026 GMT
Signature : ecdsa-with-SHA256
SCT の数を確認するには次のコマンドが便利です。
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -text | grep -c "Signed Certificate Timestamp"
出力が 2 以上であれば Chrome の CT ポリシーの最低要件を満たしています。有効期間 180 日以上の証明書では 3 以上必要です。
crt.sh を使うと、特定のドメインに発行されたすべての証明書を CT ログから検索できます。
curl "https://crt.sh/?q=%.example.com&output=json" | jq '.[0:5] | .[].name_value'
外部の視点からも確認したい場合は、Labee Dev Toolbox の SSL Cert API を使うと、外部の視点から見た結果を取得できます。
curl "https://labee.dev/api/ssl-cert?hostname=example.com"
{
"success": true,
"data": {
"hostname": "example.com",
"port": 443,
"reachable": true,
"status": 200
},
"error": null,
"meta": { "responseTime": 123 }
}
data.reachable が true であれば HTTPS 接続が確立されており、証明書が有効です。CT ポリシーに違反した証明書は TLS ハンドシェイクの段階でブラウザーに拒否されます。
よくある問題
CT ログに関連するトラブルは、SCT の不足やログの状態変更、情報漏洩リスクに集約されます。
SCT が不足して Chrome でエラーになる
NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED エラーは、証明書に有効な SCT が含まれていないか数が不足している場合に発生します。Let’s Encrypt など主要な CA は発行時に自動的に複数の CT ログに登録するため、通常は問題になりません。社内 CA や独自の CA を運用している場合、CT ログへの登録が行われていないことが原因です。
CT ログの Retired による SCT の無効化
CT ログが Retired 状態になると、そのログから発行された SCT は Chrome の CT ポリシーのカウント対象外になります。証明書に含まれる SCT のうち有効なものが不足すると、証明書の再発行が必要です。Let’s Encrypt などの CA は複数の CT ログに冗長登録しているため、1 つのログが Retired になっても他の SCT でポリシーを満たせます。
crt.sh で証明書がすぐに表示されない
証明書を発行した直後は、CT ログへの組み込みが MMD(最大 24 時間)以内で行われます。crt.sh は CT ログをアグリゲートして表示するサービスのため、ログへの組み込みとインデックス更新のタイミングにより、発行直後は検索結果に表示されないことがあります。
CT ログによる情報漏洩
CT ログはパブリックであるため、証明書に含まれるドメイン名はすべて公開されます。内部向けサブドメイン(internal.example.com、staging.example.com など)の存在が外部に知られたくない場合、ワイルドカード証明書を使うことで個別のサブドメイン名の漏洩を防げます。ただし、ワイルドカード証明書自体の存在は CT ログに記録されます。