13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「なんすかそれ?」と言われたので、Punycodeを完全解説する【RFC仕様から日本語ドメインまで】

Posted at

はじめに

こんばんは、GMOコネクト 執行役員CTOの菅野 哲(かんの さとる)です。

今日ね、社内で「Punycode(ピュニコード)」について話したら、「なんすか?それ??」 と言われた。

…いや、わかる。確かに普段意識することは少ない。

でも、ブラウザのアドレスバーに「日本語.jp」と入力したとき、裏側で何が起きているか知っているだろうか? DNSは本来ASCII文字しか扱えない。では、なぜ日本語ドメインが動くのか?

その答えがPunycodeである。

実は我々が日常的に使っているインターネットの根幹を支える技術なのだ。「知らなくても困らないけど、知っていると世界の見え方が変わる」——そんな技術をエンジニアとして解説したいと思い、本記事を書くことにした。


この記事でわかること

  • Punycodeの仕組みと、RFC 3492で定義されるBootstringアルゴリズムの動作原理
  • IDNA2003からIDNA2008への変遷と、ToASCII/ToUnicode処理の詳細
  • 日本語ドメインの登録状況(約82,000件)と具体的な変換例
  • Homograph attack(同形異字攻撃)の脅威と、ブラウザごとの対策ポリシー

対象読者

  • xn--」で始まるドメインを見て「なんだこれ?」と思ったことがある人
  • 国際化ドメイン名(IDN)の仕組みを体系的に理解したいエンジニア
  • セキュリティ視点でドメイン偽装リスクを把握したい人

Punycodeとは何か:30秒でわかる概要

Punycode(ピュニコード)は、Unicode文字列をASCII互換形式に変換するエンコーディング方式である。

日本語.jp  →  xn--wgv71a119e.jp

「puny」は英語で「小さい・弱々しい」という意味だが、技術的には「小さなコードで大きなUnicode空間を表現する」というニュアンスで命名されている。2003年にRFC 3492として標準化され、20年以上にわたりインターネットの国際化を支えてきた。

なぜ必要なのか?

DNSは歴史的経緯からASCII文字(a-z, 0-9, ハイフン)しか扱えない。しかし、世界中の人々が自国語でドメインを使いたいというニーズは当然ある。この「既存インフラを変えずに国際化対応する」という難題を解決したのがPunycodeだ。


RFC仕様の全体像と変遷

関連RFCの系譜

Punycodeの基盤となるRFC 3492「Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)」は、2003年3月にAdam M. Costello(UC Berkeley)により発行された。

IDNAフレームワークはその後大きく進化し、現在はIDNA2008と呼ばれる改訂版が主流となっている。

世代 RFC群 発行年 主要内容
IDNA2003 RFC 3490, RFC 3491, RFC 3492 2003年 初代IDNA仕様、Nameprep正規化、Punycode
IDNA2008 RFC 5890, RFC 5891, RFC 5892, RFC 5893 2010年 定義/フレームワーク、プロトコル、コードポイント、Bidi規則

IDNA2003 → IDNA2008 の主要変更点

IDNA2008ではIDNAbis Working Groupが仕様策定を担当した。主要な技術変更は以下の通り:

項目 IDNA2003 IDNA2008
Unicode依存 バージョン3.2固定 プロパティベースのルール(バージョン非依存)
正規化形式 NFKC NFC
マッピング処理 プロトコル内 プロトコル外(ローカル処理)
「ß」の扱い 「ss」にマッピング 独立文字として保持
「ς」の扱い 「σ」にマッピング 独立文字として保持

特に「ß」(Eszett)や「ς」(Greek Final Sigma)が独立文字として扱われるようになった点は、ドイツ語・ギリシャ語ドメインに実質的な影響を与えた。

実装上の注意: IDNA2003とIDNA2008では同じ入力文字列が異なるPunycode出力になる場合がある。レガシーシステムとの互換性を考慮する際は要注意。


Bootstringアルゴリズムの動作原理

ここからが本記事の核心部分だ。PunycodeはBootstringアルゴリズムの特定パラメータ実装である。

アルゴリズムの4つの柱

RFC 3492で定義されるこのアルゴリズムは、以下の4つの技術を組み合わせて効率的なエンコーディングを実現する:

1. 基本コードポイント分離

入力文字列中のASCII文字(0x00-0x7F)をそのまま出力先頭にコピーし、非ASCII文字が存在すればデリミタとしてハイフン「-」を追加する。

入力: bücher
      ↓
ASCII部分抽出: bcher
      ↓
デリミタ追加: bcher-

2. 挿入非ソートコーディング

非ASCII文字をコードポイントの数値順で処理する。同一スクリプト(例:ひらがな U+3040-U+309F)の文字が近接するため、差分値が小さくなるよう最適化される。

3. 可変長整数エンコーディング

閾値を使って終端を検出し、複数の整数を区切りなしで表現する。Base36(a-z: 0-25, 0-9: 26-35)を使用。

4. バイアス適応

