🔥

SSL証明書エラーの診断と修正

に公開

はじめに

WebサイトをHTTPSで運用していると、ブラウザで「この接続ではプライバシーが保護されません」という警告と共に ERR_CERT_DATE_INVALIDNET::ERR_CERT_COMMON_NAME_INVALID といったエラーに遭遇することがある。これらはSSL/TLS証明書の設定不備が原因で発生する代表的なエラーである。

この記事は、これらのエラーの原因を特定し、解決するための診断・修正手順をまとめた備忘録である。NginxやApacheをWebサーバーとして利用し、Let's Encryptで証明書を取得・管理している環境を主に想定している。

対象環境

この記事で紹介する手順は、以下の環境で確認している。コマンドや設定ファイルのパスは、この環境に基づいている。

エラーの初期診断

具体的なエラーの対処に入る前に、まずは証明書の状態を客観的に診断することが重要である。

1. ブラウザの開発者ツールで確認する

ChromeやFirefoxの開発者ツールには、証明書の詳細情報を確認する機能がある。
Chromeの場合、「Security」タブを開くと、証明書の有効期限、発行先(コモンネーム)、発行者などの情報を視覚的に確認できる。エラーの原因を把握するための最初のステップとして有用である。

2. openssl コマンドで確認する

サーバー側からコマンドラインで証明書の詳細を確認するには openssl コマンドが利用できる。example.com の部分は対象のドメイン名に置き換える。

ターミナル
openssl s_client -connect example.com:443 -servername example.com

このコマンドは、サーバーから返される証明書チェーン全体や、接続に使用されるプロトコル、暗号スイートなど、詳細な情報を出力する。

3. オンラインSSLチェッカーを利用する

Qualys SSL Labs' SSL Server Test のようなオンラインサービスを利用すると、外部からサーバーのSSL/TLS設定を包括的に診断できる。証明書チェーンの問題や、古いプロトコルのサポート状況など、自分では気づきにくい設定不備を発見できる場合がある。

原因と解決策

ここでは、代表的なエラーコードごとに原因と解決策を解説する。

ERR_CERT_DATE_INVALID: 証明書の有効期限切れ

このエラーは、ブラウザがサイトにアクセスした日時が、サーバーから提示された証明書の有効期間(Not BeforeNot After の間)に含まれていない場合に発生する。

原因

  • 証明書の有効期限が切れている: Let's Encryptの証明書は有効期間が90日と短いため、自動更新に失敗するとこのエラーが発生しやすい。
  • クライアントのシステム時刻がずれている: ユーザーのPCの時計が大幅に未来または過去に設定されている場合にも発生しうるが、これはサーバー側の問題ではない。

解決策

Step 1: 証明書の有効期限を確認する

openssl コマンドで証明書の有効期間(notBefore, notAfter)を確認する。

ターミナル
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

出力例:

notBefore=May 20 09:25:31 2024 GMT
notAfter=Aug 18 09:25:30 2024 GMT

notAfter の日付が現在時刻より過去になっていれば、証明書は失効している。

Certbotを利用している場合は、以下のコマンドでも管理下の証明書一覧とその有効期限を確認できる。

ターミナル
sudo certbot certificates
Step 2: 証明書を更新する

Certbotを利用している場合、以下のコマンドで証明書の更新を試みる。--dry-run オプションで、まずは更新処理が成功するかどうかをテストするのが安全である。

ターミナル
# 更新のテスト実行
sudo certbot renew --dry-run

# テストが成功したら、本番の更新を実行
sudo certbot renew
Step 3: Webサーバーをリロードする

証明書ファイルを更新しただけでは、Webサーバーは古い証明書をメモリ上に保持したままである。設定を再読み込みさせるために、Webサーバーをリロードする。

Nginxの場合:

ターミナル
sudo systemctl reload nginx

Apacheの場合:

ターミナル
sudo systemctl reload apache2
Step 4: 自動更新の設定を確認する

Certbotはインストール時に、証明書の自動更新を行うためのsystemdタイマーやcronジョブを自動で設定する。これが正しく動作しているか確認する。

