はじめに
今週、世間ではPHPの重大な脆弱性「php2shell」が大きな話題になっていました。ですがその影で、もう1つエンジニアが対応に追われたことがありました。
HTTPSの証明書を発行している会社のひとつ、eMudhraで問題が見つかりました。証明書を発行するときに使う「秘密の鍵」が、誰でも計算して取り出せる状態になっていたのです。
ただ、業務でOSやランタイム、ライブラリのアップデートの対応をしながら、正直こう思っていました。
- 「証明書を発行する鍵が漏れると、具体的にどういうリスクがあるのかいまいち理解してないなあ」
- 「うちのサイトはeMudhraの証明書を使っていないのに、なぜ対応が必要があるんだろうか」
- 「なんでOSだけでなく、Python、Node.js、Java、Goまで全部上げる必要があるんだっけ」
なので自分の整理も兼ねて調べてわかったことを書き留めます。
この記事は2026年10月25日時点の公開情報をもとにしています。状況は変わる可能性があるので、最新情報は末尾の一次情報を確認してください。
対象読者
- 会社でパッチ当てやバージョンアップの対応をしている(または、これからする)
- HTTPSや証明書の仕組みはなんとなくしかわからない
- 言われたとおりに対応はしたけれど、なぜ必要なのか腹落ちしていない
(ですのでTLSや証明書に詳しい人には当たり前で物足りない内容だと思います。)
まとめると
- eMudhraで、証明書を発行するための「秘密の鍵」が誰でも計算できる状態だった。この鍵があれば、どのサイトの証明書でも本物そっくりに作れる
- 放っておくと困ることは2つある。偽物のサイトを本物だと信じてしまうことと、正しい手続きで作った証明書なのに、そのサイトにつながらなくなること
- 「どの証明書を信じるか」のリストは、OS、ブラウザ、Python、Node.jsなどがそれぞれ別々に持っている。だから全部アップデートする必要がある
前提知識:証明書の仕組みをざっくり
この記事を読むのに必要なところだけ説明します。
証明書は「相手が本物か」を確かめるためのもの
HTTPSには大きく2つの役割があります。
- 通信を暗号化して、途中で盗み見られないようにする
- つないだ相手が本物のサイトかどうかを確かめる
今回関係するのは2つ目です。どれだけしっかり暗号化していても、相手が偽物なら、偽物と秘密の会話をしているだけです。
相手が本物かを確かめるために使うのが証明書です。サイトは「私は example.com です」と書かれた証明書を見せます。ブラウザやプログラム(以下まとめて「クライアント」と呼びます)は、それを見て相手を確認します。
信頼できる発行元が出した証明書だけを信じる
ただ、「私は example.com です」と書くだけなら誰でもできます。そこで証明書には、認証局(CA) という発行元が「このサイトが example.com の持ち主であることを確認しました」という署名を付けています。
運転免許証にたとえるとわかりやすいです。見せられた免許証を信じるのは、自分で作ったカードではなく、公安委員会という信頼できる機関が発行したものだからです。
信じる発行元のリスト(トラストストア)
クライアントは「どの認証局なら信じるか」のリストを持っています。これを トラストストア と呼びます。
- リストに載っている認証局が署名した証明書 → 信じる
- 載っていない認証局が署名した証明書 → 「この証明書は信用できません」とエラーにする
このトラストストアは、後の項でとても大事になります。
ルートCAと中間CAは「本社と支店」
実際の認証局は、2段階以上の構造になっています。会社にたとえると次のとおりです。
| 証明書の用語 | たとえ | 役割 |
|---|---|---|
| ルートCA | 本社 | トラストストアに載っている。とても大事なので、ふだんは直接証明書を発行しない |
| 中間CA | 支店 | 本社から発行の権限をもらい、実際にサイトの証明書を発行する |
| サーバー証明書 | 支店が発行した書類 | 各サイトが使う証明書 |
ルートCA(本社。トラストストアに載っている)
└ 中間CA(支店。本社が「この支店は信頼してよい」と署名している)
└ サーバー証明書(example.com の証明書。支店が署名している)
クライアントは、サーバー証明書から順に署名をたどります。最後にトラストストアに載っている本社(ルートCA)にたどり着けば、その証明書を信じます。このつながりを 証明書チェーン と呼びます。
秘密鍵は「支店の印鑑」
署名に使うのが 秘密鍵 です。たとえるなら、支店の印鑑です。
ふだん支店は、申し込んできた人が本当にそのドメインの持ち主かを確認してから、印鑑を押して証明書を発行します。
しかし、印鑑そのものが盗まれたらどうなるでしょうか。確認を飛ばして、どのサイトの証明書でも作れてしまいます。しかも押されているのは本物の印鑑なので、クライアントから見ると本物と見分けがつきません。
署名が本物かを確かめるための 公開鍵 は、誰でも見られるようになっています。印影のようなものです。今回の問題は、この公開鍵から秘密鍵を計算できてしまったことでした。たとえるなら、印影を見るだけで印鑑を複製できる状態だったということです。
何が起きたのか
- 2025年4月、eMudhraが中間CAの鍵を作るときに、手順とコードの不具合で非常に弱い秘密鍵が作られた
- その弱い鍵を持つ中間CAが、ルートCA「emSign ECC Root CA - G3」の配下で、信頼される証明書として作られてしまった
- 2026年10月17日、研究者がブラウザベンダーとeMudhraに報告した。Mozillaは実際に公開鍵から秘密鍵を取り出せることを確認した
- 対象はTLS用の中間CAが6つ、コード署名用の中間CAが2つ
ChromeとFirefoxはその日のうちに中間CAをブロックしました。その後、ルートCA「emSign ECC Root CA - G3」そのものも信頼の一覧から外されることになりました。
リスク①:偽の証明書を信じてしまう(クライアント側)
鍵を手に入れた攻撃者は、たとえば github.com や api.example.com の証明書を作れます。クライアントがまだこの中間CAを信頼していれば、偽の証明書は正当なものとして通ります。
すると、通信経路の途中にいる攻撃者(公衆Wi-Fi、乗っ取られたルーター、DNSの書き換えなど)が、本物のサーバーになりすませます。そして通信を盗み見たり書き換えたりできます。いわゆる中間者攻撃(MITM)です。
ポイントは④です。押されているのは本物の印鑑なので、クライアントは証明書をたどってトラストストアと照合しても、偽物だと気づけません。
逆に、クライアントのトラストストアが更新されていれば、同じ攻撃をされても④で止まります。
今回のパッチ当てやバージョンアップは、自分たちのクライアントを、この「アップデート後」の状態にするための作業です。
コード署名用の中間CAが使われると、マルウェアに正規の署名を付けて正しいソフトウェアに見せかけることもできます。
ブラウザは比較的安全、ブラウザ以外が危ない
Chromeは、公開された証明書に CT(Certificate Transparency) を求めます。CTは、発行された証明書をすべて公開ログに記録する仕組みです。ログに載っていない証明書は信頼されません。Googleによると、ブロックするまでに、この中間CAにつながる証明書はCTログに記録されていませんでした。そのため、CTを強制するクライアントに対して偽の証明書が使われることはなかったとしています。
一方で、curl、Pythonの requests、Goの net/http、Javaの HttpClient などは、ふつうCTをチェックしません。ブラウザのような即時ブロックの仕組み(ChromeのCRLSetなど)もありません。
つまり、残るリスクはサーバー間通信、バッチ、CLI、社内ツールのような「ブラウザ以外のクライアント」に集中しています。 外部APIを呼ぶバックエンドや、決済、認証の連携などがそれに当たります。
Cloudflareのブログでも、次のように書かれています。
A successful browser test does not prove that a background service is protected
ブラウザで問題なく見えても、裏で動いているサービスが守られているとは限りません。
リスク②:自分の証明書が使えなくなる(サーバー側)
今回はルートCA「emSign ECC Root CA - G3」ごと信頼されなくなります。そのため、鍵が漏れていない中間CAから発行された正しい証明書も、このルートにつながっていれば使えなくなります。
Googleの集計では、対象の証明書は約3万件です。ほとんどは米国の教育研究機関向けのInCommonの証明書でした。ChromeからのルートCA削除は、2026年10月27日(米国太平洋時間) に、Chromeのルート証明書一覧を使う端末へ配信される予定です。
自社のサイトや、自社が呼んでいる外部APIがこのルートの証明書を使っていると、その日以降接続エラーになります。セキュリティの問題ではありませんが、障害(可用性の問題)になります。
なぜ「全部アップデート」が必要なのか
自分としてはここがよく分かっていませんでした。
答えは、「どのCAを信頼するか」の一覧(トラストストア)が、1か所ではなくいろいろな場所にあるからです。
| 持っている場所 | 例 | 今回の対応 |
|---|---|---|
| OS | Ubuntu / Debian の ca-certificates、RHEL |
パッケージ更新でルートを削除 |
| ブラウザ | Chrome Root Store、Firefox(NSS) | CRLSet / OneCRLで中間CAをブロック、ルートを削除 |
| Go | crypto/x509 |
対象の中間CAを含むチェーンを拒否 |
| Node.js | 本体に内蔵したCA一覧 | セキュリティリリースで更新 |
| Java | JDKの cacerts
|
Oracleが中間CAを不信頼にする更新を提供 |
| Python |
certifi(requests などが使う) |
パッケージ更新 |
| PHP | Composer の ca-bundle
|
中間CAを不信頼に |
| Rust | webpki-roots |
ルートを削除 |
| Electronアプリ | ChromiumとNode.jsの両方を内蔵 | 修正版への更新が必要 |
たとえば次のような状況が起こります。
- OSの
ca-certificatesを更新しても、Pythonのrequestsはcertifiの一覧を見ているので守られない -
certifiを更新しても、pipは自分専用のcertifiを内蔵しているので、pip自体の更新も必要 - Node.jsは、デフォルトでは本体に内蔵したCA一覧を使う。そのためOSを更新してもNode.jsの更新が必要
- コンテナは、ベースイメージを作り直してデプロイし直さないと古いトラストストアのまま
- 長く動いているプロセスは、起動時に読み込んだトラストストアを使い続けることがある。更新後に再起動が必要
Cloudflareのブログにある次の一文が、この状況をよく表しています。
Updating the SDK alone may not change the underlying policy.
SDKを上げても、実際に証明書を検証しているのはその下のHTTPライブラリやランタイム、OSかもしれません。だから「依存関係の下のほうまで確認して、全部上げる」という対応になります。
自分の環境を確認する
確認は、各ベンダーのアドバイザリにある修正済みのバージョン以上になっているかで判断してください。
まずはバージョンを確認する
| 対象 | 修正済みのバージョン | 確認先 |
|---|---|---|
Ubuntu の ca-certificates
|
22.04:20260816ubuntu0.22.04.124.04: 20260816ubuntu0.24.04.126.04: 20260816ubuntu0.26.04.1
|
USN-8944-1 |
| Red Hat(RHEL) | アドバイザリを参照 | RHSB-2026-012 |
| Node.js | v22.23.4 / v24.22.1 / v26.12.1 | October 21, 2026 Security Releases |
Python の certifi
|
2026.10.19 以上 | Python Discourse |
| pip | 26.2.2 以上 | 同上 |
| uv | 0.13.4 以上 | 同上、uvのアドバイザリ |
| Go | 1.26.10 / 1.27.3 | Release History、golang/go#82345 |
| Java(Oracle) | アドバイザリを参照 | CVE-2026-91403 |
| Firefox | アドバイザリを参照 | MFSA 2026-111 |
| Electron | アドバイザリを参照 | GHSA-jr5w-w6x2-84p9 |
| PHP(Composer の ca-bundle) | リリースノートを参照 | composer/ca-bundle |
| Rust(webpki-roots) | リリースノートを参照 | rustls/webpki-roots |
サーバー側:自分の証明書が対象か
# ここを自分のサイトや、呼んでいる外部APIのホスト名に変える
HOST=api.example.com
# 証明書のつながりを表示する
openssl s_client -connect "$HOST:443" -servername "$HOST" -showcerts </dev/null 2>/dev/null \
| grep -E "^ *[0-9]+ s:|^ +i:"
example.com で試すと、次のように表示されます。
0 s:/CN=example.com
i:/C=US/O=SSL Corporation/CN=Cloudflare TLS Issuing ECC CA 3
1 s:/C=US/O=SSL Corporation/CN=Cloudflare TLS Issuing ECC CA 3
i:/C=US/O=SSL Corporation/CN=SSL.com TLS Transit ECC CA R2
...
-
s:(subject)は誰の証明書か -
i:(issuer)は誰が印鑑を押したか
前提知識のたとえで言うと、「証明書 → 支店 → 本社」のつながりがそのまま表示されています。emSignが関係していれば、どこかの行に「emSign ECC Root CA - G3」が出てきます。別の本社からもお墨付きをもらっている(クロス署名)場合もあるので、いちばん下の行だけでなく全体を見てください。
| 結果 | 意味 |
|---|---|
| つながりが表示され、「emSign ECC Root CA - G3」がどこにもない | 対象外 |
| どこかに「emSign ECC Root CA - G3」がある | 対象。証明書の差し替えが必要 |
| 何も表示されない | 接続できていない。ホスト名、ポート、ネットワークを確認する |
443以外のポートで動いているサービスは、$HOST:443 のポート番号を変えてください。自社サイトだけでなく、自社が呼んでいる外部APIのドメインも確認しておくと安心です。
動作確認用のページ
we-have-a-problem.com には、問題の中間CAにつながる証明書を使ったテスト用のサーバーが用意されています。ページ自体はふつうの証明書なので、普通に開けます。ページを開くと、ブラウザがテスト用のサーバーに自動でつなぎにいきます。
- ✅:つながらなかった(正しくはじかれた)。ただし、ネットワークの問題でつながらなかった場合も✅になります
- つながった:まだ問題の中間CAを信じてしまっている
このページでわかるのは、開いたブラウザのことだけです。サーバーやバッチは、ページに載っている curl のコマンドで確認してください。
ブラウザ(Chrome)での結果
手元のMac(macOS 27.0.1)のChromeでページを開くと、14本すべてが「Did not connect」になりました(2026年10月25日時点)。
最後の行の「Your browser trusted 0 of 14 compromised certificate chains.」は、問題の中間CAにつながる証明書を1本も信じなかった、という意味です。Googleの発表どおり、Chromeは守られています。
curlコマンドの実行(良くない例)
$ curl https://dv-g3b-emudhra.we-have-a-problem.com/
Your client successfully connected to the site. That's bad!
$ curl https://dv-g3b-aaa-emudhra.we-have-a-problem.com/
Your client successfully connected to the site. That's bad!
同じMacのターミナルで、curl を実行した結果です。Chromeでははじかれたサーバーに、curl ではつながってしまいました。 同じMacから同じサーバーにつながっているので、Chromeの「Did not connect」はネットワークの問題ではなく、正しくはじいた結果だということもわかります。
報道や有志のまとめによると、Appleは10月19日に、OSのアップデートではなく「取り消した証明書のリスト」を配る仕組みで、問題の中間CAをブロックしています。そのため、SafariやAppleの標準の仕組みで通信するアプリは守られています(一部の環境ではまだつながるという報告もあります)。
ところが、curl、git、python3 などのターミナル用のツールは、/etc/ssl/cert.pem という別の証明書の一覧ファイルを使っていて、このリストを見ません。このファイルは更新されておらず、手元のMacでもemSignのルートが残っていました。同じMacの中でも、ブラウザは守られているのに、ターミナルのツールは守られていない。まさに「ブラウザは比較的安全、ブラウザ以外が危ない」の状態です。
この時点では、最新のmacOSにしても、ターミナルのツールが使う一覧は更新されていません。公衆Wi-Fiなど信頼できないネットワークで、curl やスクリプトを使って大事な通信をするのは避けたほうが安心かと思います。
まとめ
- 中間CAの秘密鍵が漏れると、どのドメインの証明書でも偽造できる
- リスクは2つ。偽の証明書を信じるリスク(クライアント側) と、自分の証明書が使えなくなるリスク(サーバー側)
- ブラウザはCTと即時ブロックの仕組みで比較的早く守られた。残るリスクはブラウザ以外のクライアント
- トラストストアはOS、ランタイム、ライブラリ、アプリがそれぞれ持っている。だから全部のアップデートと再起動・再デプロイが必要
最初は「言われたから対応している」という状態でしたが、記事にまとめてみて、なぜこれだけ対応範囲が広範なのか、自分の中で整理できた気がします。
セキュリティの対応はスピードが大事なので、頭の整理が後回しになってしまうところもあるかもしれませんが、その都度時間を取ってキャッチアップしたいですね、、
参考
- eMudhraの中間CA証明書の秘密鍵の危殆化についてまとめてみた - piyolog
- Protecting Chrome and Android Users from Compromised eMudhra Intermediate CA Certificates - Google
- Protecting Firefox users following the compromise of eMudhra intermediate certificates - Mozilla
- 8 eMudhra certificates are compromised. Here's what you need to know - Cloudflare
- Bug 2083449 - eMudhra: Private key compromise of Intermediate Certificates - Mozilla Bugzilla
- crypto/x509: mitigation for security concerns with a specific Certificate Authority - Go
- October 21, 2026 Security Releases - Node.js



