使用方法
- 在「字符串长度」填 16(示例值,页面允许 1~256 的整数),在「生成条数」填 10(示例值,允许 1~500)。
- 勾选需要的字符集。默认勾了小写字母、大写字母、数字三组共 62 个字符,并勾上「排除易混淆」,把
0 O o 1 I l这 6 个从池里剔掉,所以实际池 = 25 + 24 + 8 = 57 位。 - 需要特定字符时把「十六进制 0-9a-f」或「常用符号」勾上;也可以在「自定义字符」里直接填任意字符,包括中文和 emoji,最多 200 个,会原样拼进池里并参与去重。
- 点「生成字符串」,或在长度、数量输入框里直接按 Enter。结果区出现 10 条字符串,上方统计条同步显示字符池大小、单条熵、取值空间、批量碰撞概率和随机源状态。
- 点「复制结果」一次带走全部,或点「导出 CSV」拿到 12 行「长度 → 熵 / 空间 / 碰撞概率」对照表,用来决定你的场景到底需要多长。
这个随机字符串生成器能解决什么问题
一是给测试数据批量灌值。写单元测试要造 200 个不重复的用户名、订单号、token 占位符,手写既慢又容易撞车。随机字符串生成器一次给出几百条,字符集和长度都可控,勾掉符号就能避开数据库字段的转义问题。
二是回答「这串东西够不够随机」这个具体问题。这是本页和普通随机串工具最大的区别:页面顶部实时显示单条熵(bits)、取值空间和批量碰撞概率三个数。长度 16、池 62 时单条熵 95.3 bits;但如果你把字符集缩小成纯数字(池 10),同样 16 位只有 53.2 bits——长度没变,安全性差了一倍多,因为熵是长度和池大小的乘积效应。
三是算清「批量去重有没有必要」。生日问题告诉我们:池 62、长度 16 时生成 1000 条,出现至少一次重复的概率低到可以忽略;但如果池缩到 10、长度缩到 6,生成 1000 条的碰撞概率是 39.32%——接近一半会重复,这时候必须开去重。页面把这个概率直接算给你看,不用凭感觉。
四是避开取模偏差这个隐蔽陷阱。用 Math.random() % 62 取字符时,232 除不尽 62,末尾几个字符会被抽到得更多。本页用 crypto.getRandomValues 配合拒绝采样消除这个偏差,具体阈值和丢弃比例在下面给出。
本页不做这些事:不生成密码(不评估强度、不检查泄露库、不做组成规则校验)、不生成 UUID/GUID 等有特定格式的标识符、不做中文姓名或词语拼装。要生成强度达标的口令请用「密码生成器」;要标准 UUID 格式请用「UUID生成器」;只要纯数字随机数请用「随机数生成器」。
原理和变量:随机源、拒绝采样与熵
第一,随机源。本页只用一行代码取随机数:crypto.getRandomValues(new Uint32Array(n))。它是浏览器内置的加密随机数接口(W3C Web Cryptography API Level 1 定义),底层取自操作系统的 CSPRNG(Windows 上是 CNG,Linux 上是 getrandom(2) 系统调用),不引入任何第三方随机库。
为什么不用 Math.random()?这是本页和「密码生成器」共同的底层选择,但理由值得说清:Math.random() 在 V8 里由 xorshift128+ 实现,内部状态只有 128 bit,而且整个页面生命周期内状态是连续的——攻击者只要观测到足够多的连续输出(理论上 3 个 32 位输出就能反推 128 位状态),就能预测之后所有的随机值。crypto.getRandomValues 每次调用都从操作系统熵池重新取数,状态不可反推,这是「不可预测」的硬保证。
第二,模偏差与拒绝采样。拿到一个 32 位无符号整数后,不能直接写 n % 62 去取字符下标。原因是 232 = 4294967296 不能被 62 整除:4294967296 ÷ 62 = 69273666 余 4,这意味着下标 0~3 对应的随机区间比 4~61 宽了 1 个取值,前 4 个字符会被多抽到。分布的偏斜极微小(相对偏差约 1.5×10-10),但它是真实存在的。
本页的做法是拒绝采样:先算接受上限 limit = Math.floor(4294967296 / 62) × 62 = 4294967292,随机数落在 4294967292~4294967295 这 4 个取值上就丢弃重抽。丢弃比例是 4 / 4294967296 = 9.313×10-10,接受率 99.999999907%——代价几乎为零,换来严格均匀的分布。各字符集的实测阈值:池 10 → 丢弃 6 个(1.397×10-9);池 26 → 丢弃 22 个(5.122×10-9);池 36 与 62 → 都丢弃 4 个;池 72 → 丢弃 40 个(9.313×10-9);池 2000 → 丢弃 1296 个(3.017×10-7)。而 池 16(十六进制)丢弃 0 个,因为 16 是 2 的幂、恰好整除 232——所有 2 的幂(2/4/8/16/32/64/128/256/512/1024/2048/4096/65536)都零丢弃,这是十六进制适合做短 ID 的技术原因之一。
第三,熵怎么算。单条熵的公式是香农熵的整数形式:H = log2(池大小 ^ 长度) = 长度 × log2(池大小) bits。每字符的熵只取决于字符集大小,与你勾了哪几组无关:池 10 是 3.3219 bits/字符,池 26 是 4.7004,池 36 是 5.1699,池 62 是 5.9542,池 72 是 6.1699,池 2000(常用汉字)是 10.9658,池 65536(整个基本平面)是 16.0000。所以从 62 扩到 72 只多 0.2157 bits/字符,而从 10 扩到 62 多了 2.6323 bits/字符——扩字符集的收益在池小的时候最大。
第四,批量碰撞(生日问题)。生成 n 条长度为 L 的字符串,任意两条相同的概率约等于 1 − exp(−n(n−1) / (2 × 池^L))。这是生日悖论:碰撞概率随 n平方增长,而不是线性。实测:池 62、长度 16 时空间 4.767×1028,生成 1000 条的碰撞概率 <0.0001%(期望重复对数 1.048×10-23);但池 10、长度 6 时空间只有 106,生成 1000 条碰撞概率高达 39.32%;池 16、长度 4 时更是 99.95%,几乎必然重复。
第五,Web Crypto 的配额硬上限。规范规定 getRandomValues 单次调用最多填充 65536 字节的视图,超过就抛 QuotaExceededError。用 Uint32Array 算就是单次最多 16384 个无符号整数。本页按需分批取:生成 1000 条 × 32 字符需要 32000 个随机数(128000 字节),必须分 2 次调用;5000 条 × 64 字符需要 320000 个,分 20 次。这也是本页把条数上限设为 500 的原因之一——再往上分批次数线性增加,用户等待体感明显变差。
完整示例(数值仅作演示)
示例条件与页面打开时的默认值完全一致:长度 16 位、条数 10 条、勾选小写字母 + 大写字母 + 数字、勾选「排除易混淆 0Oo1Il」。逐项实测如下:
- 字符池 = 25(小写去掉
o)+ 24(大写去掉I、O)+ 8(数字去掉0、1)= 57 位; - 单条熵 = 16 × log2(57) = 16 × 5.8329 = 93.3 bits;
- 取值空间 5716 ≈ 5.29×1027 种;
- 这一批 10 条出现重复的概率约 8.6×10-27,实际就是 0 条重复;
- 拒绝采样阈值 =
floor(4294967296 / 57) × 57 = 4294967244,丢弃 52 个取值,丢弃比例 1.211×10-8; - 如果取消「排除易混淆」,池变成 58 位(只多回一个小写
o),熵升到 93.5 bits——差 0.2 bit,几乎没影响。这个选项的价值是人工抄写不易看错,不是提升安全性。
结果区在页面打开时就填着上面这 10 条(服务端直出),你点一次「生成字符串」会得到完全不同的内容——随机工具的结果本来就该每次都不同,这 10 条只是用来确认字符集和长度设置对了、页面能正常工作的演示值,不要当凭据用。
输入范围与对照表
三个数值输入的范围都是刻意设的,不是随手填的:
| 输入项 | 允许范围 | 为什么 |
|---|---|---|
| 字符串长度 | 1 ~ 256 的整数 | 上限 256 是因为页面用 String.prototype.repeat 拼装,再长对绝大多数场景(ID、token、测试数据)没有意义;下限 1 是数学边界,0 位字符串没有信息量 |
| 生成条数 | 1 ~ 500 的整数 | 500 条 × 256 位 = 128000 个字符,需要分 8 次 getRandomValues;再多会明显卡顿,且 CSV 导出后 Excel 也吃不下 |
| 自定义字符 | 0 ~ 200 个字符 | 200 个足够表达「全部 ASCII 可见字符」这个最大常见需求;再多的自定义字符会让「字符集大小」这个统计失去可读性 |
| 字符集勾选 | 至少 1 组 | 一组都不勾且自定义为空时,池大小为 0,log2(0) 无意义、也无法取字符,页面直接拒绝 |
下表固定字符池 62 位(大小写字母 + 数字,不排除易混淆),逐档实测;「批量碰撞概率」是生成 1000 条时至少出现一次重复的概率:
| 行 | 长度 | 单条熵 | 取值空间 62^长度 | 每秒 1012 次穷举 | 1000 条的碰撞概率 |
|---|---|---|---|---|---|
| 1 | 4 位 | 23.8 bits | 1.478×107 | 不到 1 毫秒 | 3.3239% |
| 2 | 6 位 | 35.7 bits | 5.680×1010 | 56.8 毫秒 | 0.0009% |
| 3 | 8 位 | 47.6 bits | 2.183×1014 | 3.6 分钟 | 2.29×10-7% |
| 4 | 10 位 | 59.5 bits | 8.393×1017 | 9.7 天 | 5.95×10-11% |
| 5 | 12 位 | 71.5 bits | 3.226×1021 | 102.2 年 | 1.11×10-14% |
| 6 | 14 位 | 83.4 bits | 1.240×1025 | 39.30 万年 | <0.0001% |
| 7 | 16 位(默认) | 95.3 bits | 4.767×1028 | 1.51×109 年 | <0.0001% |
| 8 | 18 位 | 107.2 bits | 1.833×1032 | 5.81×1012 年 | <0.0001% |
| 9 | 20 位 | 119.1 bits | 7.044×1035 | 2.23×1016 年 | <0.0001% |
| 10 | 24 位 | 142.9 bits | 1.041×1043 | 3.30×1023 年 | <0.0001% |
| 11 | 32 位 | 190.5 bits | 2.273×1057 | 7.20×1037 年 | <0.0001% |
| 12 | 64 位 | 381.1 bits | 5.165×10114 | 1.64×1095 年 | <0.0001% |
第 1 行和第 7 行放在一起看最说明问题:长度 4 位时 1000 条里有 3.3% 的概率会撞车(6 位就掉到 0.0009%),而穷举时间从「不到 1 毫秒」变成「3.6 分钟」只用了 4 个字符。每多 1 位,熵增加 5.95 bit,取值空间乘以 62——所以「稍微长一点」的收益是指数级的。第 11、12 行的穷举年份已经超出宇宙年龄量级,说明再往上加长度对安全性边际收益为零,实际约束会落在存储和协议字段长度上(比如数据库 VARCHAR(32))。
边界与精度
- 字符池为 0 时直接拒绝。所有勾选都取消、自定义也留空时,池大小 = 0,
log2(0)无定义、也无法取字符。页面报错文案是「字符集为空:请至少勾选一组字符(大写/小写/数字/符号/十六进制),或在「自定义字符」里填至少 1 个字符」,而不是静默生成空串。 - 拒绝采样的丢弃是有意设计的,不是 bug。统计条里的「接受率」显示的就是 1 − 丢弃比例。以默认池 57 为例,接受率 99.999998789%。看到「有随机数被丢弃」是正常的,这正是消除模偏差的代价。
getRandomValues单次 65536 字节上限。超过会抛QuotaExceededError。本页按需分批(每批 16384 个Uint32),所以哪怕你勾满 500 条 × 256 位也不会触发。但如果你把本页的 JS 抄去别处用、一次申请超过 16384 个Uint32,就会看到这个异常。- 字符集里的重复字符会被自动去重。在「自定义字符」里填
aaa,池只增加 1 而不是 3。这是必须的——否则同一个字符被算进池两次,熵的统计会偏大,而实际取到的分布仍按去重后的池走,两者对不上。去重用Set按码点处理,所以多码点的 emoji 也会被正确当成 1 个字符。 - 熵的计算是理论值,不含实现缺陷折扣。
长度 × log2(池大小)假设每一位都独立均匀。真实的字符串若被限制首字符不能是数字、或必须含大小写,熵会小一些。本页不强制任何组成规则(那是密码场景的事),所以给出的就是理论上界。 - 「去重后重抽」会略微降低独立性。开启去重时,已生成过的字符串会被排除,这引入了轻微的负相关。理论上它让「恰好 N 条全不同」的概率高于独立同分布时的估计值,所以在安全场景下依然保守(偏安全方向)。
- 浮点精度不是问题。熵用
Math.log2计算,池大小最大 65536+200,取值空间只算log2不算实际的大整数(65536^256远超Number.MAX_SAFE_INTEGER),所以页面显示的是log2换算出的科学计数法,不会溢出也不会丢精度。
常见问题
这个工具能生成安全的密码吗?
技术上它可以做到和密码生成器同等强度的随机性(同一个 crypto.getRandomValues 加拒绝采样),但我建议你用专门的密码生成器。差别在三个地方:一是组成规则,很多网站注册时要求「必须同时含大写、小写、数字、符号」,密码生成器会先按每类各抽一个再补足,天然满足校验;本页不强制,生成结果可能连续十几位都是小写字母而被退回。二是易混淆字符,本页的「排除易混淆」默认只剔 6 个字符,而密码场景通常剔更多。三是强度评估,本页给的是理论熵,不评估你输入的密码是否已在泄露库里。要评估一条现有密码,请用「密码力量检测」。
为什么池 16(十六进制)不需要拒绝采样?
因为 16 是 2 的幂,恰好整除 232:4294967296 ÷ 16 = 268435456 整除,余数为 0,所以没有任何取值落在「余数区间」里,无需丢弃。实测 232 % 16 = 0,丢弃 0 个取值,接受率 100%。所有 2 的幂都这样:2、4、8、16、32、64、128、256、512、1024、2048、4096、65536。这也是「十六进制适合做短 ID」的技术原因——同样的字符数下它不浪费任何随机位。相比之下池 10(纯数字)要丢弃 6 个取值、池 2000(常用汉字)要丢弃 1296 个,比例 3.017×10-7。
单条熵 95.3 bits 到底算多还是算少?
看用途。NIST SP 800-63B《Digital Identity Guidelines》对口令长度给出的最低要求是 8 位,并明确不建议强制「必须含大小写、数字、符号」这类组成规则,更看重长度和唯一性。按这个口径:池 62、长度 8 位是 47.6 bits,对应 2.183×1014 种可能,攻击者每秒试 1012 次也需要 3.6 分钟——这在人类可记忆的范围内,所以 NIST 说 8 位只是底线。而 16 位(95.3 bits)需要 1.51×109 年,32 位(190.5 bits)已经远超宇宙年龄。关键结论是:从 8 位加到 16 位,熵翻一倍(47.6 → 95.3),穷举时间从 3.6 分钟变成 15 亿年;而从 32 位加到 64 位,安全性已经「用不完」了,该关注的是别的问题(能不能存进数据库字段、能不能塞进 URL)。
自定义字符里能填中文和 emoji 吗?
能,填进去会按码点去重并参与抽样,所以一个 emoji(如 😀)算 1 个字符、贡献 1 个位置,池大小加 1。需要注意两点:一是 emoji 在 UTF-8 里占 4 字节,如果你的目标系统按字节数限制字段长度(比如数据库列是 VARCHAR(20) 实际存 20 字节),那 20 个 emoji 会占 80 字节直接超限,页面按字符数算不会提醒你这点。二是组合 emoji 更占位置:ZWJ 序列的家庭 emoji 👨👩👧 由 5 个码点组成(U+1F468、U+200D、U+1F469、U+200D、U+1F467),页面会把它算作 5 个字符,而你在界面上只看到 1 个图形。池大小要按码点算才准确。
我勾了「排除易混淆」,为什么熵只少了 0.2 bit?
因为这个选项剔的是 0 O o 1 I l 共 6 个字符,池从 58 降到 57。按 ΔH = 长度 × log2(58/57) 算,16 位时差 16 × log2(1.0175) = 0.2 bit——几乎可以忽略。它的价值不在安全性,而在可用性:人工把 0 和 O、1 和 l 抄错(或者从 PDF 里复制时字体变形)的概率远高于理论偏差带来的风险。反过来,如果你要的是安全性,扩容量的收益大得多:从纯数字池 10 扩到池 62,每字符多 2.63 bit,16 位就是多 42.3 bit。所以「排除易混淆」该勾,但别把它当安全措施。
生成 1000 条时看到重复了,是不是随机源出问题了?
不一定,先看你选的池和长度。如果池 10、长度 6,1000 条的碰撞概率是 39.32%——接近一半会重复,这是数学必然不是故障。判断方法看统计条:如果碰撞概率显示的是 39.32% 而你看到了重复,说明随机源工作正常,是参数选得太弱;如果碰撞概率显示 0.0001% 以下却仍看到重复,那才是真出问题了,请把条件记下来反馈。解决办法有两条:把长度加到 8 位以上(池 10、长度 8 时空间 108,1000 条碰撞概率降到万分之几),或者勾上「导出前自动去重」,页面会重抽重复项直到条数够——去重会略微牺牲独立性,但在测试数据场景完全可接受。
「熵」这个数在密码学上可靠吗?
作为可比较的指标可靠,作为安全性的证明不够。它算的是「如果每一位都独立均匀随机,长度为 L、字符集大小为 N 的字符串有多少种可能」,即 log2(N^L)。实际系统的熵会低于这个值:PRNG 的状态空间可能有限(Math.random 只有 128 bit 状态,长度超过 16 位后理论熵就超过状态空间了)、字符分布可能不均匀(人倾向于用 abcd1234)、使用模式可能泄露信息。所以 NIST 的建议里,熵是下限参考,真正的判断依据是「长度 + 唯一性 + 不复用」。本页用它做横向对比(同一张表里 4 位 vs 64 位)非常直观,但别把 190 bits 当成「绝对安全 190 bits」的证明。
这些随机字符串会传到服务器吗?
不会。生成过程完全在浏览器里完成,只调用了 crypto.getRandomValues、Math.log2、Math.exp 和 Set 这些原生能力,不发任何网络请求,也没有引入外部随机库。开发者工具的 Network 面板刷新一遍就能确认,HTML、CSS 和本页那个内联 script 之外没有任何请求。生成的字符串不写入浏览器存储(刷新即清空),导出的 CSV 用 Blob 在本地生成,直接进你的下载目录,不经过服务器。但请注意:如果你把结果粘贴到别处(聊天工具、数据库、代码仓库),那就是另一回事了——本工具不负责你粘贴之后的事。
相关工具
- 随机字母生成器——只要字母不要数字和符号时的专用工具,字符集更聚焦,熵按纯字母池计算。
- 随机十六进制生成器——固定 16 进制字符集,因 16 整除 232 而零取模偏差,是短 ID 场景的常见选择。
- 随机数生成器——生成纯数字(可带小数点)的随机值,适合抽签、蒙特卡洛模拟这类场景,不涉及字符集与字符串拼接。
- UUID生成器——按 RFC 4122 生成标准 v4 UUID,带固定的 8-4-4-4-12 格式与版本位,数据库主键场景应该用它而不是本页。
- NanoID生成器——为短 URL 设计的 ID 方案,默认 21 位、字母表含 64 个字符,约 126 bits 熵,比 UUID 短得多。
- 序列号数字生成器——生成带前缀和校验位的业务单号(订单号、工单号),格式约束比随机串多,适合真实业务标识。
- GUID生成器——微软体系的标识符格式,与 UUID 结构不同但用途相近,Windows / SQL Server 环境下更常见。