PHP 7.4时间戳校验漏洞源于业务代码缺陷:未识别单位、缺失类型/范围校验、忽略时区上下文,导致解析错误或逻辑绕过;须统一归一化为秒级、严格验证、强制@前缀构造DateTime并以UTC存储。

PHP 7.4 时间戳校验漏洞,本质不是 PHP 自身的 CVE 漏洞,而是业务代码中对前端传入时间戳缺乏单位识别、类型校验、范围控制和时区上下文处理,导致时间被错误解析(比如把毫秒当秒用)、逻辑绕过(如跳过有效期检查)、甚至注入式误判(如构造超大数值触发整数溢出或类型转换异常)。
明确时间戳单位并强制归一化
前端 JS 的 Date.now() 返回毫秒级,PHP 处理函数(DateTime::__construct('@...')、date())只认秒级。不区分就直接用,结果可能是公元 5 万年。
- 用正则粗筛格式:
preg_match('/^\d{10,13}$/', $ts),排除含字母、小数点、负号等非法字符 - 按长度判断单位:
strlen((string)$ts) === 13→ 极大概率是毫秒;=== 10→ 更可能是秒 - 统一转为秒级整数:
$ts = (int)round($ts / ($len === 13 ? 1000 : 1));,不依赖隐式转换
严格类型与范围校验
$_GET 或 $_POST 中的时间戳永远是字符串,且可能被篡改。不能直接参与运算或传给 DateTime 构造器。
- 显式转整型:
$ts = (int) $_GET['ts'];,再立刻验证是否“真整数”:if ((string)$ts !== $_GET['ts']) { die('非法时间戳'); } - 范围限制:秒级时间戳合理区间约在
946656000(2000-01-01)到2147483647(2038-01-19,32 位系统上限),超出即拒绝 - 更安全做法:
$ts = filter_var($_GET['ts'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 946656000, 'max_range' => 2147483647]]);
用 DateTime 构造时必须带 @ 前缀
这是最容易忽略也最致命的一环。不加 @,PHP 会把数字当成日期字符串解析(例如把 1712345678 当作“1712年3月4日”),结果完全错乱。
立即学习“PHP免费学习笔记(深入)”;
- ✅ 正确写法:
$dt = new DateTime('@'.$ts);—— 明确告知这是 Unix 时间戳(秒级 UTC) - ❌ 错误写法:
new DateTime($ts)、date('Y-m-d', $ts)(无类型保障,易受 ini 设置影响) - 后续需转本地时区?用
$dt->setTimezone(new DateTimeZone('Asia/Shanghai'));,别手动加减 8 小时
补上时区与上下文意识
时间戳本身无时区,但业务含义有。比如“订单创建时间”应存为 UTC,“用户设置的有效期截止时间”可能按客户端所在时区解释。
- 所有入库时间戳统一用 UTC 存储,避免夏令时/跨区混乱
- 对外输出前,根据业务场景选择时区:
$dt->setTimezone(...)->format('Y-m-d H:i:s') - 关键逻辑(如 token 过期判断)务必用
$dt->getTimestamp()获取秒级值比对,不要用strtotime()再次解析



















