Help us understand the problem. What is going on with this article?

その透明な文字に混じらず、見つけ出すんだ。

More than 5 years have passed since last update.

経緯

日常で最もよく遭遇する透明な文字 (Whitespace など) は、いわゆる (全角|半角) スペースそして改行 (CR, LF) タブなどがある。

Unicode にはたくさんの透明な文字が存在する。以下の文字列は "AB" ではない。
なんと、 A と B の間に WORD JOINER というゼロ幅の文字が潜んでいる!

どうみてもAB
A⁠B
実際のところ
A{U+2060}B

他にも、左右のテキストの順序を変えることができる文字 RLM など、
色々と複雑な制御文字がある。


このような特性を利用してファイル名やパス名などを偽装する攻撃 が存在する。

Unicode の透明な文字を知っておくことは、セキュリティにおいても重要だと思う。

このような攻撃に使われそうな(使えそうな)、透明な文字一覧を探してみた。
The Unicode Consortium の配布する資料、 Wikipedia 、自分が Python で書いた簡易的な探し方などを用いて探した。

まず結果

tsv
U+9 
U+A 
U+B 
U+C 
U+D 
U+20    SPACE
U+85    
U+A0    NO-BREAK SPACE
U+2000  EN QUAD
U+2001  EM QUAD
U+2002  EN SPACE
U+2003  EM SPACE
U+2004  THREE-PER-EM SPACE
U+2005  FOUR-PER-EM SPACE
U+2006  SIX-PER-EM SPACE
U+2007  FIGURE SPACE
U+2008  PUNCTUATION SPACE
U+2009  THIN SPACE
U+200A  HAIR SPACE
U+2028  LINE SEPARATOR
U+2029  PARAGRAPH SEPARATOR
U+202F  NARROW NO-BREAK SPACE
U+205F  MEDIUM MATHEMATICAL SPACE
U+3000  IDEOGRAPHIC SPACE
U+180E  MONGOLIAN VOWEL SEPARATOR
U+200B  ZERO WIDTH SPACE
U+200C  ZERO WIDTH NON-JOINER
U+200D  ZERO WIDTH JOINER
U+2060  WORD JOINER
U+2061  FUNCTION APPLICATION
U+2062  INVISIBLE TIMES
U+2063  INVISIBLE SEPARATOR
U+2064  INVISIBLE PLUS
U+3164  HANGUL FILLER
U+FFA0  HALFWIDTH HANGUL FILLER
U+FEFF  ZERO WIDTH NO-BREAK SPACE

釈明・免責

私は Unicode のプロフェッショナルではありません。この記事の内容は無保証です。

以下は、延々と経緯

まずは困った時の Wikipedia

ここに一覧がある。 http://en.wikipedia.org/wiki/Whitespace_character

このページは Unicode 6.3.0 の http://www.unicode.org/Public/6.3.0/ucd/PropList.txt
を元に作られているので、ほぼ網羅されている。

レンダリングして調べる

Python の Pillow を使い、画像に文字を実際にレンダリングしてみた。
簡単に説明すると、元の画像と文字をレンダリングした画像とに変化がなければ、透明であると定義した。
それを Unicode の全文字に対して試した。
フォントは最大のグリフ数を誇ると思われる、 GNU Unifont を使用した。
Unicode の PUA (Private Use Area) は除外した。

この方式では、 Unicode 上で制御文字として定義されている文字は、むしろ列挙されない。
以下の画像のように、制御文字用のグリフがそのまま描画されるので透明でなくなる。
http://unifoundry.com/pub/unifont-7.0.06/unifont-7.0.06.bmp

find_invisible.py
import unicodedata

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont


FONT = ImageFont.truetype('./unifont-7.0.06.ttf', 32)


def main():
    assert_principles()

    for char in invisible_characters():
        try:
            name = unicodedata.name(char)
        except ValueError:
            name = ''

        line = 'U+{:X}\t{}'.format(ord(char), name)
        print(line)


def invisible_characters():
    '''空白として描画された文字のみを返却する'''
    empty_draw_bytes = letter_draw_bytes(' ')
    for char in unicode_characters():
        draw_bytes = letter_draw_bytes(char)
        if draw_bytes == empty_draw_bytes:
            yield char


def unicode_characters():
    '''Unicode コードポイントに含まれる文字全てを返すジェネレータ
ただし、 PUA (Private Use Area) は含めない。
    '''

    for codepoint in range(0, 0xE000):
        yield chr(codepoint)

    for codepoint in range(0xF8FF + 1, 0xF0000):
        yield chr(codepoint)

    for codepoint in range(0xFFFFD + 1, 0x100000):
        yield chr(codepoint)

    for codepoint in range(0x10FFFD + 1, 0x10FFFF):
        yield chr(codepoint)


def letter_draw_bytes(char):
    '''空白の画像に文字を描画した際の bytes を取得する'''

    image = Image.new('RGB', (32, 32), (255, 255, 255))
    draw = ImageDraw.Draw(image)
    draw.text((0, 0), char, font=FONT, fill='#000')
    return image.tobytes()


def assert_principles():
    '''以下の原則が成立することをいくつかの例でチェック'''
    f = letter_draw_bytes

    assert f('あ') == f('あ')  # 同じ文字の bytes は同じ

    assert f('') == f(' ') == f(' ')  # スペースは空文字列と同じ bytes になる

    assert f('あ') != f('い')  # 見た目が異なる文字の bytes は異なる


if __name__ == '__main__':
    main()

結果

U+20    SPACE
U+A0    NO-BREAK SPACE
U+2000  EN QUAD
U+2001  EM QUAD
U+2002  EN SPACE
U+2003  EM SPACE
U+2004  THREE-PER-EM SPACE
U+2005  FOUR-PER-EM SPACE
U+2006  SIX-PER-EM SPACE
U+2007  FIGURE SPACE
U+2008  PUNCTUATION SPACE
U+2009  THIN SPACE
U+200A  HAIR SPACE
U+205F  MEDIUM MATHEMATICAL SPACE
U+3000  IDEOGRAPHIC SPACE
U+FFA0  HALFWIDTH HANGUL FILLER

このうち

U+FFA0  HALFWIDTH HANGUL FILLER

だけが未知だった。 HALFWIDTH だけが透明として検知できたのだが、 HALFWIDTH でないものも、 GNU Unifont 以外では透明であったので、一応追加した。

U+3164  HANGUL FILLER

The Unicode Consortium の配布物

The Unicode Consortium の配布物を延々と人力で調べることにした。
例えば DerivedCoreProperties など

その結果 U+2061 ... U+2064 などを発見した。
おそらく数学用の制御文字(?)。詳細は不明。

U+2060 WORD JOINER のすぐそばにあるのに、ほとんど言及されてない。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした