3 puntos por GN⁺ 2024-08-27 | 1 comentarios | Compartir por WhatsApp
  • Los UUID tienen 8 versiones, de la v1 a la v8, y que el número sea más alto no significa que sea más nuevo o mejor, sino que RFC 9562 define distintos métodos de generación
  • En la práctica, las opciones habituales se reducen a v4 y v7: v4 es la opción predeterminada para IDs aleatorios y v7 es adecuada cuando se necesita ordenar por momento de creación
  • v1 y v6 usan los mismos componentes, pero v6 reorganiza los campos para que, al ordenarlos, queden en orden cronológico de creación
  • v3 y v5 crean UUID a partir de un hash de los datos de entrada: v3 usa MD5 y v5 usa SHA-1; entre los posibles valores de entrada están DNS y URL
  • En la práctica, conviene usar v7 en lugar de v1·v6 cuando sea posible; si necesitas un UUID basado en datos de entrada, considera v5; y si necesitas un UUID completamente personalizado, revisa v8

Métodos de generación por versión de UUID

  • Las versiones de UUID van de la v1 a la v8 y todas están definidas en RFC 9562
  • UUID basados en tiempo

    • UUID Version 1 se genera con timestamp, un contador monótono y una dirección MAC
    • UUID Version 6 usa los mismos datos que v1, pero cambia el orden para que al ordenar queden por tiempo de creación
    • UUID Version 7 se genera con timestamp y datos aleatorios
  • UUID aleatorios y definidos por el usuario

    • UUID Version 4 se genera completamente con datos aleatorios y es la forma más cercana a lo que mucha gente imagina cuando piensa en un UUID
    • UUID Version 8 es completamente definido por el usuario, salvo los campos version/variant requeridos en todas las versiones
  • UUID basados en hash de datos de entrada

    • UUID Version 3 se genera con el hash MD5 de los datos proporcionados por el usuario
      • Entre los posibles valores de entrada del RFC están DNS y URL
    • UUID Version 5 se genera con el hash SHA-1 de los datos proporcionados por el usuario
      • Igual que en v3, DNS y URL pueden usarse como entradas candidatas
  • UUID reservados

    • UUID Version 2 está reservada para IDs de seguridad y no se conocen detalles públicos

Criterios de elección en la práctica

  • En la mayoría de los casos, la elección real es entre v4 o v7
  • Si solo necesitas un ID aleatorio, v4 es la opción predeterminada adecuada
  • Si el ID debe poder ordenarse, puedes considerar v7
    • Por ejemplo, si usas un UUID como clave de base de datos, v7 puede ser una buena candidata
  • v5 o v8 se acercan más a casos donde quieres incluir tus propios datos dentro del UUID
    • En este tipo de casos, muchas veces el usuario ya sabe de antemano que lo necesita
  • Según el RFC, v7 mejora v1 y v6, así que cuando sea posible conviene usar v7 en lugar de v1·v6
    • Si realmente necesitas v1 o v6, puedes usar v6
  • v2 está reservada para un uso de seguridad no especificado
  • v3 fue reemplazada por v5, que usa un hash más fuerte, y si alguien necesita v3 probablemente ya lo sabe

