Vad är CUID2?
CUID2 (Collision-resistant Unique ID, version 2) är efterföljaren till CUID v1, utformat för att generera korta, kryptografiskt säkra och ogenomskinliga ID:n som är säkra att använda som primärnycklar i databaser, URL:er och distribuerade system.
Till skillnad från sin föregångare avslöjar CUID2 ingen information om skapandetiden, värdmaskinen eller processen som genererade det. Varje ID är en till synes slumpmässig sträng som börjar med en slumpmässig gemen bokstav följt av en base-36-hash härledd från SHA-512. Standardlängden är 24 tecken, men du kan konfigurera den från 2 till 32 tecken för att passa dina lagringskrav.
CUID2 rekommenderas brett av moderna databasverktyg. Prisma antog det som standard-ID-strategi för sin @default(cuid())-skalär, och PlanetScale, Neon och andra serverlösa databasleverantörer listar uttryckligen CUID2 som ett föredraget ID-format eftersom det undviker de sekventiella sårbarheterna hos auto-increment-heltal medan det är kortare och mer läsbart än ett UUID.
Varför CUID2 ersatte CUID v1
CUID v1, lanserat 2012 av Eric Elliott, var en stor förbättring jämfört med vanliga UUID:er för ID-generering på klientsidan. Säkerhetsforskare upptäckte dock två grundläggande problem med dess design:
- Fingeravtryckning: Värdfingeravtrycket inbäddat i varje CUID v1-värde kunde användas för att identifiera den maskin eller process som genererade ID:t, vilket läckte operativ metadata till alla som kunde observera ID:na.
- Förutsägbarhet: Eftersom CUID v1 innehöll en monotont ökande räknare och ett tidsstämpelsegment, kunde en angripare som observerade flera ID:n förutsäga det ungefärliga intervallet för framtida ID:n, vilket möjliggjorde uppräkningsattacker mot API:er som använder ID:n som enda autentiseringskontroll.
- Icke-kryptografisk hash: CUID v1 använde ett enkelt icke-kryptografiskt hashsteg som inte uppfyllde moderna säkerhetsstandarder.
Eric Elliott, den ursprungliga författaren, föråldrade formellt CUID v1 och skapade CUID2 från grunden för att åtgärda alla dessa problem. Den nya algoritmen använder Web Crypto API (SHA-512) och eliminerar alla deterministiska komponenter, vilket gör varje ID statistiskt oberoende av alla andra.
CUID2:s designprinciper
CUID2 vs CUID v1 — jämförelse
Tabellen nedan sammanfattar de viktigaste skillnaderna mellan CUID2 och det nu föråldrade CUID v1. Om du för närvarande använder CUID v1 rekommenderas starkt att migrera till CUID2.
| Attribut | CUID2 | CUID v1 |
|---|---|---|
| Säkerhet | Kryptografisk (SHA-512) | Icke-kryptografisk (fingeravtrycksbaserad) |
| Förutsägbarhet | Ogenomskinlig — ingen metadata läcker | Tidsstämpel + fingeravtryck synliga i ID |
| Längd | Konfigurerbar (2–32 tecken) | Fast 25 tecken |
| Prefix | Slumpmässig a–z-bokstav | Börjar alltid med "c" |
| Distribution | Jämn / enhetlig | Monotont ökande segment |
| Status | Aktivt underhållet | Föråldrat av ursprunglig författare |
CUID2 vs UUID v4 — jämförelse
UUID v4 är den dominerande standarden för slumpmässiga unika ID:n. CUID2 erbjuder flera praktiska fördelar jämfört med UUID v4 utan att kompromissa med säkerheten.
| Attribut | CUID2 | UUID v4 |
|---|---|---|
| Standardlängd | 24 tecken | 36 tecken (med bindestreck) |
| URL-säker | Ja — gemena a–z + 0–9 | Kräver kodning (innehåller bindestreck) |
| Anpassad längd | Ja (2–32) | Nej — alltid 128 bitar / 36 tecken |
| Sorterbar | Nej (med avsikt) | Nej (v4 är slumpmässig) |
| Entropikälla | SHA-512 + Web Crypto | CSPRNG |
| Teckenuppsättning | Base-36 (a–z, 0–9) | Hex + bindestreck |
Den huvudsakliga avvägningen är igenkänning: UUID v4 är en IETF-standard (RFC 4122) som stöds av praktiskt taget varje databas, programmeringsspråk och API-ramverk direkt. CUID2 är en community-standard med växande men inte universellt stöd. Välj UUID v4 när interoperabilitet med externa system är avgörande; välj CUID2 när du kontrollerar båda ändarna och föredrar kortare, URL-säkra ID:n.
Vem använder CUID2
CUID2 har fått snabb spridning i JavaScript- och TypeScript-ekosystemet:
- Prisma — det mest populära TypeScript-ORM:et använder CUID2 som rekommenderat standard för
@id-fält med@default(cuid())i Prisma Schema v2+. - PlanetScale — deras dokumentation och startmallar rekommenderar CUID2 för applikationsgenererade primärnycklar för att undvika prestandaproblem vid sekventiell genomsökning på deras distribuerade MySQL-plattform.
- Drizzle ORM — tillhandahåller en inbyggd
cuid2()-standardhjälp för kolumndefinitioner. - tRPC-boilerplates — många community-mallar för tRPC + Prisma levereras med CUID2 som primärnyckels-strategi.
- T3 Stack — create-t3-app-scaffolding-verktyget använder Prisma med CUID2-standardvärden i genererade schemafiler.
Kodexempel
Det officiella npm-paketet @paralleldrive/cuid2 har ett enkelt 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"Använda CUID2 med Prisma-schema:
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Generera CUID2 i Node.js utan npm-paketet (enbart med Web Crypto API, som det här verktyget gör i webbläsaren):
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"