ブラウザに表示されるSSL証明書が、サーバーが返しているものと違った話

自社サイトのSSL証明書を確認していたら、ブラウザに表示される発行元と、コマンドラインから確認した発行元がまったく違うという不思議な現象に遭遇しました。

最終的には端末にインストールしていたセキュリティソフトが原因と判明したのですが、切り分けの過程が学びになったので、調査の流れをまとめておきます。

環境

  • 運用サイト: エックスサーバー上で稼働するコーポレートサイト(無料SSLを利用)
  • 一部コンテンツ: 別サーバー(外部CMSサービス)からリバースプロキシで取得
  • クライアント: MacBook Air(macOS)、ブラウザは Chrome

発端: 「無料SSLのはずなのに発行元が Norton?」

ブラウザのアドレスバーから証明書ビューアを開いて確認したところ、こんな表示が出ました。

発行先:
  一般名(CN): example.co.jp
発行元:
  一般名(CN): Norton trusted CA
  組織(O)  : NORTON
  組織単位(OU): Software Development
有効期間: 約90日

エックスサーバーの無料SSL(Let’s Encrypt)を有効化しているはずなのに、発行元が Norton trusted CA と表示されている。

「いつの間に切り替わったんだろう?」と最初は混乱しました。

切り分け1: サーバーが実際に返している証明書を確認する

ブラウザ表示を疑って、コマンドラインから直接確認します。

# DNSがどこを向いているか
dig www.example.co.jp +short
# → エックスサーバーのIPアドレスを返す

# サーバーが提示している証明書
echo | openssl s_client -servername www.example.co.jp \
  -connect www.example.co.jp:443 2>/dev/null \
  | openssl x509 -noout -issuer -subject -dates

結果は次のとおり。

issuer=C=US, O=Let's Encrypt, CN=R13
subject=CN=example.co.jp
notBefore=Mar 24 01:26:12 2026 GMT
notAfter=Jun 22 01:26:11 2026 GMT

Let’s Encrypt。サーバー側はちゃんとエックスサーバーの無料SSLを返しています。

つまり、

  • サーバーが返している証明書 → Let’s Encrypt(正しい)
  • ブラウザが表示している証明書 → Norton trusted CA(おかしい)

という不一致が起きていることが判明しました。

切り分け2: ネットワーク経路の問題ではないか

ブラウザだけが違う証明書を見ているということは、

  1. DNS が経路によって違うサーバーを返している(ラウンドロビン等)
  2. 経路上に SSL を再暗号化する何かがいる
  3. ブラウザ側のキャッシュ

このあたりが疑わしいので、それぞれ潰していきます。

DNS は安定して同じIPを返している

# 複数のDNSサーバーで確認
nslookup www.example.co.jp 8.8.8.8
nslookup www.example.co.jp 1.1.1.1

両方とも同じIPアドレス。世界中どこから見ても同じ宛先を向いているはず。

curl でも証明書は Let’s Encrypt

curl -v --http2 https://www.example.co.jp/path/to/image.jpg -o /dev/null 2>&1 \
  | grep -E "(subject|issuer|HTTP)"
*  subject: CN=example.co.jp
*  issuer: C=US; O=Let's Encrypt; CN=R13
< HTTP/2 200 

ブラウザと同じHTTP/2でアクセスしても、Let’s Encrypt が返ってくる。

ブラウザのキャッシュではない

シークレットウィンドウで開いても、表示される証明書は Norton のまま。Chrome のキャッシュは関係なさそうです。

切り分け3: 端末側に何かいるのでは?

ここまでで「サーバー側は完全に正常」「ブラウザだけが Norton 証明書を見ている」ことが確定したので、端末側を疑います。

macOS のキーチェーンを確認

security find-certificate -a -c "example.co.jp" -p \
  | openssl x509 -noout -issuer -subject -dates 2>/dev/null

security find-certificate -a -p /Library/Keychains/System.keychain \
  | grep -B 1 -A 5 "example"

→ 結果なし。キーチェーンには登録されていない。

セキュリティソフトのプロセスを確認

ps aux | grep -iE "(norton|symantec|eset|trend|mcafee|sophos|zscaler)" | grep -v grep

すると、Norton関連のプロセスがズラっと出てきました。

/Applications/Norton.app/Contents/Backend/services/com.norton.proxy
/Library/SystemExtensions/.../com.norton.mes.networkextension.systemextension
/Applications/Norton.app/Contents/Backend/services/com.norton.filter
(他多数)

ここで決定的だったのは com.norton.proxycom.norton.mes.networkextension の存在。これらは Norton が HTTPS 通信を中間者として傍受するためのコンポーネントです。

