Generator CUID2
Generuje bezpieczne identyfikatory CUID2 nowej generacji
Wygenerowane CUID2
Czym jest CUID2?
CUID2 (Collision-resistant Unique ID, wersja 2) to następca nowej generacji CUID v1, zaprojektowany do generowania krótkich, kryptograficznie bezpiecznych i nieprzejrzystych identyfikatorów, które są bezpieczne do użycia jako klucze główne w bazach danych, adresach URL i systemach rozproszonych.
W przeciwieństwie do poprzednika, CUID2 nie ujawnia żadnych informacji o czasie tworzenia, maszynie hosta ani procesie, który go wygenerował. Każdy identyfikator to pozornie losowy ciąg znaków zaczynający się od losowej małej litery, po której następuje skrót base-36 wyprowadzony z SHA-512. Domyślna długość wynosi 24 znaki, ale można ją skonfigurować od 2 do 32 znaków.
CUID2 jest szeroko zalecany przez nowoczesne zestawy narzędzi do baz danych. Prisma przyjęło go jako domyślną strategię identyfikatorów dla skalaru @default(cuid()), a PlanetScale, Neon i inni dostawcy bezserwerowych baz danych wyraźnie wymieniają CUID2 jako preferowany format identyfikatorów.
Dlaczego CUID2 Zastąpił CUID v1
CUID v1, wydany w 2012 roku przez Erica Elliotta, był znaczącą poprawą w stosunku do zwykłych UUID w generowaniu identyfikatorów po stronie klienta. Jednak badacze bezpieczeństwa odkryli dwa fundamentalne problemy z jego projektem:
- Fingerprinting: Odcisk palca hosta osadzony w każdej wartości CUID v1 mógł być używany do identyfikacji maszyny lub procesu, który wygenerował identyfikator, ujawniając operacyjne metadane każdemu, kto mógł obserwować identyfikatory.
- Przewidywalność: Ponieważ CUID v1 zawierał monotonicznie rosnący licznik i segment znacznika czasu, atakujący obserwujący kilka identyfikatorów mógł przewidzieć przybliżony zakres przyszłych identyfikatorów, umożliwiając ataki wyliczeniowe na API używające identyfikatorów jako jedynej kontroli autoryzacji.
- Niekryptograficzny skrót: CUID v1 używał prostego niekryptograficznego kroku haszowania, który nie spełniał nowoczesnych standardów bezpieczeństwa.
Eric Elliott, oryginalny autor, formalnie oznaczył CUID v1 jako przestarzały i napisał CUID2 od podstaw, aby rozwiązać wszystkie te problemy. Nowy algorytm używa Web Crypto API (SHA-512) i eliminuje wszystkie deterministyczne komponenty.
Zasady Projektowania CUID2
CUID2 vs CUID v1 — Porównanie
Poniższa tabela podsumowuje kluczowe różnice między CUID2 a teraz przestarzałym CUID v1. Jeśli używasz CUID v1, zdecydowanie zaleca się migrację do CUID2.
| Atrybut | CUID2 | CUID v1 |
|---|---|---|
| Bezpieczeństwo | Kryptograficzny (SHA-512) | Niekryptograficzny (oparty na odcisku palca) |
| Przewidywalność | Nieprzejrzysty — brak ujawnionych metadanych | Znacznik czasu + odcisk palca widoczny w ID |
| Długość | Konfigurowalna (2–32 znaki) | Stała 25 znaków |
| Prefiks | Losowa litera a–z | Zawsze zaczyna się od "c" |
| Dystrybucja | Płaska / równomierna | Monotonicznie rosnące segmenty |
| Status | Aktywnie utrzymywany | Przestarzały przez oryginalnego autora |
CUID2 vs UUID v4 — Porównanie
UUID v4 jest dominującym standardem dla losowych unikalnych identyfikatorów. CUID2 oferuje kilka praktycznych zalet nad UUID v4 bez poświęcania bezpieczeństwa.
| Atrybut | CUID2 | UUID v4 |
|---|---|---|
| Domyślna długość | 24 znaki | 36 znaków (z myślnikami) |
| Bezpieczny dla URL | Tak — małe litery a–z + 0–9 | Wymaga kodowania (zawiera myślniki) |
| Niestandardowa długość | Tak (2–32) | Nie — zawsze 128 bitów / 36 znaków |
| Sortowalny | Nie (z założenia) | Nie (v4 jest losowy) |
| Źródło entropii | SHA-512 + Web Crypto | CSPRNG |
| Zestaw znaków | Base-36 (a–z, 0–9) | Hex + myślniki |
Główny kompromis to znajomość: UUID v4 to standard IETF (RFC 4122) rozpoznawany przez praktycznie każdą bazę danych, język programowania i framework API. CUID2 to standard społeczności z rosnącym, ale nie powszechnym wsparciem. Wybierz UUID v4, gdy interoperacyjność z systemami zewnętrznymi jest kluczowa; wybierz CUID2, gdy kontrolujesz oba końce i wolisz krótsze, bezpieczne dla URL identyfikatory.
Kto Używa CUID2
CUID2 szybko zyskuje adopcję w nowoczesnym ekosystemie JavaScript i TypeScript:
- Prisma — najpopularniejszy ORM TypeScript używa CUID2 jako zalecanego domyślnego dla pól
@idz@default(cuid())w Prisma Schema v2+. - PlanetScale — ich dokumentacja i szablony startowe zalecają CUID2 dla kluczy głównych generowanych przez aplikację, aby uniknąć problemów z wydajnością sekwencyjnego skanowania na ich rozproszonym platformie MySQL.
- Drizzle ORM — zapewnia wbudowanego pomocnika domyślnego
cuid2()dla definicji kolumn. - Szablony tRPC — wiele szablonów społeczności tRPC + Prisma jest dostarczanych z CUID2 jako strategią klucza głównego.
- T3 Stack — narzędzie do scaffoldingu create-t3-app używa Prisma z domyślnymi CUID2 w wygenerowanych plikach schematu.
Przykłady Kodu
Oficjalny pakiet npm @paralleldrive/cuid2 zapewnia proste 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"Używanie CUID2 ze schematem Prisma:
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Generowanie CUID2 w Node.js bez pakietu npm (używając tylko Web Crypto API, tak jak robi to to narzędzie w przeglądarce):
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"