各デルタ値のエンコード後にバイアスを再計算し、次のデルタの予測精度を高める。

Punycodeのパラメータ値

base        = 36
tmin        = 1
tmax        = 26
initial_bias = 72
initial_n   = 128  // 最初の非ASCII文字のコードポイント開始点
damp        = 700  // 初回ダンピング係数

【具体例】「bücher」の変換過程

ドイツ語で「本」を意味する「bücher」を例に、変換過程を追跡しよう。

Step 1: ASCII文字の抽出

bücher → bcher(ü以外のASCII文字を抽出)

Step 2: デリミタ追加

bcher → bcher-(非ASCII文字が存在するため)

Step 3: 非ASCII文字の処理

ü (U+00FC = 252) について:
- reduced_codepoint = 252 - 127 = 125
- 挿入位置 = 1(bの後)
- 挿入可能位置数 = 6
- デルタ = 6 × 124 + 1 = 745

Step 4: 可変長整数エンコード

745 → kva

最終出力

bcher-kva
ACEプレフィックス付き: xn--bcher-kva

「xn--」プレフィックスの謎

なぜ「xn--」なのか?

ACE(ASCII Compatible Encoding)プレフィックスの「xn--」は、RFC 3490標準化時にIANAによりランダム選定された。

「eXtended Names」の略称という説は誤りである。

選定時の考慮事項:

  • 「bl--」「bq--」など既存で使用済みのプレフィックスを除外
  • 将来のエンコーディング方式変更時に新プレフィックスへ移行可能とする設計
  • 既存DNSインフラを変更せずに国際化ドメイン名をサポート

この設計により、DNSサーバーやリゾルバは従来のASCII処理のまま動作し、変更不要となった。エレガントな後方互換性の確保だ。


IDNAフレームワークとToASCII/ToUnicode処理

処理の全体像

IDNAの核心はアプリケーション層での処理にある。

[ユーザー入力]          [DNS通信]           [表示]
日本語.example  →  xn--wgv71a.example  →  日本語.example
               ToASCII              ToUnicode

ブラウザ等のクライアントアプリケーションがToASCII操作を適用し、Punycode形式に変換してからDNSクエリを送信する。DNSサーバーは何も変わらない。

ToASCII操作の8ステップ

  1. ASCII判定: 全文字がASCII(0x00-0x7F)ならStep 3へスキップ
  2. Nameprep処理: マッピング(大文字→小文字)、NFKC正規化、禁止文字チェック、Bidi検証
  3. STD3 ASCII規則チェック: LDH(Letter-Digit-Hyphen)以外を禁止
  4. ASCII再判定: 正規化後に全ASCII化されていればStep 8へ
  5. ACEプレフィックス確認: 既に「xn--」で始まるならエラー
  6. Punycodeエンコード: Bootstringアルゴリズム適用
  7. ACEプレフィックス付加: 「xn--」を先頭に追加
  8. 長さ検証: 1-63コードポイント以内

Unicode正規化とNAMEPREP

IDNA2003ではStringprep(RFC 3454)のプロファイルである**NAMEPREP(RFC 3491)**を使用し、Unicode 3.2固定でNFKC正規化を適用する。

例:Å (U+212B, Angstrom Sign) 
  → Å (U+00C5, Latin Capital Letter A With Ring Above)
  → 同一コードポイントに統合

IDNA2008では正規化がNFCに変更され、マッピング処理はプロトコル外のローカル処理に分離された。これによりUnicodeバージョン依存が解消され、将来のUnicode更新への追従が容易になった。


日本語ドメインの実装と登録状況

JPRSの対応経緯

年月 イベント
2000年12月 JPRS設立
2002年4月 JPNICからJPドメイン名登録管理業務を移管
2003年7月 RFC準拠の日本語JPドメイン名登録管理サービス開始
2004年12月 日本語JPドメイン名ポータルサイト「日本語.jp」開設

使用可能な文字と制限

項目 内容
使用可能文字 全角ひらがな・カタカナ・漢字、半角英数字(A-Z, 0-9)、半角ハイフン、「・」「々」「ー」等
文字数制限 1-15文字(全角・半角関係なく)
登録資格 日本国内に住所がある方
登録数制限 なし

注意: 属性型JPドメイン(CO.JP、OR.JP等)では日本語ドメインは利用不可。

登録統計(2025年12月時点)

種別 登録件数
日本語汎用JPドメイン 80,729件
日本語都道府県型JPドメイン 1,261件
合計 約82,000件

汎用JPドメイン総数の約6.5%に相当する。思ったより少ない? それとも多い?

具体的な変換例

日本語ドメイン Punycode変換後
日本語.jp xn--wgv71a119e.jp
ドメイン名例.jp xn--eckwd4c7cu47r2wf.jp
シーマン.jp xn--xck1dvc8a.jp

変換確認には以下のツールが利用できる:


【セキュリティ】Homograph attackの脅威と対策

ここからはセキュリティの話。知らないと本当に怖い

Homograph attackとは

