UUID(Universally Unique Identifier)
所要時間: 30分
はじめに
UUIDは「世界中で一意な識別子」を生成するための標準規格です。
認証・認可システムでは至る所で使用されます:
| 用途 | 例 |
|---|---|
| ユーザーID | sub クレーム |
| トークンID | jti(JWT ID)クレーム |
| セッションID | セッション管理 |
| リクエストID | トレーシング、監査ログ |
| クライアントID | OAuth クライアント識別 |
1. UUIDとは
基本構造
┌─────────────────────────────────────────────────────────────────────┐
│ UUID の形式 │
├─────────────────────────────────────────────────────────────── ──────┤
│ │
│ 550e8400-e29b-41d4-a716-446655440000 │
│ ├──────┤ ├──┤ ├──┤ ├──┤ ├──────────┤ │
│ 8 4 4 4 12 文字数 │
│ │
│ 合計: 32文字の16進数 + 4つのハイフン = 36文字 │
│ ビット数: 128ビット │
│ │
│ 表記: │
│ ・標準形式: 550e8400-e29b-41d4-a716-446655440000 │
│ ・ハイフンなし: 550e8400e29b41d4a716446655440000 │
│ ・URN形式: urn:uuid:550e8400-e29b-41d4-a716-446655440000 │
│ │
└─────────────────────────────────────────────────────────────────────┘
なぜ UUID を使うのか
┌────────────────────────────────────────────────── ───────────────────┐
│ UUID vs 連番ID │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 連番ID(AUTO_INCREMENT)の問題: │
│ │
│ ❌ 推測可能 │
│ user/1, user/2, user/3... │
│ → 他のユーザーIDを推測してアクセス試行される │
│ │
│ ❌ 件数漏洩 │
│ ID が 10000 → 「約1万件 のデータがある」とわかる │
│ │
│ ❌ 分散システムで衝突 │
│ 複数サーバーで同時に採番すると重複の可能性 │
│ │
│ UUID の利点: │
│ │
│ ✅ 推測不可能(特にUUID v4) │
│ ✅ 件数が推測されない │
│ ✅ 分散生成可能(調整なしで各サーバーが生成可能) │
│ ✅ オフライン生成可能 │
│ │
└─────────────────────────────────────────────────────────────────────┘
2. UUIDのバージョン
バージョン一覧
┌──────────────────────────────────────────────────────────────────── ─┐
│ UUID バージョン │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Version 生成方法 特徴 │
│ ───────────────────────────────────────────────────────────────── │
│ v1 タイムスタンプ + MACアドレス 時刻順でソート可能 │
│ MACアドレス漏洩の懸念 │
│ │
│ v2 DCE Security UUID ほぼ使われない │
│ │
│ v3 名前空間 + 名前のMD5ハッシュ 同じ入力→同じUUID │
│ │
│ v4 ランダム 最も一般的、セキュア │
│ 122ビットのランダム値 │
│ │
│ v5 名前空間 + 名前のSHA-1ハッシュ v3のSHA-1版 │
│ │
│ v6 タイムスタンプ(ソート可能) v1の改良版 │
│ 時刻順でソート可能 │
│ │
│ v7 Unixミリ秒 + ランダム 最新推奨、ソート可能 │
│ DBインデック ス効率が良い │
│ │
│ v8 カスタム 実装依存 │
│ │
└─────────────────────────────────────────────────────────────────────┘
バージョンの見分け方
┌─────────────────────────────────────────────────────────────────────┐
│ UUID バージョンの識別 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 550e8400-e29b-41d4-a716-446655440000 │
│ ↑ │
│ バージョン番号(1桁目) │
│ │
│ 41d4 の "4" → UUID v4 │
│ │
│ 例: │
│ xxxxxxxx-xxxx-1xxx-xxxx-xxxxxxxxxxxx → v1 │
│ xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx → v4 │
│ xxxxxxxx-xxxx-7xxx-xxxx-xxxxxxxxxxxx → v7 │
│ │
│ バリアント(variant): │
│ 550e8400-e29b-41d4-a716-446655440000 │
│ ↑ │
│ 8, 9, a, b のいずれか → RFC 4122準拠 │
│ │
└─────────────────────────────────────────────────────────────────────┘
UUID v4(ランダム)
┌─────────────────────────────────────────────────────────────────────┐
│ UUID v4 の構造 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 128ビット中: │
│ ・4ビット: バージョン番号(固定で "0100" = 4) │
│ ・2ビット: バリアント(固定で "10") │
│ ・122ビット: ランダム値 │
│ │
│ パターン数: │
│ ・2^122 ≒ 5,316,911,983,139,663,491,615,228,241,121,378,304 通り │
│ ・約 5.3 × 10^36(5.3澗、53溝) │
│ │
│ 比較: │
│ ・宇宙の原子数: 約 10^80 │
│ ・地球の砂粒数: 約 10^23 │
│ ・UUID v4: 約 10^36 ← 地球の砂粒の1兆倍以上 │
│ │
│ 衝突確率: │
│ ・10億個生成しても衝突確率は極めて低い │
│ ・103兆個生成して初めて衝突確率が10億分の1 │
│ │
│ セキュリティ: │
│ ・暗号論的に安全な乱数生成器(CSPRNG)を使用すべき │
│ ・推測攻撃に対して安全 │
│ │
└───────────────────── ────────────────────────────────────────────────┘
UUID v7(時刻ベース + ランダム)
┌─────────────────────────────────────────────────────────────────────┐
│ UUID v7 の構造 │
├──────── ─────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 48ビット │ 4ビット │ 12ビット │ 2ビット │ 62ビット │ │
│ │ Unixミリ秒 │ version │ ランダム │ variant │ ランダム │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ 利点: │
│ ✅ 時系列でソート可能(先頭が時刻) │
│ ✅ DBインデックスの効率が良い(B-treeに優しい) │
│ ✅ ランダム部分があるので衝突しにくい │
│ ✅ 生成時刻がわかる(デバッグに便利) │
│ │
│ 注意: │
│ ⚠ 生成時刻が推測される(セキュリティ要件による) │
│ │
│ 推奨ケース: │
│ ・主キーとして使用(INSERTパフォーマンス向上) │
│ ・時系列でのソートが必要な場合 │
│ │
└───────────────────────────────────────────────────────────── ────────┘
3. 認証・認可での使用
JWTクレーム
┌─────────────────────────────────────────────────────────────────────┐
│ JWT での UUID 使用 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ { │
│ "iss": "https://idp.example.com", │
│ "sub": "550e8400-e29b-41d4-a716-446655440000", ← ユーザーID │
│ "aud": "client-app", │
│ "jti": "7c9e6679-7425-40de-944b-e07fc1f90ae7", ← トークンID │
│ "exp": 1704070800, │
│ "iat": 1704067200 │
│ } │
│ │
│ sub(Subject): │
│ ・ユーザーの一意識別子 │
│ ・メールアドレスではなくUUIDを推奨(変更可能性) │
│ │
│ jti(JWT ID): │
│ ・トークンの一意識別子 │
│ ・トークン無効化(ブラックリスト)に使用 │
│ ・リプレイ攻撃防止 │
│ │
└─────────────────────────────────────────────────────────────────────┘
OAuth/OIDC パラメータ
┌─────────────────────────────────────────────────────────────────────┐
│ OAuth/OIDC での UUID 使用 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ state パラメータ: │
│ /authorize? │
│ response_type=code& │
│ client_id=xxx& │
│ state=f47ac10b-58cc-4372-a567-0e02b2c3d479 ← CSRF対策 │
│ │
│ nonce パラメータ: │
│ /authorize? │
│ response_type=id_token& │
│ nonce=9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d ← リプレイ攻撃防止 │
│ │
│ authorization_code: │
│ 内部的にUUIDで管理されることが多い │
│ │
│ client_id: │
│ UUIDまたはランダム文字列で生成 │
│ │
└─────────────────────────────────────────────────────────────────────┘
セッションID
┌─────────────────────────────────────────────────────────────────────┐
│ セッションID での UUID │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Set-Cookie: SESSION=550e8400-e29b-41d4-a716-446655440000; │
│ HttpOnly; Secure; SameSite=Strict │
│ │
│ なぜ UUID v4 が適切か: │
│ ・推測不可能(セッションハイジ ャック防止) │
│ ・十分なエントロピー(122ビット) │
│ │
│ ⚠ UUID v1 は避ける: │
│ ・MACアドレスが含まれる(情報漏洩) │
│ ・時刻ベースなので推測可能性がある │
│ │
└─────────────────────────────────────────────────────────────────────┘
4. 実装
Java
import java.util.UUID;
// UUID v4 生成(最も一般的)
UUID uuid = UUID.randomUUID();
String uuidString = uuid.toString();
// "550e8400-e29b-41d4-a716-446655440000"
// 文字列からUUIDに変換
UUID parsed = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
// ハイフンなしの形式
String withoutHyphens = uuid.toString().replace("-", "");
// "550e8400e29b41d4a716446655440000"
// バージョン確認
int version = uuid.version(); // 4
// バリアント確認
int variant = uuid.variant(); // 2 (RFC 4122)
UUID v7(Java 21+では未サポート、ライブラリ使用)
// com.github.f4b6a3:uuid-creator ライブラリ使用
import com.github.f4b6a3.uuid.UuidCreator;
// UUID v7 生成
UUID uuidV7 = UuidCreator.getTimeOrderedEpoch();
// UUID v4 生成(セキュア)
UUID uuidV4 = UuidCreator.getRandomBased();
// UUID v1 生成(非推奨)
UUID uuidV1 = UuidCreator.getTimeBased();
JavaScript
// ブラウザ / Node.js(crypto API)
const uuid = crypto.randomUUID();
// "550e8400-e29b-41d4-a716-446655440000"
// uuid ライブラリ使用
import { v4 as uuidv4, v7 as uuidv7 } from 'uuid';
const id = uuidv4(); // ランダム
const id7 = uuidv7(); // 時刻ベース
PostgreSQL
-- UUID 型のカラム
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL
);
-- UUID生成
SELECT gen_random_uuid();
-- "550e8400-e29b-41d4-a716-446655440000"
-- 文字列からUUID
SELECT '550e8400-e29b-41d4-a716-446655440000'::uuid;
5. データベースでの考慮事項
UUID vs AUTO_INCREMENT
┌─────────────────────────────────────────────────────────────────────┐
│ UUID vs AUTO_INCREMENT 比較 │
├────────────────────────────────────────────────────────────── ───────┤
│ │
│ AUTO_INCREMENT UUID v4 UUID v7 │
│ ────────────────────────────────────────────────────────────────── │
│ サイズ 4-8 bytes 16 bytes 16 bytes │
│ ソート 時系列順 ランダム 時系列順 │
│ INSERTパフォーマンス 良い 悪い(v4) 良い │
│ 分散生成 困難 容易 容易 │
│ 推測可能性 高い 低い 中程度 │
│ 件数推測 可能 不可能 不可能 │
│ │
│ 推奨: │
│ ・公開ID(API、URL): UUID v4 │
│ ・内部主キー: UUID v7 または BIGINT + 別途公開ID │
│ │
└─────────────────────────────────────────────────────────────────────┘
インデックスパフォーマンス
┌─────────────────────────────────────────────────────────────────────┐
│ UUID とインデックス │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ UUID v4 の問題(B-tree インデックス): │
│ │
│ INSERT順: ランダム │
│ ┌─────────────────────────────────────────┐ │
│ │ B-tree ページ │ │
│ │ [1xxx] [3xxx] [5xxx] [7xxx] [9xxx] │ │
│ │ ↑ ↑ ↑ ↑ ↑ │ │
│ │ 新規INSERTが全ページに分散 │ │
│ │ → ページ分割が頻発、書き込み負荷大 │ │
│ └─────────────────────────────────────────┘ │
│ │
│ UUID v7 の利点: │
│ ┌─────────────────────────────────────────┐ │
│ │ B-tree ページ │ │
│ │ [古い] [古い] [古い] [新しい→] │ │
│ │ ↑ │ │
│ │ 新規INSERTは末尾に集中 │ │
│ │ → ページ分割が少ない、効率的 │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
保存形式
┌─────────────────────────────────────────────────────────────────────┐
│ UUID の保存形式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 方法1: UUID型(PostgreSQL) │
│ ✅ 16バイト固定 │
│ ✅ インデックス効率が良い │
│ ✅ 型安全 │
│ │
│ 方法2: BINARY(16) │
│ ✅ 16バイト固定 │
│ ⚠ 可読性が低い │
│ │
│ 方法3: CHAR(36) │
│ ❌ 36バイト(ハイフン込み) │
│ ❌ ストレージ効率が悪い │
│ ✅ 可読性が高い │
│ │
│ 方法4: VARCHAR(32) │
│ ❌ 32バイト(ハイフンなし) │
│ ⚠ 中途半端 │
│ │
│ 推奨: UUID型(対応DB)または BINARY(16) │
│ │
└─────────────────────────────────────────────────────────────────────┘