智享技巧屋
第二套高阶模板 · 更大气的阅读体验

网络标识符生成如何防止被预测

发布时间:2025-12-13 06:19:36 阅读:287 次

在开发网站或应用时,很多人习惯用简单的递增数字作为用户ID、订单号这类网络标识符。比如第一个用户是1001,第二个就是1002。看着方便,其实藏着风险。

为什么连续编号不安全?

试想你刚注册一个平台,系统分配的用户ID是892374。过两天再看,发现最新用户的ID已经到893000了。心里大概就有数了:这个平台每天新增用户也就几百人。数据规模暴露了,竞争对手一算就知道你的增长情况。

更危险的是,某些接口如果没做权限校验,攻击者拿着连续的ID遍历请求,可能就能扒出其他用户的信息。比如订单详情页链接是 order.php?id=1001,改成1002也能访问,这就等于把数据库一条条往外送。

用随机但可控的方式生成ID

解决办法不是不用标识符,而是让它们看起来“没规律”。UUID 是个常见选择,一长串字母加数字,像 550e8400-e29b-41d4-a716-446655440000 这种。每次生成几乎不会重复,也很难猜出下一个是什么。

但UUID太长,存数据库占空间,URL里也不好看。这时候可以考虑短唯一标识方案,比如基于时间戳混入随机数和机器标识来生成。

function generateId() {
    const timestamp = Date.now().toString(36); // 转成36进制减少长度
    const randomStr = Math.random().toString(36).substr(2, 5);
    const pid = process.pid.toString(36);
    return `${timestamp}${randomStr}${pid}`;
}

这样出来的ID既包含时间因素便于排序,又有随机部分防猜测,还带上进程号避免集群冲突。长度比UUID短,又不像自增ID那样暴露信息。

别忘了业务场景的实际需求

有些公司喜欢把订单号设计成带日期的格式,比如20240405SH123456。前八位是下单日期,中间是地区代码,最后是序列号。本意是方便人工识别,可这也等于告诉外界:我这个订单是哪天下的,来自哪个区域,当天第多少单。

如果非要用这种结构,序列号部分至少得用随机偏移或者加密序列,别直接从000001开始累加。可以用类似雪花算法(Snowflake)的思路,在分布式环境下生成趋势递增但不可预测的ID。

关键点在于,生成的标识符对外表现要像“杂乱无章”,但内部系统又能高效处理和索引。安全性和实用性得兼顾。

小改动,大防护

改掉自增ID的习惯不需要推倒重来。可以在现有系统外加一层映射表,外部看到的是随机字符串,内部还是用原来的数字主键关联。这样老代码不动,风险也降下来了。

比如用户访问 /profile/X9g2kLqA,后台查一下这个X9g2kLqA对应数据库里的ID 1001,数据照取,只是外面看不到真实编号。这种做法叫“逻辑ID”或“外部标识符”,很多大厂都在用。