IDN Homograph attackは、異なるスクリプトの視覚的類似文字を悪用してドメインを偽装する攻撃である。

Latin 「a」 (U+0061)  vs  Cyrillic 「а」 (U+0430)
→ 多くのフォントで区別不能

これを悪用すると:

正規: apple.com
偽装: аррӏе.com(全文字キリル文字)
→ 見た目は同じだが、完全に別のドメイン

過去の主要攻撃事例

年月 事例
2001年12月 Microsoft.com偽装の概念実証(Technion大学研究者)
2005年2月 PayPal.com偽装(Shmooconで実演)
2017年4月 Apple.com偽装(xn--80ak6aa92e.com、Chrome 58で修正)
2024年 Spotify偽装フィッシングメール

Akamaiの2021年調査では、32日間で6,670のホモグラフIDNを検出し、毎日平均67の新規ホモグラフドメインがアクセスされている。

ブラウザの表示ポリシー比較

各ブラウザはHomograph attack対策として、特定条件下でPunycode表示にフォールバックする。

ブラウザ Punycode表示条件 特記事項
Chrome 混合スクリプト検出、whole-script confusables、Top 500ドメイン類似性 Lookalike URL警告機能搭載
Firefox TLDホワイトリスト外、スクリプト混合時 network.IDN_show_punycodeで常時Punycode表示可能
Safari 紛らわしいスクリプト(Cyrillic、Greek)は常にPunycode 最も厳格だが失敗率も高い
Edge Chromiumと同一アルゴリズム 2020年以降Chromiumベース

Chrome 51以降ではUTS 39「Highly Restrictive」プロファイルを採用し、Latin・Cyrillic・Greekの混合を禁止している。

衝撃の事実: USENIX Security 2021の研究によれば、全ブラウザで20.62%-44.46%の検出失敗率が存在する。完璧ではない。

推奨される対策

ユーザー向け

  • パスワードマネージャーの使用(ドメイン完全一致でのみ自動入力)
  • Firefoxでnetwork.IDN_show_punycode = trueに設定
  • 多要素認証の有効化

レジストリレベル

  • .рф(ロシア): キリル文字のみ許可
  • Identity Digital(旧Donuts): Unicode Confusablesテーブルに基づくブロッキング

【おまけ】Unicode正規化に起因する脆弱性

Punycodeとは直接関係ないが、Unicode正規化がセキュリティチェックに実行される場合、以下の脆弱性が発生しうる:

攻撃手法 悪用される文字 結果
SQLインジェクション U+FF07(全角アポストロフィ) NFKC正規化後に標準アポストロフィに変換
XSS U+FE64(小なり記号変種) HTML区切り文字に変換
パストラバーサル U+FF0E(全角ピリオド) 「.」に変換されディレクトリ操作可能

防御策: 入力受信直後に正規化を実行し、その後にセキュリティチェックを行う順序が重要。


おわりに

Punycodeは「既存のDNSインフラを変更せずに国際化対応を実現する」という設計目標を達成した技術標準である。

  • Bootstringアルゴリズムによる効率的なエンコーディング
  • ACEプレフィックスによる識別性
  • アプリケーション層での処理による後方互換性

この3要素が、20年以上にわたり世界中のIDN運用を支えてきた。

一方で、Homograph attackに代表されるセキュリティリスクは完全には解消されておらず、ブラウザ・レジストリ・ユーザーの多層防御が求められる。


「なんすか?それ??」と言われたあの日から、この記事が誰かの「なるほど!」につながれば幸いです。


参考文献・RFC一覧

RFC番号 タイトル 発行年
RFC 3454 Preparation of Internationalized Strings (Stringprep) 2002
RFC 3490 Internationalizing Domain Names in Applications (IDNA) 2003
RFC 3491 Nameprep: A Stringprep Profile for IDN 2003
RFC 3492 Punycode: A Bootstring encoding of Unicode for IDNA 2003
RFC 5890 IDNA: Definitions and Document Framework 2010
RFC 5891 IDNA: Protocol 2010
RFC 5892 The Unicode Code Points and IDNA 2010
RFC 5893 Right-to-Left Scripts for IDNA 2010
RFC 8264 PRECIS Framework 2017

略語一覧

略語 正式名称
IDNA Internationalized Domain Names in Applications
IDN Internationalized Domain Name
ACE ASCII Compatible Encoding
LDH Letter-Digit-Hyphen
IETF Internet Engineering Task Force
NFC/NFKC Normalization Form (Canonical/Compatibility) Composition
JPRS Japan Registry Services Co., Ltd.

💬 議論・フィードバック

この記事について質問・指摘・感想があれば、ぜひコメント欄か X(旧Twitter) でお知らせください。

「うちの現場ではこんな問題があった」「このRFCも読むべき」などの情報も大歓迎です!


最後に、GMOコネクトでは研究開発や国際標準化に関する支援や技術検証をはじめ、幅広い支援を行っておりますので、何かありましたらお気軽にお問合せください。

お問合せ: https://gmo-connect.jp/contactus/

13
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?