CUID2 Generator
Generate secure next-generation CUID2 identifiers
Сгенерированные CUID2
Что такое CUID2?
CUID2 (Collision-resistant Unique ID, версия 2) — преемник нового поколения CUID v1, разработанный для генерации коротких, криптографически безопасных и непрозрачных идентификаторов, безопасных для использования в качестве первичных ключей в базах данных, URL и распределённых системах.
В отличие от своего предшественника, CUID2 не раскрывает никакой информации о времени создания, хост-машине или процессе, создавшем его. Каждый идентификатор — это кажущаяся случайной строка, начинающаяся со случайной строчной буквы, за которой следует хэш base-36, полученный из SHA-512. Длина по умолчанию — 24 символа, но её можно настроить от 2 до 32 символов.
CUID2 широко рекомендуется современными инструментарием для работы с базами данных. Prisma принял его как стратегию идентификатора по умолчанию для своего скаляра @default(cuid()), а PlanetScale, Neon и другие бессерверные поставщики баз данных явно указывают CUID2 как предпочтительный формат.
Почему CUID2 Заменил CUID v1
CUID v1, выпущенный в 2012 году Эриком Эллиоттом, был значительным улучшением по сравнению с обычными UUID для генерации идентификаторов на стороне клиента. Однако исследователи безопасности обнаружили две фундаментальные проблемы в его дизайне:
- Fingerprinting: Отпечаток хоста, встроенный в каждое значение CUID v1, мог использоваться для идентификации машины или процесса, создавшего идентификатор, раскрывая операционные метаданные всем, кто мог наблюдать идентификаторы.
- Предсказуемость: Поскольку CUID v1 включал монотонно возрастающий счётчик и сегмент временной метки, злоумышленник, наблюдавший несколько идентификаторов, мог предсказать приблизительный диапазон будущих идентификаторов, что позволяло проводить атаки перечисления против API, использующих идентификаторы как единственную проверку авторизации.
- Некриптографический хэш: CUID v1 использовал простой некриптографический шаг хэширования, не отвечавший современным стандартам безопасности.
Эрик Эллиотт, первоначальный автор, официально объявил CUID v1 устаревшим и создал CUID2 с нуля для решения всех этих проблем. Новый алгоритм использует Web Crypto API (SHA-512) и устраняет все детерминированные компоненты.
Принципы Дизайна CUID2
CUID2 vs CUID v1 — Сравнение
Таблица ниже суммирует ключевые различия между CUID2 и ныне устаревшим CUID v1. Если вы сейчас используете CUID v1, настоятельно рекомендуется перейти на CUID2.
| Атрибут | CUID2 | CUID v1 |
|---|---|---|
| Безопасность | Криптографический (SHA-512) | Некриптографический (на основе отпечатка) |
| Предсказуемость | Непрозрачный — метаданные не раскрываются | Временная метка + отпечаток видны в идентификаторе |
| Длина | Настраиваемая (2–32 символа) | Фиксированная 25 символов |
| Префикс | Случайная буква a–z | Всегда начинается с "c" |
| Распределение | Плоское / равномерное | Монотонно возрастающие сегменты |
| Статус | Активно поддерживается | Устарел по решению первоначального автора |
CUID2 vs UUID v4 — Сравнение
UUID v4 является доминирующим стандартом для случайных уникальных идентификаторов. 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) | Hex + дефисы |
Главный компромисс — знакомость: UUID v4 является стандартом IETF (RFC 4122), распознаваемым практически каждой базой данных, языком программирования и API-фреймворком. CUID2 — это стандарт сообщества с растущей, но не универсальной поддержкой. Выбирайте UUID v4, когда приоритетна совместимость с внешними системами; выбирайте CUID2, когда контролируете оба конца и предпочитаете более короткие, безопасные для URL идентификаторы.
Кто Использует CUID2
CUID2 быстро получил распространение в современной экосистеме JavaScript и TypeScript:
- Prisma — самый популярный TypeScript ORM использует CUID2 как рекомендуемый по умолчанию для полей
@idс@default(cuid())в Prisma Schema v2+. - PlanetScale — их документация и стартовые шаблоны рекомендуют CUID2 для первичных ключей, создаваемых приложением, чтобы избежать проблем с производительностью последовательного сканирования на их распределённой платформе MySQL.
- Drizzle ORM — предоставляет встроенный вспомогательный метод
cuid2()по умолчанию для определений столбцов. - tRPC-шаблоны — многие шаблоны сообщества tRPC + Prisma поставляются с CUID2 в качестве стратегии первичного ключа.
- T3 Stack — инструмент scaffolding create-t3-app использует Prisma с CUID2 по умолчанию в сгенерированных файлах схемы.
Примеры Кода
Официальный 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"Использование CUID2 со схемой Prisma:
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Генерация CUID2 в Node.js без npm-пакета (используя только 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"