Litmus — 話題のニュースを、深く読み解く。 RSS
ソフトウェア

Pythonのsslモジュールが、漏えいした認証機関の鍵を名指しで拒否するようになった

認証機関eMudhraの中間CA証明書8本の秘密鍵が漏えいした問題で、Python 3.15.2などはpipやOSの更新を待たず、ssl自身がその鍵を使った接続を拒否するようになった。

手のひらで開いた真鍮の南京錠を持つ写真

イメージ写真 記事の内容を撮影したものではありません。Photo by Nathan Thomas on Pexels

この記事には Amazon アソシエイトのリンクを含みます。

10月中旬、認証機関eMudhraの証明書に問題があるというニュースを見て、pipとcertifiを慌てて更新した人は多いはずだ。だが、社内に何十個もあるDockerイメージや、放置していた仮想環境のすべてに更新が行き渡っているかと聞かれたら、自信を持って「はい」とは言えないだろう。

Pythonの開発チームは2026年10月25日、3.15系から3.11系まで5つのバージョンを一斉にリリースした。Python公式ブログによれば、pipを更新していなくても、Pythonをこのバージョンに上げれば今回の鍵を使った攻撃からはもう守られるという。pipの中身を直さずに、なぜ守られるのか。

漏れた鍵は1〜255のどれか

事の発端は、認証機関eMudhraが発行した中間CA証明書8本の秘密鍵が、外部から復元できる状態だったことだ。6本はTLSサーバー証明書用、2本はコード署名用の鍵だった。Mozilla公式セキュリティブログはこの8本を「compromised(侵害された)」と呼んでいる。Cloudflare公式ブログも、独立に同じ本数を確認した。

この鍵が侵害された経緯を知って、自分は数字の桁を二度見した。使われていたのはP-384という方式の鍵で、本来は2の384乗通りという天文学的な数から乱数で選ばれるはずのものだ。ところがMozillaの技術者がバグ報告システムに残した記録には、こう書かれている。

“The 8 impacted keys all used integers less than 256.”

8本の鍵はすべて、256未満の整数だった。Mozilla Bugzilla #2083449のコメントによれば、鍵の48バイトのうち最後の1バイトしか値を持たず、残りはすべてゼロだったという。2の384乗という選択肢の中から、実質0〜255の256択しか使っていなかったことになる。

鍵生成に使うハードウェアの操作手順に不具合があり、本来使うはずのない弱い鍵が誤って証明書の発行に使われた、というのがeMudhra側の説明だ(同コメント)。公開鍵の形式チェックや署名の検証だけでは、この弱さは見抜けない。

発覚から遮断までは速かった。Mozilla Bugzilla #2083449に残る記録(UTC)を見ると、研究者の報告からFirefoxでの遮断完了まで3時間弱しかかかっていない。

日時(UTC)出来事
10/17 16:27研究者がCCADBのテスト中にEC鍵の欠陥を発見
10/17 18:05Apple・Chrome・Microsoft・Mozilla・eMudhraへ報告
10/17 21:48Mozillaが外部の手掛かりなしに鍵を独自に復元
10/17 22:59FirefoxのOneCRLで遮断が完了
10/18 09:24〜09:38eMudhraが8件の証明書を「caCompromise」理由で失効

ブラウザはもう対応を終えている。それでもPythonが追加の修正を出したのはなぜか。

ブラウザの遮断とpipの中は別の層

答えは、証明書を信頼するかどうかの判断が、ひとつの場所にまとまっていないことにある。

ブラウザは、OSやブラウザ自身が持つ「ルート証明書ストア」を見て、どの認証機関を信頼するかを決めている。Firefoxが10月17日に遮断したのは、まさにこのストアの話だ。ところがPythonのpipは、OSの証明書ストアとは別に、certifiというパッケージが同梱する独自の証明書一覧を読みにいく。pip開発者のChristian Heimes氏はdiscuss.python.org公式スレッドで、こう説明している。「pipは常に同梱のcertifiをシステムの信頼ストアに追加して読み込む。これを無効化する方法はない」。

つまり、OSやブラウザがどれだけ早く遮断しても、pipが同梱するcertifiのバンドルを更新しない限り、そのpipは古い判断のまま動き続ける。同じスレッドには、2026年10月21日時点でもDocker公式のPythonイメージがまだ修正前のpip(26.2.1)を含んでいたという報告が残っている。ブラウザの3時間弱という速さと、Dockerイメージの数日越しの遅れは、どちらも本当の話だ。見ている信頼の層が違うだけになる。

では、pipの更新が追いつくまでの間、このDockerイメージは無防備なのか。ここでPython自身が動いた。

sslモジュールが「最後の砦」になった

Python 3.15.2などのchangelogには、こう書かれている。

