可靠写法是用random_int(0, 999999)生成再sprintf('%06d', $num)补零;校验时需trim、清除非数字、截取前6位并补零比对;session中须存有效期且校验后立即销毁。

php生成六位随机数字的可靠写法
直接用 random_int(),别碰 rand() 或 mt_rand()。前者是密码学安全的,后者在 PHP 7.2+ 已被明确标记为不适用于安全场景——验证码虽不算高危,但一旦被批量打码或撞库,弱随机就是突破口。
常见错误是写成 rand(100000, 999999):它可能返回 100000 到 999999 之间任意整数,看似六位,但实际会漏掉前导零(比如 001234)——而验证码通常要字符串形式展示,且必须严格六位。
- 正确做法:先用
random_int(0, 999999)生成 0–999999 的整数,再用sprintf('%06d', $num)补零 - 不要用
str_pad(),它在处理负数或非数字时行为不稳定,sprintf更确定 - 如果后续要存进数据库字段为
VARCHAR(6),补零后直接存;若字段是INT,就失去前导零——所以务必确认存储和比对逻辑是否一致
验证码校验时大小写和空格怎么处理
用户输入常带空格、换行或全角字符,而 sprintf('%06d', ...) 输出纯数字字符串,二者直接 === 比对必失败。
- 前端提交后,立刻用
trim()去首尾空白 - 用
preg_replace('/[^0-9]/', '', $input)清掉所有非数字(比如中文输入法下的全角数字、字母 O/l 等干扰) - 再截取前 6 位:
substr($cleaned, 0, 6),防止用户多输 - 最后补零比对:
$expected === sprintf('%06d', (int)$cleaned),注意强制转int会自动吞掉非法字符后的数字,但前面已清洗过,这步更保险
为什么不用 uniqid() 或 md5(time())
这些不是随机数生成器,是基于时间戳的唯一标识符,可预测、易碰撞,且长度不可控。有人试过 substr(md5(time()), 0, 6),结果发现连续请求返回的字符串高度相似,甚至重复——因为 time() 秒级精度,在同一秒内调用多次,md5 输入一样,输出就一样。
立即学习“PHP免费学习笔记(深入)”;
-
uniqid('', true)虽带微秒,但依然可推测,不适合做验证码 -
openssl_random_pseudo_bytes()可用,但比random_int()多一层转换(需 bindec + mod),没必要绕路 - PHP 8.2+ 支持
random_string(),但目前覆盖率低,不建议现在依赖
session 存储验证码值时容易忽略的点
验证码值存在 $_SESSION 里没问题,但很多人忘了设有效期或没清空旧值,导致一个验证码能反复用、或新验证码覆盖不及时。
- 写入时加时间戳:
$_SESSION['captcha_value'] = ['value' => $code, 'expires' => time() + 300](5 分钟) - 校验前先检查
isset($_SESSION['captcha_value']['expires']) && $_SESSION['captcha_value']['expires'] > time() - 无论成功失败,校验完立刻
unset($_SESSION['captcha_value']),防重放 - 如果用了 Redis 或数据库存 session,确认其 GC 机制没把未过期的验证码提前清理掉(某些配置下 session.gc_maxlifetime 设得太小)











