
2026/08/26 5:49
Pythonにおける`str.lower()`がセキュリティ脆弱性になる場合 — セス・ラーソン
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Python は標準ライブラリの IDNA 実装を StringPrep および IDNA 2003(RFC 3491)で要求されている Unicode 3.2.0 の小文字化ルールと整合させるための重大なセキュリティ修正を発表しました。以前は Python が最新バージョンの Unicode データ(例:17.0.0)に依存しており、StringPrep の小文字化ステップにおいて
str.lower() を使用していましたが、これは RFC 3454 と矛盾し、特定のドメイン名の符号化を誤ったものとして生成していました。例えば、「ᎠᎠ」という文字列が最新データ下では 'xn--kz9aa' と符号化されますが、コンプライアンス要件を満たす正解は 'xn--58da' でした。この問題を解決するため、Python は内部例外を導入し、StringPrep および IDNA アルゴリズムに対して str.lower() が呼び出された場合に Unicode 3.2.0 のマッピングテーブルを仮定して動作するようにしました。同時に、現代の Unicode データを置き換えることもせず、PyPI の idna パッケージ利用者には影響を与えません(同パッケージは一般的に好まれる IDNA 2008 を既にサポートしています)。この修正は CVE-2026-17084 で文書化されており、標準ライブラリ内の国際化タスクにおいて一貫した正規形ドメイン名形式を保証します。この脆弱性は Bitshift によって報告され、Stan Ulbrych が共同で修復を開発し、Marc-Andre Lemburg および Petr Viktorin によるレビューを経て完了しました。本文
IDNA 脆弱性の原因と対策:Unicode バージョン不整合について
背景:国際化ドメイン名(IDN)の課題
- インターネット標準ではASCII 文字のみをサポートしているが、世界中で多様な文字が使われている。
- ドメイン名に Unicode を使用するためには、Unicode から ASCII へのマッピングが必要。
- その解決策の一つが NamePrep(RFC 3491) で、国際化ドメイン名(IDNA)実装の重要な要素である。
アルゴリズムとバージョンの進化
- NamePrep は「StringPrep」のプロファイルとして定義され、IDNA 2003としても知られる。
- 規定書:RFC 3454(StringPrep)および RFC 5890〜5893(IDNA 2003)。
- Python における実装状況:
- IDNA 2003: 標準ライブラリの
モジュールでサポート。stringprep - IDNA 2008: パッケージインデックス上の
パッケージ(idna
)でサポート。str.encode('idna')
- IDNA 2003: 標準ライブラリの
- 推奨事項: 古い動作が必要な場合を除き、IDNA 2008 を使用すべき。
脆弱性の原因:ケースフォールドの不整合
StringPrep の第 3.2 節では「ケースフォールド」が定義されており、文字列の区分別比較を行うためのステップである。
- 仕様上の動作 (B.2 テーブル): Unicode ルールに従いすべての文字を小文字化(実質的に
)。str.lower() - 例外処理 (B.3 テーブル): 特殊なケースのみ例外として扱う。
Python 標準ライブラリにおいて、以下のコードが脆弱性の原因となっていた:
def map_table_b3(code): r = b3_exceptions.get(ord(code)) if r is not None: return r return code.lower() # ⚠️ ここが問題の所在
なぜ str.lower()
はダメなのか?
str.lower()- Python インタープリタは付帯された最新のUnicode データベースを直接使用しているため。
- 現在の環境(例:
がunicodedata.unidata_version
)では、最新の Unicode ルールが適用される。'17.0.0'
仕様との不一致の具体例
StringPrep は一貫性のある動作のため、Unicode 3.2.0 のデータベースを必須としている(RFC 3454 に基づく)。
-
正しい挙動 (Unicode 3.2.0):
# RFC 3454 準拠の期待される結果 "ᎠᎠ".encode("idna") # 'xn--58da' -
脆弱性の原因 (現在の Python 17.0.0):
# Unicode 17.0.0 のルールが適用され、仕様に沿わない結果 "ᎠᎠ".encode("idna") # 'xn--kz9aa'
対策:例外テーブルの作成と修正
を Unicode 3.2.0 の動作に見立てるために、新しい例外テーブルを作成した。str.lower()- 最新の Unicode バージョンと Unicode 3.2.0 との挙動が異なるすべてのコードポイントを検出・記録した。
- これにより、IDNA 2003 は仕様に完全準拠するようになった。
謝辞とクレジット
- 脆弱性の報告: Bitshift
- 対策の開発: Stan Ulbrych(共同開発)
- 対策のレビュー: Marc-Andre Lemburg, Petr Viktorin
- 詳細情報:CVE-2026-17084 をご参照ください。
おわりに
Python Software Foundation のセキュリティ開発者として、Alpha-Omega による支援に感謝します。
- 共有: Mastodon、メール、Bluesky で考えを発信できます。
- アーカイブ: このブログの全投稿(193 件)を閲覧可能です。
- フォロー: RSS やニュースレターを通じて最新情報を取得できます。
- 推奨: まずは外に出て過ごすことが一番おすすめです。