14
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

隠し情報をゼロ幅文字エンコードする仕組み

Last updated at Posted at 2018-04-11

はじめに

ゼロ幅文字にエンコードした隠し情報で、文書をリークしたメンバーを特定 | 秋元@サイボウズラボ・プログラマー・ブログ
http://developer.cybozu.co.jp/akky/2018/04/leaker-detection-by-zero-width-characters/

が、とても興味深かったのでRubyで写経してみました。

どういう仕組み?

簡単に書くと次のようになります。

v = "a".ord.to_s(2)             # => "1100001"
v = v.tr("01", "\u{200B 200C}") # => "‌‌​​​​‌"
v = v.tr("\u{200B 200C}", "01") # => "1100001"
v.to_i(2).chr                   # => "a"
  1. 文字を二進数にする
  2. 0 と 1 を見えない文字に変換する
  3. 見えない文字を 0 と 1 に変換する
  4. 二進数から文字に変換

たったこれだけのことですがアイデアをすぐに適用できるのはすごいなあと感心しました。

実際に埋め込んで復元するまでの流れ

埋め込みたい秘密の文字列を準備

str = "ぱくった人(id:123)"

もっとちゃんと作る場合には、改竄されてもわかるようにハッシュなどにした方がよいとのことです。

見えない文字列に変換

bin = str.codepoints.collect { |code|
  code.digits(2).collect { |bit|
    bit.zero? ? "\u200B" : "\u200C"
  }.join
}.join("\u200D")

bin.size                        # => 133
bin                             # => "‌​​​‌‌‌​​​​​‌‌‍‌‌‌‌​​‌​​​​​‌‌‍‌‌​​​‌‌​​​​​‌‌‍‌‌‌‌‌​‌​​​​​‌‌‍​‌​‌‌‌​‌​‌‌‌​​‌‍​​​‌​‌‍‌​​‌​‌‌‍​​‌​​‌‌‍​‌​‌‌‌‍‌​​​‌‌‍​‌​​‌‌‍‌‌​​‌‌‍‌​​‌​‌"
  • U+200B, U+200C, U+200D の3つを使いました
  • 本家では4つ使ってますがまだ意図がよくわかってません
  • bin 変数がゼロ幅の文字列で、ブラウザ上では空文字列に見えます
  • Emacsだと1文字1ドットぐらいですが何かいるのが確認できます

任意の文字列に埋め込む

text = "相手の防#{bin}衛ラインを乱したりして、得意の接近戦に持ち込もう"
text # => "相手の防‌​​​‌‌‌​​​​​‌‌‍‌‌‌‌​​‌​​​​​‌‌‍‌‌​​​‌‌​​​​​‌‌‍‌‌‌‌‌​‌​​​​​‌‌‍​‌​‌‌‌​‌​‌‌‌​​‌‍​​​‌​‌‍‌​​‌​‌‌‍​​‌​​‌‌‍​‌​‌‌‌‍‌​​​‌‌‍​‌​​‌‌‍‌‌​​‌‌‍‌​​‌​‌衛ラインを乱したりして、得意の接近戦に持ち込もう"

埋め込んだのにブラウザ上では見えていないことがわかります。

埋め込んだ文字列から見えない文字列を抽出

bin2 = text[/[\u200B-\u200D]+/]

埋め込まれているのは一箇所だけと仮定しています。

見えない文字列から秘密の文字列に復元

bin2                            # => "‌​​​‌‌‌​​​​​‌‌‍‌‌‌‌​​‌​​​​​‌‌‍‌‌​​​‌‌​​​​​‌‌‍‌‌‌‌‌​‌​​​​​‌‌‍​‌​‌‌‌​‌​‌‌‌​​‌‍​​​‌​‌‍‌​​‌​‌‌‍​​‌​​‌‌‍​‌​‌‌‌‍‌​​​‌‌‍​‌​​‌‌‍‌‌​​‌‌‍‌​​‌​‌"

str2 = bin2.split("\u200D").collect { |str|
  str.chars.collect.with_index { |char, i|
    (char == "\u200B" ? 0 : 1) << i
  }.sum.chr("UTF-8")
}.join

str2                            # => "ぱくった人(id:123)"

正しく復元できました。

この仕組みがあれば文章のコピペを追跡しやすくなります。自衛のためのChrome拡張もありますがそれでも効果はあると思います。最近だと個人運営のゲーム攻略サイトの盗用対策として活用するのもおもしろそうです。

参照

Be careful what you copy: Invisibly inserting usernames into text with Zero-Width Characters
https://medium.com/@umpox/be-careful-what-you-copy-invisibly-inserting-usernames-into-text-with-zero-width-characters-18b4e6f17b66

ゼロ幅文字にエンコードした隠し情報で、文書をリークしたメンバーを特定 | 秋元@サイボウズラボ・プログラマー・ブログ
http://developer.cybozu.co.jp/akky/2018/04/leaker-detection-by-zero-width-characters/

その透明な文字に混じらず、見つけ出すんだ。 - Qiita
https://qiita.com/kitsuyui/items/12db383f5e5971f32b08

Whitespace character - Wikipedia
https://en.wikipedia.org/wiki/Whitespace_character

chpmrc/zero-width-chrome-extension: Replace scary zero-width characters with funny emojis
https://github.com/chpmrc/zero-width-chrome-extension

14
9
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
14
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?