メインコンテンツまでスキップ

UUID(Universally Unique Identifier)

所要時間: 30分


はじめに

UUIDは「世界中で一意な識別子」を生成するための標準規格です。

認証・認可システムでは至る所で使用されます:

用途
ユーザーIDsub クレーム
トークンIDjti(JWT ID)クレーム
セッションIDセッション管理
リクエストIDトレーシング、監査ログ
クライアントIDOAuth クライアント識別

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) │
│ │
└─────────────────────────────────────────────────────────────────────┘

6. セキュリティ考慮事項

UUID v1 の情報漏洩

┌─────────────────────────────────────────────────────────────────────┐
│ UUID v1 のセキュリティリスク │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ UUID v1 の構造: │
│ ・60ビット: タイムスタンプ(100ナノ秒単位) │
│ ・48ビット: ノードID(通常はMACアドレス) │
│ ・14ビット: クロックシーケンス │
│ │
│ 漏洩する情報: │
│ ❌ 生成時刻(いつ作成されたか) │
│ ❌ MACアドレス(どのマシンで生成されたか) │
│ ❌ 生成順序(どの順番で作成されたか) │
│ │
│ 例: │
│ d1c71566-65a9-11ef-8b60-325096b39f47 │
│ └──────────┘ │
│ MACアドレス: 32:50:96:b3:9f:47 │
│ │
│ → セッションIDやトークンIDには絶対に使わない │
│ │
└─────────────────────────────────────────────────────────────────────┘

推奨バージョン

┌─────────────────────────────────────────────────────────────────────┐
│ 用途別 UUID バージョン推奨 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 用途 推奨バージョン │
│ ───────────────────────────────────────────────────────────────── │
│ セッションID v4(ランダム) │
│ トークンID(jti) v4(ランダム) │
│ state / nonce v4(ランダム) │
│ ユーザーID(sub) v4 または v7 │
│ データベース主キー v7(ソート可能) │
│ 冪等性キー v4 または v5(名前ベース) │
│ 決定論的ID v5(同じ入力→同じ出力) │
│ │
│ ❌ 避けるべき: v1(情報漏洩のリスク) │
│ │
└─────────────────────────────────────────────────────────────────────┘

7. 代替技術

ULID

┌─────────────────────────────────────────────────────────────────────┐
│ ULID(Universally Unique Lexicographically │
│ Sortable Identifier) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 形式: 01ARZ3NDEKTSV4RRFFQ69G5FAV │
│ └──────┘└──────────────────┘ │
│ 時刻 ランダム │
│ │
│ 特徴: │
│ ・128ビット(UUIDと同じ) │
│ ・先頭48ビット: Unixミリ秒 │
│ ・残り80ビット: ランダム │
│ ・Base32エンコード(26文字、大文字小文字区別なし) │
│ ・辞書順でソート可能 │
│ │
│ UUID v7 との比較: │
│ ・ULID: 短い表記(26文字 vs 36文字) │
│ ・UUID v7: RFC標準、広いサポート │
│ │
└─────────────────────────────────────────────────────────────────────┘

Snowflake ID

┌─────────────────────────────────────────────────────────────────────┐
│ Snowflake ID(Twitter発祥) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 64ビット構成: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 1bit │ 41bit │ 10bit │ 12bit │ │
│ │ 未使用│ タイムスタンプ │ マシンID │ シーケンス番号 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 特徴: │
│ ・64ビット(UUIDの半分) │
│ ・時系列ソート可能 │
│ ・1ミリ秒あたり4096個生成可能 │
│ ・マシンIDによる分散生成 │
│ │
│ 欠点: │
│ ・マシンID管理が必要 │
│ ・推測可能性がある │
│ │
└─────────────────────────────────────────────────────────────────────┘

まとめ

┌─────────────────────────────────────────────────────────────────────┐
│ 覚えておくべきポイント │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. セキュリティが重要な場面では UUID v4 │
│ → セッションID、トークンID、state、nonce │
│ │
│ 2. DBパフォーマンスが重要な場面では UUID v7 │
│ → 主キー、大量INSERTがある場合 │
│ │
│ 3. UUID v1 は避ける │
│ → MACアドレス、生成時刻が漏洩 │
│ │
│ 4. DB保存は UUID型 または BINARY(16) │
│ → CHAR(36) は非効率 │
│ │
│ 5. バージョンは3番目のセクションの先頭で識別 │
│ → xxxxxxxx-xxxx-4xxx: v4, -7xxx: v7 │
│ │
└─────────────────────────────────────────────────────────────────────┘

参考