base64コマンド入門|テキストに変換して安全に運ぶ基本

base64コマンド入門|テキストに変換して安全に運ぶ基本

API のドキュメントを読んでいると、画像や認証情報が「base64 でエンコードして送れ」と指示されている場面によく出会います。

ツールが勝手にやってくれるので、意味を知らないまま通り過ぎてきた方も多いはず。

けれど中身を知らないと、改行が混ざって認証に失敗したときに手が止まります。

幸い、変換そのものは base64 コマンド1本で済むのです。

手元の環境(GNU coreutils 8.32)で実際に叩いた結果を並べながら、押さえておきたい要点を整理してみました。

base64とは「バイナリをテキストに直す変換」

まず、base64 は圧縮でも暗号化でもありません。

どんなバイト列も、64種類の文字だけで書き直すための変換規則です。

なぜテキストに直す必要があるのか

メールのヘッダや JSON、HTTP のヘッダといった通り道は、もともとテキストを運ぶために作られています。

そこへ画像や実行ファイルのような生のバイト列を流し込むと、制御文字が途中で解釈されて壊れることがある。

そこで、途中の経路が絶対に誤解しない安全な文字だけに置き換えてから運ぶわけです。

使われるのは英大小文字・数字・記号2つの計64種類と、長さ調整のための = という組み合わせ。

3バイトを4文字に置き換える

変換の中身は、24ビット(3バイト)をまとめて6ビット×4個に切り直す操作です。

6ビットで表せるのは64通りなので、ちょうど64種類の文字に1対1で対応する。

結果として、データ量はおよそ3分の4、つまり約33%増えるという性質が生まれます。

実際に300バイトのファイルで試すと、変換後は400文字になりました。

$ wc -c < s.bin
300
$ base64 -w0 s.bin | wc -c
400

基本の使い方はエンコードとデコードの2つだけ

覚えるオプションはごく少なく、引数なしでエンコード、-d でデコードという対になっています。

標準入力とファイルのどちらでも動く

パイプで流し込む形がいちばん手軽でしょう。

$ echo -n 'hello world' | base64
aGVsbG8gd29ybGQ=
$ echo -n 'hello world' | base64 | base64 -d
hello world

マルチバイト文字もバイト列として扱われるため、UTF-8 の日本語がそのまま往復します。

$ printf 'かもめ' | base64
44GL44KC44KB
$ printf 'かもめ' | base64 | base64 -d
かもめ

ファイルを渡すときは引数に置くだけで、結果は標準出力へ流れる。

base64 t.txt > t.b64 のようにリダイレクトすれば、そのままファイルとして残せます。

改行の落とし穴を -w0 で塞ぐ

ここが実務でいちばん引っかかるところ。

GNU 版の base64 は、既定で76文字ごとに改行を入れる仕様になっています。

メール用の規格に由来する親切ですが、JSON の値や HTTP ヘッダに入れると改行が悪さをする。

そこで、1行に収めたいときは -w0 を付けます。

$ head -c 100 /dev/urandom | base64 | head -3
8D1dP83CRD5zhFg2jogFNbcwBR5QjJo5rZZIFrU/74rzy9WdhlY+Qianu0+6cQ5wZ76+BMxYDLf3
2yarMBqeQsFN2dvQ7AsQryin6JR7O2xj0ZIG4+32LdctMYcnTDClxw9bdQ==
$ head -c 100 /dev/urandom | base64 -w0
EwhXY69XS5XVdr8ZVgv9XwhHtDn0e43tAmtv2toG9X4B/T6n22ySMSKREVbeMyJzxPI4hi2X15iF

なお -w0 は GNU 版のオプションなので、環境によっては受け付けないことがあります(手元で確認できたのは GNU coreutils 8.32 のみ)。

実務で当たる場面と、勘違いしやすい点

コマンドの形が分かると、次に気になるのは「どこで使うのか」ではないでしょうか。

Basic 認証ヘッダと data URI

HTTP の Basic 認証は、ユーザー名とパスワードを : でつないで base64 にした文字列を送る仕様です。

$ printf 'user:pass' | base64
dXNlcjpwYXNz

HTML や CSS に画像を埋め込む data URI も同じ発想で、data: の後ろにメディアタイプと base64 文字列を並べる。

小さなアイコンを1リクエスト減らすために埋め込む、といった使い方をします。

暗号化ではないので秘密は隠せない

上の例を見て分かるとおり、base64 -d を通せば誰でも元に戻せる。

base64 は「読めなくする」技術ではなく「運べる形にする」技術で、秘密の保護はまったく担っていません。

「なんとなく読めないから安全そう」と感じたら、そこは危険信号。

パスワードやトークンを base64 にしただけで安心してしまう事故は、レビューでも見かける典型例でしょう。

URLに入れるときは別の変換表を使う

標準の base64 は +/ を使うため、URL やファイル名に入れると意味が変わってしまいます。

そこで RFC では、この2文字を -_ に置き換えた URL セーフ版が別途定められている。

同じ3バイトを変換すると、次のように出力が変わりました。

$ printf '\xfb\xff\xfe' | base64
+//+
$ python -c "import sys,base64;print(base64.urlsafe_b64encode(sys.stdin.buffer.read()).decode())"
-__-

JWT やクエリパラメータで見かける base64 風の文字列は、この URL セーフ版であることが多いと覚えておくと読み解きやすくなります。

最後に

base64 は、バイナリをテキストの通り道に流すための「詰め替え作業」でした。

3バイトを4文字にするので約33%太る、既定で76文字ごとに改行が入る、そして暗号化ではない。

この3点さえ頭にあれば、API ドキュメントの指示に迷わず従えるはず。

バイト列そのものを目で確かめたいときや、転送前後で中身が変わっていないかを検証したいときは、こちらの記事もあわせてどうぞ。

od・xxd入門|ファイルの中身をバイト単位で確かめる
sha256sum入門|ファイルの中身が同じか確かめる

以上です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA