この記事を始める前に、まずは base64 エンコードされた文字列のグループを見てみましょう。
ZG==
YY==
aW==
ZF==
cm==
aM==
b2==
dc==
c2==
Zf==
デコードされた内容は daidrhouse で、問題はなさそうです。しかし、よく見ると、1 行目と 4 行目のデコード結果はどちらも d ですが、内容が異なるようです。
通常の base64 エンコードでは、daidrhouse は以下の結果になります。
ZA==
YQ==
aQ==
ZA==
cg==
aA==
bw==
dQ==
cw==
ZQ==
明らかに、前者と比較して、各 base64 の 2 番目の文字が変更されていますが、デコード後の内容は変わりません。これは、base64 エンコードの原理について話す必要があります。
base64 とは#
その名前が示すように、base64 エンコードは、バイナリコンテンツをエンコードするために 64 個の ASCII 文字を基にするエンコーディング方法です。おそらく皆さんは、ウェブページで base64 エンコードされた埋め込み画像を見たことがあるでしょうし、QQ 音楽が歌詞ファイルを転送する際にも base64 エンコードを使用しているかもしれません。バイナリを ASCII 文字にエンコードすることで、データが特定のシナリオで読みやすく、転送しやすくなります。もちろん、すべてのバイナリをたった 64 文字で「凝縮」して表現することは、容量の面で妥協する必要があります。文字はエンコード後に 1/3 倍に増加しますが、その理由については後で説明します。
インデックステーブル#
base64 には、64 個の ASCII 文字を並べ替えてインデックスを割り当てた標準のエンコードテーブルがあります。
インデックス | 文字 | インデックス | 文字 | インデックス | 文字 | インデックス | 文字 |
---|---|---|---|---|---|---|---|
0 | A | 16 | Q | 32 | g | 48 | w |
1 | B | 17 | R | 33 | h | 49 | x |
2 | C | 18 | S | 34 | i | 50 | y |
3 | D | 19 | T | 35 | j | 51 | z |
4 | E | 20 | U | 36 | k | 52 | 0 |
5 | F | 21 | V | 37 | l | 53 | 1 |
6 | G | 22 | W | 38 | m | 54 | 2 |
7 | H | 23 | X | 39 | n | 55 | 3 |
8 | I | 24 | Y | 40 | o | 56 | 4 |
9 | J | 25 | Z | 41 | p | 57 | 5 |
10 | K | 26 | a | 42 | q | 58 | 6 |
11 | L | 27 | b | 43 | r | 59 | 7 |
12 | M | 28 | c | 44 | s | 60 | 8 |
13 | N | 29 | d | 45 | t | 61 | 9 |
14 | O | 30 | e | 46 | u | 62 | + |
15 | P | 31 | f | 47 | v | 63 | / |
混乱を避けるために(リンクなど)、+
/
の代わりに .
_
を使用することもあります。
エンコード方法#
base64 は、3 バイト(24 ビット)を 1 グループとして処理します。3 バイトに満たない場合は、0 でパディングし、末尾に =
を使用してパディングされたバイト数を示します。そして、6 ビットごとに 1 グループとし、24 ビットを 4 つの 6 ビットのバイナリにエンコードします。この時、6 ビットのバイナリは合計で 26=642^6=6426=64 の組み合わせがあり、ちょうど 64 個の文字で表現できます。(これがエンコード後のサイズが 1/3 増加する理由でもあります)
例を挙げる#
ステガノグラフィの原理#
base64 のデコード時には、文字列の末尾の =
の数に応じて、対応するバイト数を削除します。おそらく気づいているかもしれませんが、1 バイトまたは 2 バイトの場合、4 ビットまたは 2 ビットのバイナリがデコード時に無視されることがあります。以下の図の赤いマークです。
赤いマークのバイナリはエンコードできますが、デコード時には無視されます。これらの位置の内容を変更しても、元のデータには影響しません。
問題を解決する#
さて、記事の冒頭の問題を解決してみましょう。その base64 エンコードされた文字列には何が隠されているのでしょうか?
赤いマークのバイナリビットをすべて結合すると、最終的な結果である hello が得られます。