SAN(Subject Alternative Name)
概要
SAN(Subject Alternative Name) は、1 枚の SSL/TLS 証明書で複数のドメイン名や IP アドレスを保護するための X.509 v3 拡張フィールドです。RFC 5280(2008 年)で定義されています。
従来、証明書の対象ドメインは Subject フィールドの CommonName(CN)に 1 つだけ記載していました。しかし CN にはドメイン名を 1 つしか指定できないため、複数のドメインを保護するにはドメインごとに証明書を取得・管理する必要がありました。SAN はこの制約を解消します。1 枚の証明書に example.com、www.example.com、api.example.com といった複数のエントリを列挙でき、証明書の管理コストと運用負荷を削減できます。
現在の主要ブラウザーは CN ではなく SAN を優先して検証します。Chrome は 2017 年の Chrome 58 で CN のみの証明書を信頼しなくなりました。Firefox と Safari も同様の動作です。CA/Browser Forum の Baseline Requirements では、証明書に SAN を含めることが必須と規定されています。CN に正しいドメインが記載されていても、SAN が空であればブラウザーはその証明書を拒否します。
仕組み
SAN は X.509 v3 拡張フィールドとして証明書に格納され、ブラウザーは CN より SAN を優先して検証します。
SAN フィールドの構造
SAN は X.509 v3 の拡張領域に格納され、次の種別のエントリを含められます。
X509v3 Subject Alternative Name:
DNS:example.com
DNS:www.example.com
DNS:api.example.com
IP Address:203.0.113.10
IP Address:2001:db8::1
主に使われるのは DNS と IP Address の 2 種別です。仕様上は email(RFC 822 Name)や URI も定義されていますが、ウェブサーバーの証明書で使われることはほぼありません。
CN との関係
RFC 5280 では、SAN が存在する場合にはクライアントは CN を無視して SAN のみで検証するよう定めています。実際の動作は次のとおりです。
- ブラウザーがサーバーから証明書を受け取る
- 証明書に SAN が含まれていれば、SAN のエントリ一覧とアクセス先のホスト名を照合する
- SAN にアクセス先のホスト名と一致するエントリがあれば検証成功
- SAN がなければ接続を拒否する(CN のフォールバックは行わない)
Let’s Encrypt をはじめとする現代の CA は、発行する証明書の CN と SAN の両方に同じドメイン名を記載します。これは SAN 非対応の古いクライアントとの互換性を維持するためです。
ワイルドカードとの組み合わせ
SAN にはワイルドカードエントリも記載できます。
X509v3 Subject Alternative Name:
DNS:example.com
DNS:*.example.com
この構成では example.com 自体と、その直下のサブドメイン(www.example.com、api.example.com 等)の両方を 1 枚の証明書で保護できます。Let’s Encrypt で certbot -d example.com -d "*.example.com" と指定すると、この形式の証明書が発行されます。
ワイルドカードの * は 1 レベルのサブドメインにのみマッチします。*.example.com は a.b.example.com にはマッチしません。2 階層以上のサブドメインが必要な場合は、SAN に *.sub.example.com を追加するか、個別のホスト名を列挙します。
SAN のエントリ数の上限
RFC 5280 は SAN のエントリ数に上限を定めていません。ただし CA ごとに実務上の制限があります。
- Let’s Encrypt は 1 枚の証明書に最大 100 エントリを許可しています
- DigiCert や GlobalSign などの商用 CA は、プランにより 25〜250 エントリ程度に設定しています
- エントリ数が多すぎると証明書のサイズが大きくなり、TLS ハンドシェイクのデータ量が増加します
CDN やホスティングサービスでは、数十のドメインを 1 枚の SAN 証明書にまとめて管理することが一般的です。Cloudflare のユニバーサル SSL はこの方式を使っています。
設定例
certbot の -d オプションや OpenSSL の設定ファイルで SAN エントリを指定します。
Let’s Encrypt での SAN 証明書の取得
certbot で複数ドメインを SAN に含めた証明書を取得する例です。
certbot certonly --webroot -w /var/www/html \
-d example.com \
-d www.example.com \
-d api.example.com \
-d staging.example.com
-d オプションで指定したドメインがすべて SAN に記載されます。最初の -d が CN にも設定されます。
ワイルドカードと組み合わせる場合は DNS チャレンジを使います。
certbot certonly --manual --preferred-challenges dns \
-d example.com \
-d "*.example.com"
CSR(証明書署名要求)での SAN 指定
商用 CA に証明書を申請する場合、CSR に SAN を含めます。OpenSSL で SAN 付きの CSR を生成するには設定ファイルを使います。
# san.cnf
[req]
default_bits = 2048
prompt = no
distinguished_name = dn
req_extensions = v3_req
[dn]
CN = example.com
O = Example Inc.
[v3_req]
subjectAltName = DNS:example.com, DNS:www.example.com, DNS:api.example.com
openssl req -new -key private.key -out request.csr -config san.cnf
Nginx での SAN 証明書の設定
SAN 証明書は通常の証明書と同じ方法で Nginx に設定します。
server {
listen 443 ssl;
server_name example.com www.example.com api.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
SAN に含まれる全ドメインを server_name に列挙します。各ドメインへのリクエストは同一のサーバーブロックで処理され、1 枚の証明書で応答します。
確認方法
openssl で証明書の SAN を確認するには次のコマンドを使います。
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
出力例は次のとおりです。
X509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.com, DNS:api.example.com
SAN のエントリ数が多い証明書の場合、grep -A1 では表示しきれません。行数を増やすか -text の全出力を確認します。
# SAN の全エントリを表示
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -ext subjectAltName
CN と SAN の両方を確認するには次のコマンドが使えます。
# CN(Subject)の確認
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject
# SAN の確認
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -ext subjectAltName
外部の視点からも確認したい場合は、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 であれば、SAN の設定を含む TLS ハンドシェイクが正常に完了し、HTTPS 接続が確立されています。false の場合、error フィールドに接続失敗の原因が含まれます。
よくある問題
SAN に関するトラブルの大半は、証明書取得時のドメイン指定漏れや SAN 自体の未設定に起因します。
SAN にドメインが含まれていない
証明書を取得する際に -d オプションでドメインを指定し忘れると、そのドメインの SAN エントリが欠落します。ブラウザーは ERR_CERT_COMMON_NAME_INVALID(Chrome)や SSL_ERROR_BAD_CERT_DOMAIN(Firefox)を表示します。エラー名に “COMMON_NAME” とありますが、実際には SAN の不一致が原因です。
証明書を再発行して、必要なドメインをすべて -d オプションで指定します。
certbot certonly --webroot -w /var/www/html \
-d example.com \
-d www.example.com \
-d api.example.com
CN のみで SAN が空の証明書
自己署名証明書や古いツールで生成した証明書は、SAN を含まず CN のみにドメインを設定していることがあります。Chrome 58 以降はこのような証明書を信頼しないため、開発環境でも SAN を含めて生成する必要があります。
OpenSSL で自己署名証明書を作る場合は -addext オプションで SAN を追加します。
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
www あり / なし の両方でアクセスできない
example.com の証明書を取得したが www.example.com でアクセスするとエラーになるケースです。SAN に www.example.com が含まれていないことが原因です。certbot で取得する際に両方を指定します。
certbot certonly --webroot -w /var/www/html \
-d example.com \
-d www.example.com
IP アドレスでの HTTPS アクセス
SAN に IP Address:203.0.113.10 のようなエントリがない証明書では、IP アドレスでの HTTPS アクセス時にホスト名検証が失敗します。IP アドレスでの直接アクセスが必要な場合は、CSR の SAN に IP アドレスを含めます。ただし Let’s Encrypt は IP アドレスへの証明書発行に対応していないため、商用 CA を使います。
SAN 証明書の更新時にドメインが欠落する
certbot で証明書を更新する際、certbot renew は取得時のドメインリストを引き継ぎます。ただし certbot certonly で再取得すると、指定し忘れたドメインが SAN から消えます。certbot certificates コマンドで現在の証明書に含まれるドメインを事前に確認します。
certbot certificates
出力に含まれる Domains: の一覧が SAN のエントリに対応しています。再取得する場合はこの一覧のドメインをすべて -d で指定します。
SAN と SNI の違い
SAN と SNI(Server Name Indication)は名前が似ていますが、役割が異なります。
SAN は証明書の中に記載される情報です。「この証明書はどのドメインに対して有効か」を定義します。証明書の発行時に決まり、有効期限中は変わりません。
SNI は TLS ハンドシェイクのプロトコル拡張です。「クライアントがどのドメインに接続しようとしているか」をサーバーに通知します。サーバーは SNI の値に基づいて適切な証明書を選択し、その証明書の SAN にアクセス先ドメインが含まれているかを検証します。
1 つの IP アドレスに複数のドメインをホストしている場合、SNI でどの証明書を返すか決め、返した証明書の SAN でホスト名の一致を検証する、という 2 段階の処理が行われます。