「接続がプライベートではありません」:SSL 証明書の警告
· 読了目安 2 分
エラーの意味
ブラウザーはサーバーに接続し、サーバーは証明書を提示しましたが、ブラウザーはそれを信頼することを拒否しました。Chrome は、下にエラーコードを付けて「接続がプライベートではありません」、Firefox は「警告:潜在的なセキュリティリスクあり」、Safari は「この接続はプライベートではありません」と表示します。サイトがハッキングされたわけでも、お使いのパソコンがウイルスに感染したわけでもありません。サーバーが送信した証明書が、ブラウザーが行うチェックに合格しないのです。
エラーコードが、どのチェックに失敗したかを示します。
NET::ERR_CERT_DATE_INVALID:証明書が期限切れか、開始日が未来になっています(または訪問者の時計が間違っています)。NET::ERR_CERT_COMMON_NAME_INVALID:証明書が別の名前のものです。たとえば、www.example.com用で、example.com用ではありません。NET::ERR_CERT_AUTHORITY_INVALID:ブラウザーが、証明書を信頼できる認証局までたどれません。自己署名、不明な発行元、または中間証明書の欠落です。NET::ERR_CERT_REVOKED:証明書が、発行元によって失効されました。SSL_ERROR_BAD_CERT_DOMAINとSEC_ERROR_EXPIRED_CERTIFICATEは、名前と日付のエラーの、Firefox での表示です。
よくある原因
- 証明書が期限切れです。 Let's Encrypt の証明書の有効期間は 90 日で、自動更新が数週間にわたって失敗し、期限が切れる日まで誰も気づきませんでした。有料の証明書は 1 年で期限が切れ、更新のメールが古いアドレスに届きました。
- 更新したが、再読み込みしていません。 certbot が新しい証明書をディスクに書き込みましたが、再読み込みのフックがないため、ウェブサーバーはメモリ上の古い証明書を提示し続けています。
- 名前が間違っています。 証明書は
example.comをカバーしているのに、訪問者はwww.example.comに来る、または新しいサブドメインが証明書なしで追加されました。 - 中間証明書がありません。 サーバーが、チェーンではなく、自身の証明書だけを送っています。デスクトップのブラウザーは、キャッシュから補うことが多いですが、スマートフォンやアプリは失敗します。
- 時計の問題です。 訪問者のパソコンでは、日付が間違っていると、すべての証明書が期限切れ、またはまだ有効でないように見えます。サーバーでは、時計が間違っていると、更新が失敗します。
- 手前の CDN やプロキシが独自の証明書を持っており、 それが期限切れか、名前をカバーしていません。オリジンは問題ありません。
訪問者の場合
まず、パソコンの日付と時刻を確認してください。CMOS バッテリーが切れている、または時刻同期が無効になっていると、すべてのサイトが失敗します。次に、別のネットワークを試してください。警告が 1 つのサイトだけで、すべての端末に表示される場合は、そのサイトの証明書に問題があり、運営者が修復する必要があります。「サイトへ進む(安全ではありません)」をクリックして進むのは、知っているサイトなら問題ありませんが、ログインや支払いのあるものでは行わないでください。
ご自身のサイトの場合:まず確認
SSL チェッカーを実行してください。ブラウザーのキャッシュなしで、外部から、有効期限、証明書が有効な名前、チェーンが完全かどうかが表示されます。そのうえで、ご自身のサーバーで:
# サーバーは実際に何を送っているか?
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates -subject -ext subjectAltName
# ディスク上には何があるか?
sudo certbot certificates
# サーバーの時計は正しいか?
timedatectl
ディスク上の日付が、サーバーが送信する日付より新しい場合は、原因 2、つまり、更新したが再読み込みしていない状態です。
ご自身のサーバーでの対処法
1. certbot で更新する
sudo certbot renew
# または、renew が「まだ期限ではない」と言うのに、提示されている証明書が古い場合:
sudo certbot renew --force-renewal
sudo systemctl reload nginx # または apache2
更新に失敗する場合は、出力に理由が書かれています。よくある理由:ポート 80 がブロックされている、または certbot が想定しない場所にリダイレクトされている(HTTP-01 チャレンジには、/.well-known/acme-challenge/ に、通常の HTTP で到達できる必要があります)、DNS レコードが別のサーバーを指している、または多数の失敗の後にレート制限に達した。原因を修正し、sudo certbot renew --dry-run が成功するまで実行してください。
2. 再読み込みを自動化する
# Debian/Ubuntu:certbot がタイマーをインストールします。確認してください
systemctl list-timers | grep certbot
# ウェブサーバーが新しい証明書を読み込むよう、デプロイフックを追加する
printf '#!/bin/sh\nsystemctl reload nginx\n' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-web.sh
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-web.sh
3. 足りない名前を追加する
sudo certbot --nginx -d example.com -d www.example.com -d shop.example.com --expand
--expand フラグは、既存の証明書を、記載されたすべての名前をカバーするものに置き換えます。
4. チェーンを修正する
certbot では、ウェブサーバーを cert.pem ではなく、必ず fullchain.pem に向けてください。
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
Apache:SSLCertificateFile に fullchain.pem。購入した証明書では、発行元から中間証明書(「CA bundle」)をダウンロードして、証明書ファイルに追記します。sudo nginx -t(Apache:sudo apachectl configtest)でテストし、再読み込みしてから、SSL チェッカーでチェーンを確認してください。
5. 時計を修正する
sudo timedatectl set-ntp true
timedatectl
時計が数分以上ずれているサーバーは、証明書の検証が双方向で失敗します。
6. 共有ホスティングまたはサイトビルダー
ほとんどのホスティング会社は、コントロールパネル(「SSL/TLS」「セキュリティ」「Let's Encrypt」)から、Let's Encrypt の証明書を発行・更新します。それを開き、証明書が example.com と www.example.com の両方をカバーしていることを確認して、更新または再発行をクリックしてください。パネルでは証明書が有効と表示されるのに、ブラウザーでは違う場合は、ホスティング会社側で、再読み込みまたはチェーンの問題が起きています。
ホスティング会社の問題の場合
お世話になっております。06:00 UTC 頃から、ウェブサイト example.com で、ブラウザーに「接続がプライベートではありません」(NET::ERR_CERT_DATE_INVALID)と表示されます。管理画面では有効な証明書と表示されるため、ウェブサーバーが古い証明書を提示しているようです。サーバーで証明書を再読み込みし、ドメインの自動更新を確認していただけますでしょうか。
次回に備えて
期限切れの証明書は、日付がわかっている唯一の障害です。SSL 証明書の監視は、期限の前に警告し、チェーンや名前が間違っている場合にも再度警告します。5 件のチェックは無料です。ご自身のサーバーでは、certbot が設定されていれば、Quick Fix が定額で証明書を更新してウェブサーバーを再読み込みします。更新の仕組みがまだない場合は、Deep Fix が設定します。