原因確定: Norton のHTTPSインスペクション機能

仕組みはこうなっていました。

ブラウザ → Norton(傍受)→ 本物のサーバー(example.co.jp)
                ↓
           Norton が独自の証明書で再暗号化
                ↓
ブラウザに見える証明書 = Norton trusted CA 発行

セキュリティソフトの「Webサイトスキャン」「HTTPSスキャン」「Safe Web」などと呼ばれる機能は、マルウェアや危険なサイトをチェックするために、HTTPS通信を一度復号して中身を検査し、再暗号化してブラウザに渡します。このとき、再暗号化に使われる証明書がセキュリティソフト独自のもの(Nortonの場合は「Norton trusted CA」)になります。

curl や openssl で確認したときに Let’s Encrypt が見えていたのは、これらのコマンドラインツールが Norton の傍受経路を経由しなかったためと推測されます(ブラウザだけが対象になっている、もしくは別の実装上の理由がある)。

公開鍵が他の証明書と一致していた件

調査の途中、ブラウザに表示された証明書の公開鍵フィンガープリントが、まったく別のドメイン用の証明書(リバプロ先サービスの *.example-cms.biz 証明書)と一致していて、これも混乱の元でした。

これは、Norton が中間者として動的に証明書を発行する際、同じ秘密鍵を使い回しているためです。つまり、

  • ブラウザがアクセスするどのドメインに対しても
  • Norton は同じ鍵ペアで再署名した証明書を返す

ので、複数の証明書で公開鍵が一致するように見えていた、というわけです。

検証: Norton の機能を一時的に止めて確認

最終確認として、Norton の「Webサイトスキャン」を一時的にオフにして、シークレットウィンドウで同じURLを開きました。

発行先:
  一般名(CN): example.co.jp
発行元:
  一般名(CN): R13
  組織(O)  : Let's Encrypt
有効期間: 約90日

無事 Let’s Encrypt の表示になりました。証明書のフィンガープリントも、openssl で取得した値と完全に一致。

仮説どおり、ブラウザに見えていたのは Norton が中間者として再発行した証明書、というのが確定しました。

まとめ

起きていたこと

  • サーバーは正しく Let’s Encrypt の証明書を返していた
  • 端末にインストールされたセキュリティソフト(Norton)が HTTPS 通信を傍受し、独自の証明書で再暗号化していた
  • ブラウザに見えていたのは Norton が再発行した証明書だった

学び

  • ブラウザに表示される証明書 = サーバーが返している証明書とは限らない。間にセキュリティソフトや企業プロキシ、CDN などが入っていると、ブラウザが見ているのは別物。
  • SSL 証明書の検証は、ブラウザだけでなく openssl s_clientcurl -v などで複数経路から確認すべき。コマンドラインツールはセキュリティソフトの傍受経路を通らないことが多く、サーバーの「本物」の証明書を確認できる。
  • 証明書ビューアの「公開鍵」フィンガープリントは、同じセキュリティソフトが中間者として動的発行している場合、複数ドメインで一致することがある。これは原因特定の手がかりになる。
  • 企業向けのHTTPSインスペクションも仕組みは同じ。ZScaler や Netskope、社内プロキシ等が入っている環境では、ブラウザに見える証明書は社内CAが発行したものになる。

確認に使ったコマンドまとめ

サーバーが実際に返す証明書を確認:

echo | openssl s_client -servername example.co.jp \
  -connect example.co.jp:443 2>/dev/null \
  | openssl x509 -noout -issuer -subject -dates -fingerprint -sha256

curl でHTTPS接続の詳細を確認:

curl -v --http2 https://example.co.jp/ -o /dev/null 2>&1 \
  | grep -E "(Server certificate|subject|issuer|HTTP)"

macOSのキーチェーンに該当ドメインの証明書が登録されていないか:

security find-certificate -a -c "example.co.jp" -p \
  | openssl x509 -noout -issuer -subject -dates 2>/dev/null

セキュリティソフトが中間者として動いていないか:

ps aux | grep -iE "(norton|symantec|eset|trend|mcafee|sophos|zscaler|netskope)" \
  | grep -v grep

おわりに

「自社サイトのSSL更新を確認しよう」とブラウザを開いたところから始まり、最終的には端末のセキュリティソフトに辿り着くという展開でした。

SSL証明書周りで「思っていたのと違う」という現象に遭遇したら、サーバー側を疑う前に、まず複数経路で実態を確認するのが大切ですね。コマンドラインからの検証は、ブラウザだけでは見えない真実を教えてくれます。

コメント

タイトルとURLをコピーしました