登录前后抓包,Cookie 里的 PHPSESSID 完全没变。把登录前那个 ID 发给同事,他打开就是已登录状态。日志里能对上时间点,这种反而好排。

登录成功只写了 $_SESSION['uid'],没有调用 session_regenerate_id。攻击者先把固定 ID 送到受害者浏览器,等对方登录,这个 ID 直接变成有效会话。这一点先确认清楚,后面就不用反复试。

session_start();
// 校验通过后
session_regenerate_id(true);
$_SESSION['uid'] = $uid;

这次卡住的地方就在「会话固定」这一环,单独把它拎出来验证,别和其他改动混在同一次发布里。线上出问题先对时间点,跟发布、备份、任务执行的时刻排一排,能省掉一半功夫。

日志级别临时调高可以,记得改回去,长期开着既占磁盘又掩盖真问题。权限不要一口气开到最大,先加到够用再往上试,范围小才好收。

把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。

处理上按这个顺序:登录校验通过后立刻 session_regenerate_id(true),旧会话文件一并删掉;Cookie 补上 HttpOnly 和 SameSite=Lax,脚本读不到;退出登录先 session_destroy 再清 Cookie。

结果对不对很好判断,再抓一次包,登录后 PHPSESSID 已经换成新值,旧 ID 访问变回未登录。

作者 9823189