systemdタイマーの確認:

ターミナル
sudo systemctl list-timers | grep certbot

certbot.timer が有効になっていれば、定期的に自動更新が試みられる。もし無効になっている、あるいは存在しない場合は、Certbotのドキュメントに従って再設定が必要である。

NET::ERR_CERT_COMMON_NAME_INVALID: ドメイン名の不一致

このエラーは、ブラウザがアクセスしているURLのドメイン名と、サーバーから提示された証明書に記載されているドメイン名(コモンネーム: CN や サブジェクト代替名: SAN)が一致しない場合に発生する。

原因

  • 証明書が正しいドメイン名を含んでいない: 例えば、www.example.com でアクセスしているのに、証明書が example.com にしか対応していない。
  • Webサーバーの設定ミス: 複数のサイトを運営しているサーバーで、アクセスされたドメイン名に対して誤った証明書を提示するように設定されている。

解決策

Step 1: 証明書に含まれるドメイン名を確認する

openssl コマンドで、証明書のSubject(CN)とSubject Alternative Name(SAN)を確認する。

ターミナル
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text | grep -E 'Subject:|DNS:'

出力例:

Subject: CN = example.com
            X509v3 Subject Alternative Name: 
                DNS:example.com, DNS:www.example.com

この DNS: のリストに、アクセスしようとしているドメイン名が全て含まれているか確認する。

Step 2: Webサーバーの設定を確認する

Webサーバーの設定ファイルで、server_name(Nginx)や ServerName/ServerAlias(Apache)ディレクティブと、読み込んでいる証明書ファイル(ssl_certificateSSLCertificateFile)が対応しているか確認する。

Nginxの場合 (/etc/nginx/sites-available/example.com.conf など):

nginx.conf
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name example.com www.example.com; # アクセスするドメイン名と一致しているか

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # 正しい証明書パスか
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # 正しい秘密鍵パスか

    # ...
}

Apacheの場合 (/etc/apache2/sites-available/example.com-ssl.conf など):

apache.conf
<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com # アクセスするドメイン名と一致しているか

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem # 正しい証明書パスか
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem # 正しい秘密鍵パスか

    # ...
</VirtualHost>
Step 3: 証明書を再発行する

証明書に必要なドメイン名が含まれていない場合は、ドメイン名を追加して証明書を再発行する。Certbotでは -d オプションで複数のドメインを指定できる。

ターミナル
# 既存の証明書にドメインを追加して更新する
sudo certbot --nginx --cert-name example.com -d example.com -d www.example.com -d sub.example.com

その他のエラー: ERR_CERT_AUTHORITY_INVALID

これは中間証明書が正しく設定されていない場合に発生する、信頼されていない発行局からの証明書として扱われるエラーである。

原因

  • 中間証明書がサーバーから送信されていない: Webサーバーの設定で、サーバー証明書(cert.pem)のみを指定しており、中間証明書を含む証明書チェーンファイル(fullchain.pem)を指定していない。

解決策

Webサーバーの設定ファイルで、証明書ファイルとして fullchain.pem を指定する。これはサーバー証明書と中間証明書を連結したファイルである。

Nginxの場合:

nginx.conf
# 誤り: ssl_certificate /etc/letsencrypt/live/example.com/cert.pem;
# 正解
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

Apacheの場合:

apache.conf
# 誤り: SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem;
# 正解
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem;

# 中間証明書を別途指定するSSLCertificateChainFileディレクティブは古いApacheで必要だったが、
# 最近のバージョンではfullchain.pemをSSLCertificateFileに指定するのが一般的である。

おわりに

SSL/TLS証明書のエラーは、一見すると複雑に見えるが、エラーメッセージを手がかりに、証明書の内容とWebサーバーの設定を一つずつ確認していくことで、原因を特定できる場合がほとんどである。特にLet's Encryptを利用している場合は、「有効期限」「対象ドメイン」「自動更新設定」の3点が主なチェックポイントとなる。

この記事が、同様の問題に直面した開発者やサーバー管理者の一助となれば幸いである。

参考リンク

Discussion