CUID2ジェネレーター
Generate secure next-generation CUID2 identifiers
生成された CUID2
CUID2とは何ですか?
CUID2(Collision-resistant Unique ID、バージョン2)はCUID v1の次世代後継者で、データベース、URL、分散システムの主キーとして安全に使用できる短く、暗号的に安全で不透明なIDを生成するよう設計されています。
前身とは異なり、CUID2は作成時刻、ホストマシン、生成したプロセスに関する情報を一切明かしません。各IDはランダムな小文字で始まり、SHA-512から派生したbase-36ハッシュが続く、一見ランダムな文字列です。デフォルト長は24文字ですが、ストレージ制約に合わせて2〜32文字の間で設定できます。
CUID2は現代のデータベースツールキットに広く推奨されています。Prismaは @default(cuid()) スカラーのデフォルトID戦略として採用し、PlanetScale、Neon、その他のサーバーレスデータベースプロバイダーはCUID2を推奨IDフォーマットとして明示しています。
CUID2がCUID v1を置き換えた理由
2012年にEric Elliottがリリースしたいたd CUID v1は、クライアントサイドID生成における単純なUUIDに対する大きな改善でした。しかし、セキュリティ研究者がその設計に2つの根本的な問題を発見しました:
- フィンガープリンティング:すべてのCUID v1値に埋め込まれたホストフィンガープリントを使用して、IDを生成したマシンまたはプロセスを特定でき、IDを観察できる誰にでも操作上のメタデータが漏洩します。
- 予測可能性:CUID v1が単調増加カウンターとタイムスタンプセグメントを含んでいたため、複数のIDを観察した攻撃者が将来のIDのおおよその範囲を予測でき、IDを唯一の認証チェックとして使用するAPIに対する列挙攻撃が可能になります。
- 非暗号ハッシュ:CUID v1は現代のセキュリティ標準を満たさない単純な非暗号ハッシュステップを使用していました。
元の作者Eric Elliottは、CUID v1を正式に非推奨とし、これらの問題をすべて解決するためにCUID2をゼロから開発しました。新しいアルゴリズムはWeb Crypto API(SHA-512)を使用し、すべての決定論的コンポーネントを排除します。
CUID2の設計原則
CUID2 vs CUID v1——比較
以下の表は、CUID2と現在非推奨のCUID v1の主な違いをまとめています。現在CUID v1を使用している場合は、CUID2への移行を強くお勧めします。
| 属性 | CUID2 | CUID v1 |
|---|---|---|
| セキュリティ | 暗号(SHA-512) | 非暗号(フィンガープリントベース) |
| 予測可能性 | 不透明——メタデータ漏洩なし | タイムスタンプ + フィンガープリントがIDに見える |
| 長さ | 設定可能(2〜32文字) | 固定25文字 |
| プレフィックス | ランダム文字a〜z | 常に「c」で始まる |
| 分布 | フラット / 均一 | 単調増加セグメント |
| ステータス | 積極的にメンテナンス | 元の作者により非推奨 |
CUID2 vs UUID v4——比較
UUID v4はランダムなユニークIDの主要標準です。CUID2はセキュリティを犠牲にすることなくUUID v4に対していくつかの実用的な利点を提供します。
| 属性 | CUID2 | UUID v4 |
|---|---|---|
| デフォルト長 | 24文字 | 36文字(ハイフン付き) |
| URL安全 | はい——小文字a〜z + 0〜9 | エンコード必要(ハイフンを含む) |
| カスタム長 | はい(2〜32) | いいえ——常に128ビット / 36文字 |
| ソート可能 | いいえ(設計上) | いいえ(v4はランダム) |
| エントロピーソース | SHA-512 + Web Crypto | CSPRNG |
| 文字セット | Base-36(a〜z, 0〜9) | 16進数 + ハイフン |
主なトレードオフは親しみやすさです:UUID v4はIETF標準(RFC 4122)で、ほぼすべてのデータベース、プログラミング言語、APIフレームワークで即座に認識されます。CUID2は成長しているが普遍的でないサポートを持つコミュニティ標準です。外部システムとの相互運用性が最重要の場合はUUID v4を選択し;両端を制御していて短くURL安全なIDを好む場合はCUID2を選択してください。
CUID2を使用しているのは誰ですか
CUID2は現代のJavaScriptおよびTypeScriptエコシステムで急速に採用されています:
- Prisma——最も人気のあるTypeScript ORMは、Prisma Schema v2+の
@idフィールドに@default(cuid())とともにCUID2を推奨デフォルトとして使用します。 - PlanetScale——ドキュメントとスターターテンプレートは、分散MySQLプラットフォームでのシーケンシャルスキャンのパフォーマンス問題を避けるためにアプリケーション生成の主キーにCUID2を推奨しています。
- Drizzle ORM——列定義用のビルトインデフォルトヘルパー
cuid2()を提供します。 - tRPCボイラープレート——多くのコミュニティtRPC + Prismaスターターテンプレートは主キー戦略としてCUID2とともに提供されています。
- T3 Stack——create-t3-appスキャフォールディングツールは生成されたスキーマファイルでCUID2デフォルトを持つPrismaを使用します。
コード例
公式npmパッケージ @paralleldrive/cuid2 がシンプルなAPIを提供します:
import { createId } from '@paralleldrive/cuid2'
// Generate a single CUID2 (default length: 24)
const id = createId()
console.log(id) // e.g. "tz4a98xxat96iws9zmbrgj3a"
// Custom length
import { init } from '@paralleldrive/cuid2'
const createShortId = init({ length: 16 })
const shortId = createShortId()
console.log(shortId) // e.g. "tz4a98xxat96iws9"PrismaスキーマでCUID2を使用する:
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}npmパッケージなしでNode.jsでCUID2を生成する(Web Crypto APIのみを使用、このツールがブラウザーで行うように):
async function generateCuid2(length = 24) {
const alphabet = 'abcdefghijklmnopqrstuvwxyz'
// Random prefix letter
const firstByte = crypto.getRandomValues(new Uint8Array(1))[0]
const firstChar = alphabet[firstByte % 26]
// Random 32-byte salt
const salt = crypto.getRandomValues(new Uint8Array(32))
const saltHex = [...salt].map(b => b.toString(16).padStart(2, '0')).join('')
// SHA-512 of timestamp + salt
const input = Date.now().toString(36) + saltHex
const hashBuffer = await crypto.subtle.digest(
'SHA-512',
new TextEncoder().encode(input)
)
// Encode hash bytes as base-36 string
const bytes = new Uint8Array(hashBuffer)
let hash = ''
for (let i = 0; i < bytes.length; i += 8) {
let chunk = 0n
for (let j = 0; j < 8 && i + j < bytes.length; j++) {
chunk = (chunk << 8n) | BigInt(bytes[i + j])
}
hash += chunk.toString(36)
}
return (firstChar + hash).slice(0, length)
}
// Usage
const id = await generateCuid2()
console.log(id) // e.g. "m7k3r9p2nxq8zt5a6cwj4bvd"