For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/proposal-uuid.md.
  • 简体中文
  • UUID ?

    提案概览
    提案速览

    该提案通过引入标准库 API,解决了 JavaScript 中生成 RFC 4122 UUID 的常见需求。它最初提供了一个用于版本 4 UUID 的 randomUUID() 方法,并由密码学安全随机源支持。getRandomValues()` 作为随机性基础的使用上。该提案已移至 WICG 继续开发。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    ECMAScript 提案:JavaScript 标准库 UUID

    ⚠️⚠️ 更新 2021:本提案现已在以下地址推进: https://github.com/WICG/uuid ⚠️⚠️

    状态:Stage 1

    作者

    概要

    JavaScript 标准库 UUID 描述了一个用于生成基于 IETF RFC 4122 的字符编码的通用唯一标识符(UUID)的 API,可在 JavaScript 引擎中导入使用。

    动机

    UUID 生成是一个极其常见的软件需求

    npm 上的 uuid 模块 目前每月约有 64,000,000 次下载,并且被超过 2,600,000 个仓库依赖(截至 2019 年 6 月)。

    uuid 模块的普遍存在表明 UUID 生成是 JavaScript 软件应用的常见需求,这使得该功能成为标准库的合适候选。

    开发者“重新发明轮子”可能是有害的

    未接触过 RFC 4122 的开发者可能自然会选择发明自己的 UUID 生成方法,可能会使用 Math.random()(在 TIFU by using Math.random() 中,有关于生成 UUID 时应使用密码学安全伪随机数生成器(CSPRNG)的深入讨论)。

    引入一个强制要求使用 CSPRNG 的 UUID 标准库,有助于保护开发者免受安全陷阱的影响。

    概述

    UUID API

    UUID 标准库提供了一个生成 RFC 4122 标识符的 API。

    UUID 库最初支持的唯一导出是 randomUUID(),该方法实现了版本 4 “从真正随机或伪随机数创建 UUID 的算法”,并返回字符串表示形式(如 RFC-4122 所述)。

    // 我们尚不确定如何访问该 API(是在全局对象中,还是在未来的内置模块中),这将是我们继续推进提案时的调查过程的一部分。
    randomUUID(); // "52e6953d-edbe-4953-be2e-65ed3836b2f0"

    Math.getRandomValues()

    Math.getRandomValues() 暴露了与 W3C crypto.getRandomValues() 推荐标准相同的 API,并具有相同的随机性质量保证:

    实现应使用以高质量熵(例如来自操作系统熵源,如 "/dev/urandom")作为种子的成熟的密码学伪随机数生成器来生成密码学随机值。本规范对密码学随机值中存在的信息论熵不设下限,但实现应尽最大努力提供尽可能多的熵。

    Math.getRandomValues() 将作为实现 UUID 算法的基础,提供单一可模拟的(参见 #25)随机性来源。

    范围之外

    RFC 4122 中描述的其他算法(除版本 4 外)最初不支持。

    我们收集的统计数据(参见 analysis/README.md)表明版本 4 算法使用最广泛:

    算法版本仓库数%按观看数加权%
    v4431577.0%14980289.5%
    v1122821.9%162199.7%
    v5510.9%12900.8%
    v3110.2%1160.1%

    关于其他 UUID 版本

    虽然其他 UUID 版本也有其用途,但我们主张从一个最小的 API 表面开始,以支持大部分用户(版本 4 UUID 的字符串表示形式)。

    如果后来的研究和/或用户反馈表明添加其他功能(如版本 1、3 和 5 UUID)会有价值,本提案不排除这些添加。

    使用场景

    社区中的人们如何使用 RFC 4122 UUID?

    为数据库条目创建唯一键

    生成虚假测试数据

    写入临时文件

    常见问题解答

    uuid 进入标准库有哪些优势?

    • uuid 模块被 GitHub 上超过 2,600,000 个仓库依赖(2019 年 6 月)。保证一个安全、一致、维护良好的 UUID 实现对数百万开发者有价值。
    • 12 kb 的 uuid 模块每月从 npm 下载超过 62,000,000 次(2019 年 6 月);将其放入标准库最终会在全球节省 TB 级带宽。如果我们继续通过标准库满足用户需求(如 uuid),带宽节省将累积起来。

    v4 UUID 有多独特?

    如果忽略随机数生成中涉及的挑战,那么 v4 UUID 对于绝大多数严格要求的使用场景来说已经足够独特。例如,在 3.3 万亿个版本 4 UUID 中发生碰撞的几率(相当于以每秒一百万个 UUID 的速度生成 104 年)约为百万分之一(p = 0.000001)。来源

    话虽如此,随机数生成器的质量对于唯一性至关重要。有缺陷的 RNG 实现已经导致了现实系统中的 UUID 碰撞。正是出于这个原因,本规范规定任何使用的随机数必须来自“密码学安全”的来源,从而(希望)避免此类问题。

    为什么将导出命名为 randomUUID() 而不是像 uuidV4() 这样的名称?

    正如讨论中指出的,v4 UUID 具有在 IETF RFC 4122 中定义的合法 UUID 可能的最大熵。

    IETF RFC 4122 中定义的 UUID 是遵循特定字节布局的 128 位数字。所有 UUID 都包含一个由 4 位组成的“版本”字段和一个由 2 位组成的“变体”字段,这意味着 128 位中有 6 位被保留用于元信息。

    由于 v4 UUID 被定义为所有剩余的 122 位都设置为随机值,因此不可能有另一个 UUID 版本包含更多随机性。

    虽然任何包含 v4 的名称都需要对 UUID 规范中“版本”一词的复杂含义有较深理解,但 randomUUID() 这个术语似乎对 v4 UUID 更具描述性。

    v1 UUID 不是更好吗?因为它们保证唯一。

    简单来说,v1 UUID 由两部分组成:高精度 timestampnode 标识。 IETF RFC 4122 包含了几项旨在确保生成的 v1 UUID 唯一的要求。

    • 时间戳具有 100 纳秒的分辨率,并且实现被要求在单个 node 上以高于 10M/秒的速率生成 UUID 时抛出错误或停止。实际上,这只能在单个主机上的单个线程/进程内强制执行。跨多个进程/主机强制执行则需要与 UUID 规范的主要论点 相悖的非平凡架构:"使用 UUID 的主要原因之一是无需中央机构来管理它们"
    • RFC 首选的生成 node 值的机制是使用主机系统的 IEEE 802 MAC 地址。在撰写 RFC 时,MAC 地址可以合理地预期是唯一的,但现在情况已非如此,尤其是在虚拟机和容器激增的情况下,MAC 地址可能 设计上 就不唯一。

    因此,在实践中,现代实现会在每次进程启动时生成一个随机的 48 位 node 值,导致 node 部分碰撞的概率为 248 分之一。在这种不太可能发生的碰撞事件中,只需 75 毫秒 就能以每秒 1M 的速率生成重复的 v1 UUID。因此,虽然也不太可能,但就像 v4 UUID 一样,没有实际保证 v1 UUID 是唯一的。

    v1 UUID 是否存在隐私问题?

    如果实现遵循 RFC 4122 的主要建议,那么 v1 UUID 确实会泄露创建它们的主机的硬件 MAC 地址。如上所述,在现代 JavaScript 实现中,这很可能不会发生,因为硬件 MAC 地址要么不可用(浏览器、无服务器函数),要么不一定唯一(容器)。然而,有传言称 MAC 地址的存在导致了 Melissa 病毒作者被捕,并且根据手册,甚至 MySQL 8.0 在某些操作系统上仍使用硬件 MAC 地址

    无论如何,任何 v1 UUID 的确切创建时间都会包含在 UUID 中。仅此一点就可能成为许多用例的隐私或数据保护问题(例如,泄露用户账户的创建时间戳),因此这是选择使用 v1 UUID 时需非常谨慎的另一个原因。

    其他语言/库如何处理 UUID?

    一些其他语言/库也使用“random”来描述版本 4 UUID(goJavaC++ Boost)。

    除此之外,其他语言/库对 UUID 的采纳似乎相当不一致:

    • deno 在其标准库中添加了 UUID,但省略了 v3。UUID 创建的代码基本上是从 uuid npm 模块 复制而来,因此方法命名遵循 vX 方案。
    • Java 提供了生成 v3UUID.nameUUIDFromBytes())和 v4UUID.randomUUID())UUID 的方法,但不支持 v1v5。进一步调查为什么选择这些算法将会很有趣,因为一方面基于时间的 UUID(v1)似乎比基于名称的(v3/v5)UUID 使用更广泛,另一方面对于基于名称的 UUID,RFC 已经推荐 v5 而非 v3
    • C++ Boost 对于基于名称的 UUID 默认使用 v5 而非 v3,但在其实现中预期 v5(使用 SHA-1 进行哈希)将被使用不同哈希算法的更新的基于名称的 UUID 版本所取代("In anticipation of a new RFC for uuid arriving…")。
    • Google 的 Go 实现 选择将 v1 作为“默认”导出,其生成器方法名为 NewUUID(),而其他暴露的方法名称更接近我们提议的抽象:v4NewRandom()v3NewMD5()v5NewSHA1()
    • Python 提供了为所有 4 个版本生成 UUID 的方法,方法名以版本命名(uuid.uuid1()uuid.uuid3()uuid.uuid4()uuid.uuid5()),此外还有一个 UUID 类用于表示 UUID 并将其转换为各种表示形式。
    • Rust 提供了为所有 4 个版本生成 UUID 的方法,方法名以版本命名(Uuid::new_v1()Uuid::new_v3()Uuid::new_v4()Uuid::new_v5()),这些方法是 Uuid 类的静态成员,该类用于表示 UUID 并将其转换为各种表示形式。

    待办事项

    • 确定推进添加的 Champion(stage-1)
    • 概述问题或需求以及解决方案总体形态的散文(stage-1)
    • 使用示例(stage-1)
    • 高级 API(stage-1)
    • 初始规范文本(stage-2)
    • Babel 插件(stage-2)
    • 完成规范文本并获得审阅者签字(stage-3)
    • Test262 验收测试(stage-4)
    • 带有集成规范文本的 tc39/ecma262 拉取请求(stage-4)
    • 审阅者签字(stage-4)

    参考