Back

Web Crypto APIを使った一意のID生成

Web Crypto APIを使った一意のID生成

すべてのフロントエンド開発者は、いずれこの問題に直面します。クライアント側のデータに一意の識別子が必要だが、サーバー生成のIDがまだない状況です。ToDoリストを構築している場合、フォームフィールドを管理している場合、または永続化される前のアイテムを追跡している場合など、信頼できるものが必要で、しかも今すぐ必要なのです。

朗報があります。モダンブラウザには組み込みのソリューションが搭載されています。Web Crypto APIcrypto.randomUUID()を提供しており、ライブラリをインストールすることなくブラウザで一意のIDを生成するシンプルで安全な方法です。

重要なポイント

  • crypto.randomUUID()は、依存関係ゼロでブラウザネイティブにRFC準拠の暗号学的に安全なv4 UUIDを生成します。
  • IDは常にオブジェクト作成時に生成し、レンダリングサイクル中には生成しないでください。
  • このメソッドはセキュアコンテキスト(HTTPSまたはlocalhost)が必要です。
  • crypto.randomUUID()が利用できない環境では、軽量なフォールバックとしてcrypto.getRandomValues()を使用してください。

crypto.randomUUID()をデフォルトにすべき理由

crypto.randomUUID()メソッドは、暗号学的に安全な乱数生成器を使用してRFC 9562(旧RFC 4122)バージョン4のUUIDを生成します。出力は次のようになります:

550e8400-e29b-41d4-a716-446655440000

これは122ビットのランダム性を持つ36文字の文字列です。衝突確率は天文学的に低く、単一の衝突が50%の確率で発生するには、毎秒10億個のUUIDを約85年間生成し続ける必要があります。

使い方は非常にシンプルです:

function generateId() {
  return crypto.randomUUID()
}

const newItem = { id: generateId(), label: 'My item' }

このメソッドは、すべてのモダンブラウザ(Chrome 92+、Firefox 95+、Safari 15.4+)で動作し、Web Workerでも実行でき、依存関係は不要です。

セキュアコンテキストの要件

重要な詳細が1つあります。crypto.randomUUID()セキュアコンテキストでのみ動作します。これは、本番環境ではHTTPS、開発中はlocalhostを意味します。localhost以外のドメインでプレーンHTTPを使用してテストしている場合、このメソッドは利用できません。

この要件が存在する理由は、Web Crypto APIが暗号化プリミティブを提供しており、安全でない環境では公開すべきではないためです。

従来のパターンが不十分な理由

crypto.randomUUID()が広く利用可能になる前、開発者は一見合理的に見えるものの、実際には問題がある代替手段に頼ることがよくありました。

Math.random() は暗号学的に安全ではありません。その出力は予測可能であり、本番アプリケーションでは予想以上に衝突が発生する可能性が高くなります。

タイムスタンプ(例:Date.now())は、同じミリ秒内に複数のIDが生成される場合に失敗します。これはループやバッチ操作でよくあるシナリオです。

タイムスタンプと乱数を組み合わせたアドホックジェネレータは改善されていますが、プラットフォームがネイティブに提供するものよりも厳密性が低く、解決済みの問題を再発明しているに過ぎません。

安全なクライアント側ID生成には、crypto.randomUUID()が明確な選択肢です。

実践的なフロントエンドでの考慮事項

一度生成し、即座に保存する

最もよくある間違いは、レンダリングサイクル中にIDを生成することです。代わりに、オブジェクトが作成されたときにIDを作成し、それを保存してください:

// ✓ 正しい: 作成時にIDを生成
function addItem(label) {
  return { id: crypto.randomUUID(), label }
}

// ✗ 間違い: レンダリング中にIDを生成
items.map(item => <li key={crypto.randomUUID()}>{item.label}</li>)

レンダリングごとに新しいUUIDを生成すると、安定したキーの目的が損なわれます。例えば、Reactはキーを使用して再レンダリング間で要素を追跡します。毎回新しいキーを使用すると、不要なDOM破棄と再作成が強制されます。

SSRとブラウザ環境

サーバーサイドレンダリングを使用している場合、crypto.randomUUID()はNode.js 19+および他のランタイムの最新バージョンで利用可能です。古いNodeバージョンやエッジケースの場合は、呼び出す前に可用性を確認してください:

const id = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
  ? crypto.randomUUID()
  : fallbackGenerator()

軽量なフォールバック戦略

crypto.randomUUID()が利用できない環境では、crypto.getRandomValues()がより広範なサポート(Node.js 16+を含む)を持ち、ライブラリなしでUUIDを生成できます:

function fallbackUUID() {
  const bytes = crypto.getRandomValues(new Uint8Array(16))
  bytes[6] = (bytes[6] & 0x0f) | 0x40 // Version 4
  bytes[8] = (bytes[8] & 0x3f) | 0x80 // Variant 10
  
  const hex = [...bytes].map(b => b.toString(16).padStart(2, '0')).join('')
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
}

これは、バージョンとバリアントビットを手動で設定して有効なv4 UUIDを生成し、内部で同じ暗号学的に安全な乱数ソースを使用します。

ライブラリがまだ意味を持つ場合

crypto.randomUUID()はほとんどのケースをカバーしますが、次のような場合にはuuidnanoidのようなライブラリに頼ることがあります:

  • v4以外のUUIDバージョン(v1、v5、v7)が必要な場合
  • URLやユーザー向け表示用の短いIDが必要な場合
  • 古いランタイムとのクロス環境互換性を保証する必要がある場合

しかし、標準的な安全なクライアント側ID生成には、ネイティブAPIが出発点であるべきです。

まとめ

crypto.randomUUID()は、依存関係ゼロでRFC準拠の暗号学的に安全なUUIDを提供します。IDはオブジェクト作成時に生成し、レンダリング中には生成しないでください。本番環境ではHTTPSを使用してください。エッジケースでは、crypto.getRandomValues()にフォールバックしてください。ブラウザには既に必要なものがあります。それを使いましょう。

よくある質問

はい、crypto.randomUUID()によって生成されたv4 UUIDはプライマリキーとして適しています。122ビットの暗号学的ランダム性により、衝突は実質的に不可能です。ただし、ランダムなUUIDは一部のデータベースでインデックスの断片化を引き起こす可能性があることに注意してください。大規模な挿入パフォーマンスが重要な場合は、時系列順のUUID v7を検討してください(ただし、これにはライブラリが必要です)。

Web Crypto APIが暗号化プリミティブを公開するため、セキュアコンテキストが必要です。ブラウザは、中間者攻撃が暗号化操作を改ざんするのを防ぐため、これらをHTTPSとlocalhostに制限しています。localhostでのローカル開発中は、HTTPSなしで動作します。本番環境では、有効なTLS証明書が必要です。

はい。cryptoオブジェクトとそのrandomUUIDメソッドは、コンテキストが安全である限り、Web Worker、Service Worker、Shared Workerで利用可能です。これにより、ID作成のためにメインスレッドと通信する必要なく、バックグラウンドスレッドでIDを生成するのが便利になります。

crypto.randomUUID()は、ブラウザの組み込み暗号学的乱数生成器を使用して、標準的な36文字のv4 UUIDを生成します。nanoidは、カスタマイズ可能なアルファベットと長さを持つ、より短いURL対応のIDを生成し、より多くの環境で標準で動作します。標準準拠とゼロ依存性にはrandomUUIDを選択してください。コンパクトなIDやより広範なランタイムサポートが必要な場合はnanoidを選択してください。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay