Punycodeとは
PunycodeはRFC 3492で定義されたエンコード構文で、Unicode文字列をDNSが扱える限定されたASCII文字集合へ変換します。既存のDNS基盤であらゆる言語のドメイン名を機能させる、国際化ドメイン名(IDN)の技術的基盤です。
Punycodeが解決する問題
Domain Name Systemは1980年代にASCII文字だけを前提として設計されました。DNSラベルに使えるのは次の文字だけです。
- 小文字(a-z)
- 数字(0-9)
- ハイフン(-)
この制限は、主にラテン文字を使わない膨大な数のインターネット利用者を排除していました。Punycodeは任意のUnicode文字列を有効なASCIIへエンコードして、この差を埋めます。
Punycodeエンコードの仕組み
PunycodeはASCII文字を保持しながら、非ASCII文字をコンパクトなASCII表現にする巧妙なアルゴリズムを使います。
エンコード処理
1. 文字を分離する: ASCII文字と非ASCII文字に分ける
2. ASCIIをコピーする: ASCII文字を元の位置に保つ
3. 非ASCIIをエンコードする: 一般化された可変長整数エンコードを使う
4. 接頭辞を付ける: Punycodeエンコードを示す「xn--」を先頭に付ける
例
| 元の文字列(Unicode) | エンコード後(Punycode) |
|---|---|
| münchen | xn--mnchen-3ya |
| 北京 | xn--fiqs8s |
| münchen.de | xn--mnchen-3ya.de |
| 中文.com | xn--fiq228c.com |
| café.com | xn--caf-dma.com |
「xn--」接頭辞
「xn--」接頭辞はACE(ASCII Compatible Encoding)接頭辞と呼ばれます。DNSリゾルバーやアプリケーションに、そのラベルがPunycodeでエンコードされた内容を含むことを知らせます。この接頭辞には次の特徴があります。
- 常に小文字である
- 通常のASCIIドメイン名には現れない
- 対応ソフトウェアでUnicodeデコードを開始する
実際の利用
ブラウザーの処理
現在のブラウザーはPunycodeを自動的に処理します。
User types: 中文.com
Browser sends: xn--fiq228c.com (to DNS)
Browser displays: 中文.com (in address bar)
開発者による実装
JavaScript(Node.js)による実装:const punycode = require('punycode/');
// Encode to Punycode
const encoded = punycode.toASCII('münchen.de');
// Result: xn--mnchen-3ya.de
// Decode from Punycode
const decoded = punycode.toUnicode('xn--mnchen-3ya.de');
// Result: münchen.de
Python:
domain = 'münchen.de'
encoded = domain.encode('idna').decode('ascii')
# Result: xn--mnchen-3ya.de
URLの処理
IDNを含むURLを扱う場合:
// URL API handles Punycode automatically
const url = new URL('https://中文.com/path');
console.log(url.hostname); // xn--fiq228c.com
console.log(url.href); // https://xn--fiq228c.com/path
セキュリティへの影響
Punycodeは任意のUnicode文字を表現できるため、セキュリティリスクも生じます。
見た目を利用したなりすまし
攻撃者は、正規サイトと同じように見えるドメインを登録できます。
аррlе.com (Cyrillic 'а' and 'р')
apple.com (Latin letters)
フォントによっては同じ表示になりますが、実際には異なるドメインです。
ブラウザーの保護機能
なりすましに対処するため、ブラウザーは次の保護を実装しています。
1. 混在スクリプト検出: 複数の文字体系を不審に混ぜたドメインではPunycodeを表示する
2. 類似文字検出: ASCIIに見える文字を使うドメインに警告する
3. 許可リスト: 信頼されたTLDでだけUnicode表示を許可する
APIでPunycodeを扱う
ドメインツールを構築するときは、次の方針にします。
Punycodeを常に保存する: 一貫性とデータベース索引のため、内部ではASCII形式を使う。 両方の形式を受け付ける: 利用者がUnicodeとPunycodeのどちらでも入力できるようにし、必要に応じて変換する。 Unicodeを表示する: ユーザーインターフェースでは、人が読める形式を示す。function normalizeDomain(input) {
const punycode = require('punycode/');
// Convert to lowercase Punycode for internal use
return punycode.toASCII(input.toLowerCase());
}
Punycodeは多くの利用者からは意識されませんが、国際化ウェブアプリケーションを構築する開発者には不可欠な知識です。