16文字のIDは、何を表している?Base64とランダム値を分ける
share&doのID生成は、ランダムな12バイトをURL向けのBase64で表します。既知の架空バイト列を往復変換し、文字数、表記、予測しにくさが別の話であることを確かめました。
CONTENTSこの記事の目次6項目
TODOのIDを作る処理を見ると、12バイトを用意しているのに、結果は16文字になっていました。どこで増えたのでしょうか。既知のバイト列を同じ方式で変換してみると、文字列から元のバイト列へ戻せました。長い英数字は、暗号化された本文とは限りません。
ランダムに作る処理と、文字に表す処理
TodoIdGeneratorは、まず12バイトの領域を作り、SecureRandomで値を入れます。その後、URL向けのBase64エンコーダーへ渡し、末尾のpaddingを付けない方式で文字列にします。 前半はIDの材料を作る処理、後半はその材料を文字で表す処理です。利用者名、登録時刻、TODO本文を詰め込んで変換するコードではありません。二つの段階を分けると、16文字の意味が追いやすくなります。
12バイトは96ビット。6ビットずつ文字へ
1バイトは8ビットなので、12バイトは96ビットです。Base64は6ビットごとに一文字で表すため、この長さでは96÷6で16文字になります。 URL向けの方式は、通常のBase64で使う半角の「+」や「/」の代わりに、半角のハイフン(-)とアンダースコア(_)を使う文字集合です。JavaのgetUrlEncoderとwithoutPaddingを使う指定を確認しました。文字数が増えても、元の情報が増えたり、秘密になったりしたわけではありません。
既知の値なら、元に戻ることを見てみる
| 段階 | 値または結果 |
|---|---|
| 元のバイト列 | 0、1、2、…、11の12個 |
| エンコード後 | AAECAwQFBgcICQoL |
| 文字数 | 16文字 |
| デコード後 | 元の12バイトと一致 |
この例は、ランダム性を試すための値ではありません。順番の分かる架空の12バイトを使い、表記を変えても材料が同じであることを確認しました。実TodoIdGeneratorも別に3回呼び、いずれも16文字でURL向けの文字集合に収まることを確認しています。
エンコードは、秘密を守る暗号化ではない
Base64のデコードには、秘密の鍵は要りません。この方式が分かれば、文字列から元のバイト列を取り出せます。暗号化のように、鍵を持つ相手だけが内容を読める仕組みではありません。 ただし、デコードできることと、次に作られるランダム値を予測できることは別です。この生成器が予測しにくさを求めている段階はSecureRandomで材料を作るところであり、Base64で文字へ変えるところではありません。
少数の試験で、衝突しないとは言えない
3個生成できたことは、形式の小さな動作確認に過ぎません。衝突が絶対に起きないことや、利用者の操作がすべて安全であることを証明しません。IDの衝突時にどう扱うかも、保存側の設計と合わせて確認する話です。 また、IDが推測しにくくても、それを知っている人へ何でも許可する理由にはなりません。対象のデータを操作できるかは、サーバー側の認可という別の確認です。
文字の見た目から、役割を決めつけない
英数字の長い文字列を見たら、まず何を材料にしているか、どう表しているか、どこで権限を確認するかを分けます。この例の16文字は、12バイトのランダムな材料をURLで扱いやすく表したIDでした。 今回は実コード、既知のバイト列、生成器の形式だけを確認しました。秘密情報や実在する利用者のデータを変換した実験ではありません。
参照:Java SE 21公式「Base64」。実TodoIdGeneratorと、同じJavaエンコーダーの実行結果を確認しました。
確認日:2026年9月22日。実装・検証結果・公式資料は確認時点のものです。