うたカモ 技術知識・用語集

技術知識・用語集

ドメイン・DNS

Punycode

post:

Punycodeは日本語などを含むUnicodeの文字列を、ASCIIの文字列で表す符号化方式です。 IDN(国際化ドメイン名)をDNSで扱えるASCIIの名前に変換するときに使われます。

Punycodeとは?

Punycodeは、2003年にRFC 3492として定められました。 Webなどで使うホスト名には、英字・数字・ハイフンを基本とする制約があります。 IDNでは文字の確認や入力の変換を行ったうえでPunycodeを使い、既存のDNSと互換性のある名前に変換します。 Punycodeで変換した文字列は、元のUnicodeの文字列に戻せます。

RFC 3492は、PunycodeをIDNのために設計した方式と位置付けています。 そのため、Punycodeを目にするのはほとんどの場合ドメイン名の中です。

変換の例

次の表は、いくつかの文字列をPunycodeで変換した結果です。

表が画面に収まらない場合は横にスクロールできます。

元の文字列Punycode特徴
うたカモ用語集p8j7bymyh4432a350ao1rひらがな・カタカナ・漢字を含む例
用語集jnyu17b00iASCIIの文字がないため、全体が符号化された文字列になる
アプリcckvf7b元の文字列の意味は、変換結果から読み取れない

これらの例をIDNのラベルとして使うときは、変換結果の先頭にxn--を付けます。 例えばうたカモ用語集.exampleはxn--p8j7bymyh4432a350ao1r.example、用語集.exampleはxn--jnyu17b00i.exampleになります。

Punycodeの変換の仕組み

Punycodeは、ASCIIの文字とそれ以外の文字を分けて記録します。うたカモ用語集の変換は、おおまかに次の順で行われます。

  1. 元の文字列からASCIIの文字を取り出す。この例にはASCIIの文字がない
  2. ASCIIの文字が1つ以上あれば区切りのハイフン-を付けるが、この例では付かない
  3. 残りの文字の種類と位置を数値で表し、英小文字と数字を使って書く(p8j7bymyh4432a350ao1r)

この変換結果には、元の文字を復元するための情報が含まれています。 元に戻すときは符号化された数値を読み取り、文字を順に挿入してうたカモ用語集を復元します。

PunycodeではASCIIの文字をそのままコピーするため、大文字や記号も出力に残ります。例えばABCうはABC-263bになります。 一方、263bのように数値を表す部分は英字の大文字・小文字を区別せずに読み取れます。

符号化の手順では、挿入する位置と文字の情報をまとめて1つの数値として扱い、短い文字列で表せるように調整しています。 そのため、同じ文字が続く場合や近い範囲の文字が並ぶ場合は、変換後の文字列が比較的短くなります。

Punycodeとxn--の関係

xn--は、Punycodeの一部ではありません。IDNの仕組みであるIDNAが定めた目印で、後ろにPunycodeで変換したラベルが続くことを示します。

元のラベル
うたカモ用語集
Punycodeの変換結果
p8j7bymyh4432a350ao1r
xn--を付けたA-label
xn--p8j7bymyh4432a350ao1r
うたカモ用語集をPunycodeでp8j7bymyh4432a350ao1rに変換し、先頭にxn--を付けてxn--p8j7bymyh4432a350ao1rとする例
Punycodeの変換結果にxn--を付けてA-labelにする例

表が画面に収まらない場合は横にスクロールできます。

呼び方例説明
U-labelうたカモ用語集利用者が読むUnicodeの形のラベル
Punycodeの結果p8j7bymyh4432a350ao1r符号化しただけの文字列。単独ではIDNのラベルと区別できない
A-labelxn--p8j7bymyh4432a350ao1rxn--を付け、DNSで使うASCIIの形のラベル

p8j7bymyh4432a350ao1rだけでは、Punycodeで変換した結果なのか、もともとそういう名前なのかを判断できません。 先頭にxn--があれば、ブラウザなどのソフトウェアはそのラベルをUnicodeの形に戻して表示できます。

ただし、xn--で始まる文字列がすべて正しいA-labelとは限りません。 Punycodeとして復元できないものや、復元した結果にIDNで使えない文字が含まれるものは正しいA-labelとして扱われません。 例えば、ブラウザと同じ規則でURLを解析するJavaScriptのURLは、https://xn--a.example/を不正なURLとしてエラーにします。

ドメイン名の長さとPunycode

DNSでは、ドメイン名の1つのラベルを63オクテット(ASCIIでは63文字)以内にする必要があります。 IDNの場合、この制限はPunycodeで変換し、xn--を付けたA-labelの長さに適用されます。

例えば6文字のドメイン名例は、A-labelではxn--eckwd4c7cu47r2wfの20文字になります。 変換後の長さは文字の種類や並びによって変わるため、日本語のラベルに使える文字数は一律に決まりません。

xn--で始まるドメインを見かけたとき

アドレスバーやログにxn--で始まるドメインが表示されていても、それだけで危険なサイトという意味ではありません。 日本語のドメインなど、IDNを正しくASCIIで表した結果であることが多いためです。

一方で、見た目の似た文字を使って正規のサイトに見せかけるIDNホモグラフ攻撃では、Punycodeの形がなりすましを見抜く手がかりになります。 例えば、先頭のaだけをキリル文字のа(U+0430)に置き換えたаpple.exampleは、画面ではapple.exampleとほとんど区別できません。 しかし、A-labelにするとxn--pple-43d.exampleとなり、ASCIIだけのapple.exampleとは別の名前であることが分かります。

ブラウザによっては、このような紛らわしい名前をあえてA-labelのまま表示します。 Unicodeの表示だけでは紛らわしい文字を見分けられないこともあります。 ログインや決済を行うサイトは、保存したブックマークや公式アプリから開く方法も有効です。 ドメイン名を調べるときは、ASCII形式も含めて正規の名前と照合します。

プログラムでPunycodeを扱うときの注意

ドメイン名を変換するときは、Punycodeの処理だけを直接呼び出すのではなく、IDNAの変換を行う関数を使うのが基本です。 ブラウザなどではPunycodeを適用する前に、UTS #46などの規則に従って大文字や全角文字をそろえ、文字列を確認します。 IDNA2008自体は、このような入力の変換を必須としていません。 Punycodeの処理だけを使うと、こうした入力の変換や確認が抜け、ブラウザとは異なる名前を作ってしまうことがあります。

例えばNode.jsでは、組み込みのpunycodeモジュールは非推奨とされています。ドメイン名の変換にはurl.domainToASCII()やURLを使えます。 Pythonの標準のidnaコーデックは古いIDNA2003に従うため、現在のブラウザと変換結果が異なる場合があります。 IDNA2003とIDNA2008の違いはIDNの解説で扱っています。

参考資料

技術知識・用語集に戻る