1 comentarios

 
GN⁺ 2024-08-27
Opiniones en Hacker News
  • Decir que no hay detalles sobre UUID v2 solo aplica si leíste únicamente el RFC, famoso por ser poco claro: https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#t...
    Casi nunca te lo vas a topar, pero también existe un UUID version 0. Vale la pena mencionarlo porque es el origen de los bits reservados que luego permitieron definir otras “versiones” de manera compatible. Dejé recopilada la investigación relacionada en mi biblioteca de UUID: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
    Decidí darle soporte porque está genial, pero todavía tengo que decidir cómo manejar el rollover de fechas y los UID Apollo más antiguos

    • Para ser justos, RFC9562 sí cita dos documentos de especificación de UUID Version 2. Pero RFC4122 me pareció demasiado críptico para mi gusto
      Estoy empezando a trabajar en un RFC informativo sobre los tipos históricos de UUID definidos en el espacio Variant 0-7, que ayude a la gente a entenderlos. Si quieres participar en la discusión o revisar la redacción, puedes ver https://github.com/yocto/draft-yocto-uuid
  • Pude encontrar los detalles en 2 minutos. Haces clic en el enlace del artículo para ir a la sección de definición de DCE en el RFC 9562, entras a la especificación desde el primer enlace de ese párrafo y luego buscas “UUID” para llegar al Apéndice A, donde está todo
    El nombre, de forma confusa, es “Universal Unique Identifier”, pero contiene todo lo necesario. Estaría bien que al menos hiciera clic en los enlaces que puso en su propio artículo

    • Aquí se pueden aprender dos cosas: una es leer la especificación de UUID v2, y la otra es averiguar información contextual del momento de generación a partir de un UUID
      Por el contexto, como se explica que otras versiones se generan a partir de ciertos elementos, esa oración claramente se refiere a lo segundo. La frase es algo ambigua, pero no diría que induce al error
    • Yo seguí exactamente el mismo proceso y, como parecía que no le había prestado mucha atención a los detalles, dejé de leer el artículo de inmediato
      Aun así, el apéndice fue entretenido de leer, como una instantánea de la época
    • Quizá sea un artículo escrito por un modelo de lenguaje
  • Estaría bueno que existiera un estándar de UUID corto como 73WakrfVbNJBaAmhQtEeDv o bK7nP9xM
    En sentido estricto no sería un UUID, porque podría duplicarse en algún lugar, pero quiero una combinación estándar de ID aleatorios que sean lo bastante cortos como para memorizarlos

    • Lo más cercano que se me ocurre es ULID. Son 26 caracteres en base32, es corto, tiene 128 bits y además se puede ordenar lexicográficamente
      Creo que no hay un estándar más popular porque hay que sacrificar algo. 128 bits tiene bajo riesgo de colisión para casi todos los usos, pero mientras más pequeño se vuelve, más hay que evaluar el contexto concreto y el impacto de una colisión, lo que dificulta estandarizarlo. Si usas otras codificaciones como base64 o base85 queda más corto, pero sacrificas cosas como distinción entre mayúsculas y minúsculas y seguridad en URLs: https://github.com/ulid/spec
    • Si necesitas un UUID más compacto, puedes convertir un UUID existente a base64 segura para URL y representarlo en 22 caracteres
      Es solo otra representación del mismo UUID y se puede revertir. Al final, un UUID es un valor de 128 bits, así que no es tanto una conversión real sino una notación alternativa
    • Sqids podría ser lo que buscas. Genera IDs mucho más cortos que un UUID, pero no son universalmente únicos y se generan a partir de secuencias de enteros: https://sqids.org/
    • Normalmente se generan N bits aleatorios y se codifican en base58. Puedes elegir N tan grande como quieras
      Pierdes la ventaja de ordenamiento monótono que tienen algunas versiones de UUID, pero base58 es segura para URLs y no contiene caracteres especiales. El valor todavía se puede almacenar como binario. Por ejemplo, en Postgres puedes usar bytea en lugar de una columna de texto
    • Está en marcha un trabajo para estandarizar técnicas alternativas de codificación que permitan representar UUID de 128 bits en forma de texto más corta
      La discusión se puede ver aquí: https://github.com/uuid6/new-uuid-encoding-techniques-ietf-d...
  • El timestamp de UUID v7 fue un gran cambio para Databend. Lo usamos para encontrar rápidamente archivos de metadatos por timestamp en AWS S3, así que operaciones como vacuum se volvieron mucho más rápidas
    PR: https://github.com/datafuselabs/databend/pull/16049

    • Es interesante, pero no se me ocurriría usar UUID para buscar por tiempo. Yo pondría un campo de timestamp separado
      La gran ventaja de los UUID ordenados por tiempo es que mejoran la localidad. Al insertar nuevos elementos en un índice, por lo general terminas anexándolos al final, lo que puede ser más barato que inserciones aleatorias. Aunque también puede aumentar la contención, así que vale la pena considerar un híbrido que ponga algunos bits aleatorios antes del timestamp para crear “shards” ordenados. Además, las lecturas suelen concentrarse en los datos más recientes, así que es útil que esos datos estén juntos y se carguen bien en caché
    • También es muy bueno para almacenes clave-valor como DynamoDB. Es mucho más limpio que una clave compuesta con un timestamp o una fecha ISO como prefijo, y también mejor que desperdiciar un índice secundario para timestamps
  • Es difícil entender el propósito de uuid2. Ni siquiera sabía que existían más tipos como estos, y vi uuid2 por primera vez cuando le pedí a Xandr que borrara mi información personal: https://news.ycombinator.com/item?id=40913915
    Incluso leyendo Wikipedia, no me queda claro por qué crear algo llamado “identificador único universal” y aun así tener varios tipos, y por qué algunos de ellos originalmente permitían rastrear hasta la PC. Me pregunto si mezclar parte del código MAC hace que uuid2 sea más cercano a lo aleatorio, o si hay otra razón. Desde el punto de vista de la privacidad, también me pregunto si no bastaría con usar identificadores largos con muchísimos caracteres posibles para que la probabilidad de colisión sea prácticamente nula.

    • El propósito original era identificar mensajes en la arquitectura de computación distribuida Apollo. Los UID, y luego los UUID, eran una forma reversible de marcar la intersección de dos dimensiones.
      Cualesquiera dos máquinas podían generar el mismo UID/UUID para las mismas dos entradas, y quien recibía el mensaje identificado podía revertir el identificador a sus componentes originales. Como se diseñó como etiqueta para mensajes transitorios, las dos dimensiones eran el tiempo y el ID de hardware; al principio se usaban números de serie de Apollo, y más tarde direcciones de hardware Ethernet, entre otras cosas.
      Creo que gran parte de la confusión viene de que, en la implementación inicial de AEGIS, los ingenieros de Apollo empezaron a usar UID “canned”, es decir, estáticos y bien conocidos, para identificar elementos del sistema de archivos. Con el tiempo, el uso común de los UUID se desplazó por completo: de identificadores transitorios donde las colisiones estaban previstas, a identificadores canned donde había que evitarlas, y las dos dimensiones pasaron a ser aleatoriedad y más aleatoriedad.
      La historia es más compleja. Microsoft contrató a una de las personas clave de Apollo para crear MSRPC para Windows NT, y de ahí también surgieron los GUID. Los GUID tienen una disposición de campos distinta a la de los UUID y, a diferencia de lo que dicen muchas fuentes, no son de endianidad mixta. Microsoft suele usar GUID canned no solo para identificar mensajes RPC transitorios, sino para casi cualquier cosa que necesite identificadores bien conocidos, como clases COM, códecs multimedia, etc. Ejemplo: https://gix.github.io/media-types/
      Perdón por enlazar dos veces mi repositorio en el mismo hilo de comentarios, pero empecé a recopilar esta historia en el README de mi biblioteca de UUID y debería retomarlo. Apollo empezó en 1980 y el borrador del RFC de UUID de Leach/Salz recién apareció en 1998, así que hay muchísima historia que quedó fuera de los estándares modernos: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
  • UUID v4 no es más que un generador de bytes aleatorios con guiones en posiciones determinadas. No hace falta usarlo; se puede ahorrar espacio generando bytes aleatorios directamente.
    También se reducen cosas innecesarias como los guiones y la información de versión.

    • Un UUID es un número de 128 bits, y la representación en cadena con guiones es solo una de las varias formas de representar ese número.
      Es parecido a una dirección IPv4, que es un número de 32 bits, donde la notación de “cuatro grupos separados por puntos” es una de sus representaciones. Si piensas en un UUID como un formato de cadena, ya tienes mal entendido el concepto más básico de UUID. Incluso si solo quieres un identificador aleatorio, me parece elegante que un UUID aleatorio tenga unos pequeños bits de bandera que indiquen “esto fue pensado como aleatorio”. Es útil cuando encuentras un identificador aislado y sin contexto.
    • UUID v4 también fija 4 bits con un valor constante para indicar que es versión 4.
      Se puede debatir si es útil crear espacios de nombres distintos entre los diferentes métodos de generación, pero la probabilidad de que un generador aleatorio común produzca un UUIDv4 válido es solo de 1/16. Claro que, si quieres hacer tu propio generador de UUID, configurar correctamente los bits es algo trivial.
    • Es cierto, pero para la mayoría de los desarrolladores el atractivo está en lo simple que es implementarlo. Casi todos los lenguajes tienen una biblioteca de UUID que funciona con una sola línea.
      En Go basta con uuid.New().String(), pero leer datos aleatorios con crypto/rand y convertirlos a base64 o hex requiere más líneas y más esfuerzo.
  • Recomiendo no usar las versiones basadas en MAC. En teoría eso podría aplicar a todas salvo v4 y v7, pero v1 es la peor.
    v3 también tiene el problema de que MD5 está gravemente roto.

    • MD5 está “roto” como función hash criptográfica. Como función hash no criptográfica, sigue estando perfectamente bien.
  • No conocía los detalles fuera de la versión 4, pero lo que de verdad parece faltar y sería útil es un método que use datos SHA256 y un contador, algo parecido a PBKDF2.
    Podría servir como identificador derivado que preserve la privacidad, y también permitiría demostrar de forma laxa que un UUID determinado se derivó de cierta semilla.

    • Si de verdad lo necesitas, puedes ver UUIDv4 como un formato de codificación, generar una salida de 122 bits con un algoritmo criptográfico y luego codificarla como UUID.
      Más allá de eso, probablemente vas a querer una salida más larga.
    • Sería algo parecido a v3, pero con la posibilidad de especificar el algoritmo hash.
  • Simplemente usa v7.
    Ahora es el turno de que los expertos en seguridad digan que no.

    • Si la fecha de generación puede ser información sensible o el UUID debe ser completamente impredecible, usa v4. Para todo lo demás, usa v7.
    • Lo que siempre me ha parecido una carencia de UUID y ULID es que, hasta donde sé, no hay una buena forma de generarlos de manera determinista.
      Para muchos usos sería muy útil poder volver a procesar los datos y generar el mismo ID, pero no conozco una forma estándar de lograrlo.
    • No entiendo por qué se toman tan a la ligera los problemas relacionados con la seguridad.