develop

eMudhraの中間CA鍵漏洩、Macの開発環境でやった対策

インドの認証局eMudhraで、中間CA証明書の秘密鍵が漏れていたことが公表されました。ブラウザは自動で守られていますが、curlやgit、Node.jsなどの開発ツールは自分で更新しないと守られません。Macでフロントエンド開発をしている私の環境で、実際にやった対策をまとめました。

何が起きたのか

2026年10月20日、eMudhraの中間CA証明書8つについて、秘密鍵が危殆化(悪用できる状態になること)していたと公表されました。

ポイントはこのあたりです。

  • 原因は盗難ではなく、CA側の鍵の作り方のミス
  • 証明書から、誰でも秘密鍵を復元できる状態だった
  • 2025年4月から、ずっとその状態だった可能性がある
  • Mozilla・Microsoft・Apple・Chromeが信頼するルート証明書につながっている

つまり鍵を手に入れた攻撃者は、どんなサイトにもなりすませる偽の証明書を作れてしまいます。

危ないのはブラウザ以外

ブラウザはほぼ心配ありません。Firefox・Chromeは10/17から、Appleは10/19から、Microsoftは10/20から、該当の中間CAをブロックしています。

問題はブラウザ以外のツールです。curl・git・pip・npm・Node.jsなどは、「信頼するルート証明書の一覧」から対象を外さない限り、偽の証明書を信用してしまいます。

とはいえ、実際に被害にあうのは、通信の途中に割り込まれた(MITM)場合です。怪しい公衆Wi-Fiのほか、乗っ取られたルーターやDNSの改ざんなどでも起こりえます。普段の環境ならまず大丈夫。ただ、直すまではリスクが続くので対応しましょう。

ちなみに、漏れたのはeMudhra側にある中間CAの秘密鍵です。自分のMacには入っていないので、消すことはできません。手元でできるのは、その先にあるルート証明書を「信頼しない」ようにすることです。対象のルートはこの2つです。

  • AAA Certificate Services(古いSectigo系のルート)
  • emSign ECC Root CA - G3

まずは自分の環境をチェック

参考サイトに、危ない証明書を使ったテスト用のサイトが用意されています。エラーになれば対策済み、繋がったら未対策です。

Mac標準のcurlで確認します。テスト用のサイトは2つあります。

/usr/bin/curl -sS -o /dev/null https://dv-g3b-emudhra.we-have-a-problem.com/ && echo "繋がった=未対策"
/usr/bin/curl -sS -o /dev/null https://dv-g3b-aaa-emudhra.we-have-a-problem.com/ && echo "繋がった=未対策"

Node.jsはこちら。

node -e "fetch('https://dv-g3b-emudhra.we-have-a-problem.com/').then(()=>console.log('繋がった=未対策')).catch(e=>console.log('エラー=OK:', e.cause?.code))"

curlで「繋がった=未対策」と出たら、「信用してしまう設定になっている」という意味です。(あくまで設定のチェックなので、攻撃を受けたかどうかまでは分かりません)

Macでやった対策

Homebrew

Homebrewで入れたcurlやPythonは、Homebrewのca-certificatesを参照しています。こちらを更新しておきます。

brew update && brew upgrade ca-certificates

/etc/ssl/cert.pemからルートを外す

Mac標準のcurl・git・python3などは、/etc/ssl/cert.pemというファイルで証明書をチェックしています。このファイルがまだ更新されていないので、自分で2つのルートを外します。

たとえば怪しいWi-Fiでgithub.comになりすまされた場合、対策前だとSafariは止まってもgit pullは偽サーバーに繋がってしまいます。対策後は、証明書エラーで止まるようになります。

まず、外す対象が入っているか確認します。2行表示されればOKです。

grep -E '^=== .*CN=(AAA Certificate Services|emSign ECC Root CA - G3)$' /etc/ssl/cert.pem

証明書の数もメモしておきます。

grep -c 'BEGIN CERTIFICATE' /etc/ssl/cert.pem

バックアップを取ってから、2つを外します。

sudo cp -p /etc/ssl/cert.pem /etc/ssl/cert.pem.bak && sudo awk '/^(###|===) /{skip=0}; /^=== .*\/CN=(AAA Certificate Services|emSign ECC Root CA - G3)$/{skip=1}; !skip' /etc/ssl/cert.pem.bak | sudo tee /etc/ssl/cert.pem > /dev/null

もう一度grepして何も出ず、証明書の数が2つ減っていれば成功です。テスト用サイトへのcurlもエラーになります。

困ったときは、バックアップから戻せます。

sudo cp -p /etc/ssl/cert.pem.bak /etc/ssl/cert.pem

macOSのアップデートでファイルが元に戻ることもあるので、OSを更新したら一度確認しておくと安心です。

Apple標準のpipは見送り

Apple標準のpip3は、pipが持っている古い証明書の一覧を使っています。参考サイトでは、sitecustomize.pyという設定ファイルに追記する方法が紹介されています。これは、Apple標準のpipを日常的に使っていて、しかも今すぐ守りたい人向けの対応です。

まず、普段どのPythonを使っているか確認してみました。

which -a python3 pip3