“ssl now rejects TLS certificate chains that contain any of a set of compromised eMudhra intermediate CA keys. This is an extraordinary measure and a last line of defense: removing trust is primarily the job of the platform root store(…)”

標準ライブラリのsslモジュールが、侵害された8本の鍵を使った証明書チェーンを、それと分かった時点で拒否するようになった。Python公式changelogはこれを「異例の措置であり、最後の防衛線」と自ら呼んでいる。この書き方には納得がいく。認証機関の鍵を信頼するかどうかは、本来OSやブラウザが担当する領域だ。プログラミング言語のランタイムが単独で特定の鍵を名指しして拒否するのは、普段は起きないことだからだ。

ハッシュ値の照合で自動適用

照合の方法は具体的だ。証明書に含まれる公開鍵情報(SubjectPublicKeyInfo、SPKI)をSHA-256でハッシュ化し、侵害された8本の鍵のハッシュと一致するかを見る。cpython公式GitHubリポジトリのIssue #160040によれば、この照合は証明書の検証を有効にしたすべての接続に自動で適用される。同じ鍵を使い回した別経路の証明書(クロス署名証明書)が後から見つかっても、公開鍵さえ一致すれば同じように拒否される設計だ。

Python公式ブログは、pipの通常の利用(pip自身をアップグレードする操作を含む)について、pipが未更新のままでもこの攻撃からすでに安全だと述べている。pip側の信頼リストが古くても、通信を実際に行うsslモジュールの側で、侵害された鍵を使った接続そのものが成立しなくなるからだ。Dockerイメージの中でpipの更新が止まっていても、そのイメージのPython本体が3.15.2などに上がっていれば、この8本の鍵を使った接続だけは拒否される。

守られるのは8本の鍵だけ

ここまで聞くと、Pythonを上げておけば十分だと思いたくなる。だが、この拒否リストが守ってくれるのは、今回判明した8本の鍵に限られる。cpython公式Issueでも、適用基準は高い閾値に絞られている。「グローバルに信頼されたルートまたは中間CAが侵害され、通信の盗聴・改ざん(MitM攻撃)が可能な場合」だけだ。将来、別の認証機関で同種の問題が起きても、今回のリストが自動で対応してくれるわけではない。

だからこそdiscuss.python.orgの公式スレッドは、Pythonのアップグレードと並行した更新を案内している。読者が実際に確認すべきことは、次の3点になる。

  • Pythonの系列ごとの修正版へ上げる。手元のPythonが使う系列の最新修正版を入れる
  • pipを26.2.2以上、certifiを2026.10.19以上に上げる。discuss.python.orgのスレッドは、これを「唯一確実な修正」と案内している
  • uvを使っている場合は0.13.4以上に上げる。UV_SYSTEM_CERTS=true uv self updateで更新すれば、埋め込みの証明書バンドルを経由せずに済む

Pythonの系列と修正版の対応は次のとおりだ。

系列今回の修正版配布形態
3.153.15.2バイナリインストーラあり
3.143.14.10バイナリインストーラあり
3.133.13.18ソースのみ
3.123.12.16ソースのみ
3.113.11.18ソースのみ

3.13以前はソースコードのみの配布で、公式のバイナリインストーラは無い。ディストリビューションやDockerイメージ側の更新を待つか、自分でビルドすることになる。Python公式changelogを見て、使っている系列がどちらに当たるかを確認しておく必要がある。

同じリリースには、tarfileの展開先をリンクで置き換える手口を塞ぐ修正(CVE-2026-114583)も入った。こちらは今回のeMudhraの一件とは別の脆弱性になる。

まとめ

eMudhraの中間CA証明書8本の秘密鍵は、2の384乗通りのはずの乱数が実質256択に縮んでいたために復元された。ブラウザはOSやブラウザ自身の証明書ストアをすでに遮断した。だがPythonのpipが同梱する証明書バンドルはそれとは別の層にあり、更新が追いつくまでは古い判断のまま残る。Python 3.15.2などはその隙間を埋めるため、sslモジュール自身に侵害された鍵のSHA-256ハッシュを直接組み込んだ。pipの更新を待たずに該当の接続を拒否する「最後の砦」を、異例の措置として追加した。

ただしこれは今回の8本の鍵に限った特別対応であり、将来の別の侵害を自動で防ぐものではない。手元のPythonを修正版に上げること、pip・certifiを更新すること、この両方が欠かせない。

この記事の先を読む本を探す

特定の本を勧めるものではありません。検索結果から、いまの版と目次を確かめて選んでください。

コメント

気づきや感想をどうぞ

記事への補足や、読んで考えたことをお寄せください。個人情報や、他の人を傷つける内容の投稿はお控えください。

コメントを読み込んでいます…

    ほかの記事

    最新の記事 →