Punycode によるホモグラフ攻撃例とその回避

no extension

たとえば,以下の Go 言語コードで2つの “apple" を考える(元ネタはここ)。

package main

import "fmt"

func main() {
    for _, value := range "apple" {
        fmt.Printf("%#U\n", value)
    }
    fmt.Println()
    for _, value := range "аррӏе" {
        fmt.Printf("%#U\n", value)
    }
}

見た目では分かりにくいかもしれないが,最初の “apple" は US ASCII で2番目の “аррӏе" はキリル文字なんだそうだ1。 このコードの実行結果は以下の通り。

U+0061 'a'
U+0070 'p'
U+0070 'p'
U+006C 'l'
U+0065 'e'

U+0430 'а'
U+0440 'р'
U+0440 'р'
U+04CF 'ӏ'
U+0435 'е'

現在,国際化ドメイン名(Internationalized Domain Name; IDN)については xn-- から始まる punycode を使った表記が認められている。 更に punycode を使った「ホモグラフ攻撃(homograph attack)」については以前から議論があり,少なくとも複数の言語の文字が混在する場合はブラウザ側で Unicode 文字による表記がキャンセルされる。 たとえば xn-pple-43d.com は Unicode 表記では аpple.com (先頭の а がキリル文字)だが, Chrome や Firefox といった主要ブラウザでは punycode のまま xn-pple-43d.com と表記される(試さないように)。

しかし複数言語が混在しない場合,つまり最初に挙げたキリル文字だけの “аррӏе" のような場合にはこの制約は効かない。 その言語による真っ当な名前なのかホモグラフ攻撃なのか見分けがつかないからである。 たとえば xn--80ak6aa92e.comаррӏе.com だが “аррӏе" の部分は全てキリル文字なので主要ブラウザでも аррӏе.com と表示される。

PoC として https://www.xn--80ak6aa92e.com/ が用意されているので,皆さんが使っているブラウザで(証明書の詳細情報も含めて)ドメイン名がどう表示されるか確認して欲しい。

さて,この手の攻撃の回避法だが, Chrome についてはバージョン 58 以降であれば xn--80ak6aa92e.compunycode 表記になる。 どういうロジックなのかは不明2。 Firefox については,設定の network.IDN_show_punycode 項目3 を true にすれば強制的に punycode 表記になる。

個人的には国際化ドメイン名は要らんのじゃないかと思うのだが,どうなんだろうねぇ。 果てしなく紛らわしい。

ブックマーク


  1. キリル文字の “аррӏе" の並びに単語としての意味はない。ここでは単純に字形の類似性のみに着目して考える。 ↩︎

  2. Firefox のように punycode をまるっと無視するのではないようだ。たとえば Chrome 58 でも「情報処理試験.jp(xn--n9q36mh1hnxuksz7wt.jp)」はちゃんと Unicode 表記になる。 ↩︎

  3. about:config から設定する。 “punycode" で検索すれば一発で出てくる。 ↩︎