TT Lab
はじめる
学ぶ 学習パス コース

Tomcat & nginxの運用

証明書障害の90%はチェーンと期限切れだ

TT Labで続きを見る

一言でいうと

証明書障害の大半は、暗号学ではなく、2つのことから生じます。中間証明書を付けていないチェーンと、誰も見ていなかった有効期限です。

なぜこれが問題なのか

この2つが危険な理由は、開発者のPCでは再現されない点です。ブラウザーは中間証明書をキャッシュしたり、自動的にダウンロードして補ってくれるため、チェーンが抜けていても、画面は問題なく表示されます。ところが、サーバー同士の連携(Javaクライアント、バッチ)は、そのような補正をしないため、そのまま失敗します。「私のブラウザーでは動くのですが」は、ここから出てきます。

期限切れは、もっと単純で、もっと痛いです。有効期間が過ぎた瞬間に全面障害で、明け方に始まり、対応はファイルの交換1つなのに、そのファイルを発行してもらうのに数日かかります。そのため、対応ではなく監視の問題です。残りの日数を数えるスクリプト1つとアラート1つで済む話を、毎年どこかで経験します。

SI現場で証明書に出会う瞬間

証明書は、普通このような形で届きます。顧客企業のセキュリティチームが、.pfxファイル1つとパスワードをメールで送ってきます。または、server.crt、server.key、chain.crtの3つのファイルが届きます。ときには、.cerが1つだけ届きます。そして「サービスインまでに適用をお願いします」と書かれています。

このとき必要な知識は、暗号学ではなく、ファイルが何を含んでいるかを確認する方法です。

# 인증서 내용 보기 (주체, 발급자, 유효기간, SAN)
openssl x509 -in server.crt -noout -subject -issuer -dates -ext subjectAltName

# 키와 인증서가 짝인지 확인 (두 해시가 같아야 함)
openssl x509 -in server.crt -noout -modulus | openssl md5
openssl rsa  -in server.key -noout -modulus | openssl md5

# pfx 를 crt/key 로 분해
openssl pkcs12 -in cert.pfx -clcerts -nokeys  -out server.crt
openssl pkcs12 -in cert.pfx -nocerts -nodes   -out server.key

このコードブロックの韓国語コメントは、順に、証明書の内容を見る(サブジェクト、発行者、有効期間、SAN)、鍵と証明書がペアかを確認する(2つのハッシュが同じである必要がある)、pfxをcrt/keyに分解する、という意味です。

鍵と証明書がペアでないと、nginxは起動すらせず、key values mismatchという短いメッセージだけを残します。このコマンド2行を知っていれば、30秒で終わります。

チェーン: 開発者のPCでは動くのに、サーバー同士では動かない理由

証明書は、普通3段です。

Root CA (브라우저·OS 에 이미 들어 있음)
   └─ Intermediate CA (중간 인증서)
        └─ 서버 인증서 (우리 것)

サーバーは自分の証明書+中間証明書を一緒に送る必要があります。Rootは相手が持っています。中間証明書を抜かすと、どうなるでしょうか。

そのため、症状は次のように現れます。「開発者PCのブラウザーでは問題なく動くのに、連携相手のシステムでだけSSLエラーが出ます。」これがチェーン欠落の典型的な姿です。確認は1行で済みます。

echo | openssl s_client -connect api.example.com:443 \
  -servername api.example.com -showcerts 2>/dev/null | grep -c 'BEGIN CERTIFICATE'

1が出たら、チェーンがありません。正常なら2以上が出ます。

nginxは、ssl_certificateに、サーバー証明書 → 中間証明書の順につなげたファイルを渡します。順序が逆だとだめです。

cat server.crt intermediate.crt > fullchain.pem

SANがないと、最近のブラウザーは拒否する

昔は、CN(Common Name)にドメインを書いていました。今は、SAN(Subject Alternative Name)拡張がないと、最新のブラウザーが証明書を拒否します。CNは参考用になりました。

自己署名証明書を作るときに、SANを書き忘れて「なぜ動かないのか」を繰り返すことが多いです。開発/検証環境用のプライベートCAを作るときは、必ずSANを入れます。

subjectAltName = DNS:labhub.local, DNS:*.labhub.local, IP:127.0.0.1

期限切れ: 最もよくあり、最もあきれる障害

実際の事例を1つ挙げると、大規模なSSO移行プロジェクトで、SAML署名証明書の期限切れで47分間の全面ログイン障害が起きたことがあります。証明書の期限切れは、

そのため、期限の監視は、人ではなくスクリプトが行う必要があります。

openssl x509 -in server.crt -noout -enddate
# notAfter=Nov 12 09:00:00 2026 GMT

# 임계일 이내면 실패로 종료 (checkend 는 초 단위)
openssl x509 -in server.crt -noout -checkend $((30*86400)) \
  || echo "30일 이내 만료"

-checkendはほとんど知られていませんが、監視スクリプトを作るとき、まさにこの用途です。サーバー証明書だけでなく、連携相手の証明書、クライアント証明書、SAML署名証明書、コード署名証明書まで、一覧で管理する必要があります。一覧がなければ、必ず1つ見落とします。

nginxのTLS設定の実務の基本値

server {
    listen 8443 ssl;
    server_name labhub.local;

    ssl_certificate     /etc/nginx/tls/fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/server.key;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers off;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
}

HTTP → HTTPSのリダイレクト

server {
    listen 8088;
    server_name labhub.local;
    return 301 https://$host:8443$request_uri;
}

rewriteの代わりにreturn 301を使うことが推奨されます。より速く、意図が明確です。そして、リダイレクトを設定したなら、HSTSも一緒に検討します。ただし、HSTSは一度ブラウザーに記憶されると、元に戻すのが難しいので、内部システムでは、max-ageを短く始めて、延ばしていくのが安全です。