一番上に出てきたのは、miseで入れたPythonでした。Apple標準のPythonは使っていなかったので、この対応はしないことにしました。設定もずっと残るので、何年か後、/etc/ssl/cert.pemの更新が追いついていないときに、pipだけが証明書エラーになる、といった原因の分かりにくいトラブルにつながる可能性があることも理由の1つです。代わりに、miseで入れたPythonのpipやuvのバージョンを調べて更新します。

まず、pipのバージョンを確認します。修正版は26.2.2以上です。

python3 -m pip --version

古ければ更新します。miseで入れたPythonでも、このコマンドでOKです。

python3 -m pip install -U pip

更新されるのは、今使っているバージョンのPythonだけです。miseで複数のPythonを入れている場合は、それぞれ更新します。入っているPythonはこちらで確認できます。

mise ls python

requestsなどのライブラリが使うcertifiも確認します。修正版は2026.10.19以上です。「not found」と出たら、入っていないので対応は不要です。

python3 -m pip show certifi

古ければ更新します。

python3 -m pip install -U certifi

次はuvです。修正版は0.13.4以上です。

uv --version

miseで入れている場合は、こちらで更新します。

mise upgrade uv

uv = "0.12"のようにバージョンを固定していると、その範囲でしか上がりません。最新に上げたいときは--bumpを付けます。

mise upgrade --bump uv

最後に、pipが偽の証明書を弾くか確認します。SSLエラー(CERTIFICATE_VERIFY_FAILEDなど)が出ればOKです。

python3 -m pip download --no-deps -d /tmp/piptest -i https://dv-g3b-emudhra.we-have-a-problem.com/simple/ pip

Node.jsは本体の更新が必要

OSやHomebrewを更新しただけだと、抜けがちなのが以下です。

Node.jsは、信頼するルート証明書の一覧を本体に組み込んで持っています。なので、/etc/ssl/cert.pemを直してもNode.jsには効きません。Node.js本体を修正版に上げる必要があります。

Node.jsで通信している処理は、意外と多いです。

  • npm installやpnpm installでのパッケージ取得
  • Next.jsのサーバー側でのfetch
  • Vercel CLIなど、Node.js製のCLIツール

どれも秘密情報を扱ったり、取ってきたものを実行したりするので、ここは確実に押さえておきたいところです。

修正版のバージョン

Node.jsは2026年10月21日のセキュリティリリースで対応しました。公式の告知に「ca-cert (eMudhra Intermediate CA Compromise)」と明記されています。

  • 26系:26.12.1
  • 24系:24.22.1
  • 22系:22.23.4

20系には修正版が出ていません。すでにサポートが終わっているので、使っているなら22以上に上げましょう。

miseで上げる

私はmiseでNode.jsを管理しています。まず、どのバージョンが使われているか確認します。

mise ls node

修正版を入れます。

mise install node@24.22.1

.node-versionを24.22.1に書き換えてから、バージョンを確認します。

node --version

v24.22.1と出て、さっきのテスト用サイトへのfetchがエラーになれば完了です。.node-versionはリポジトリに入っているので、PRでチームにも共有できます。

CIのバージョンもそろえる

GitHub Actionsでnode-version: 24のようにメジャーバージョンだけを指定していると、ランナーに入っている古い24系が使われることがあります。

GitHubのデータセンター内で割り込まれるリスクはほぼありません。ただ、手元とCIのバージョンをそろえておくと、今後の更新が.node-versionの1か所で済みます。

 - uses: actions/setup-node@v6
   with:
-    node-version: 24
+    node-version-file: .node-version

本番環境は大丈夫?

気になるのが、リリース済みのWebアプリへの影響です。判断のポイントは、Node.jsが「通信する側」として動いているかどうかです。

  • ユーザーがブラウザでアプリを見る通信:影響なし
  • S3やCloudFrontで配信している静的なSPA:本番にNode.jsがないので影響なし
  • SSRやAPI Routesなど、サーバーから外部APIへの通信:理屈上は対象だけど、割り込むのが難しくリスクは低い
  • 手元の開発環境:現実的にリスクあり

サーバー側でNode.jsを動かしている場合、Vercelなどのマネージド環境なら事業者側で更新されます。Dockerで動かしているなら、次のデプロイのときに修正版のイメージで作り直しましょう。

そのほかに更新しておきたいツール

フロントエンド開発でよく使うものを抜粋しました。全体の一覧は参考サイトにあります。

  • Homebrew ca-certificates:2026-09-25_1
  • Deno:2.9.8
  • Bun:対応待ち
  • Electron(VS Codeなどのアプリ):44.9.1 / 43.8.1 / 42.11.16
  • Claude Code:2.1.303
  • mise:2026.10.17
  • pip:26.2.2
  • Python certifi:2026.10.19
  • uv:0.13.4

まとめ

eMudhraの件は、ブラウザだけ使っている人なら気にしなくて大丈夫です。でも開発者は、curlやgit、Node.jsなどブラウザ以外のツールを自分で更新する必要があります。特にNode.jsは、OS側の対策ではカバーされないので、本体の更新を忘れずに。どれも数行のコマンドで終わるので、ほかのツール更新のついでにサクッとやっておくのがおすすめです。

参考