登录成功后必须调用session_regenerate_id(true),否则旧会话文件残留,攻击者仍可用原phpsessid访问;需在session_start()后立即执行,并配合session.use_strict_mode=1、httponly、secure等cookie安全设置。

登录成功后必须调用 session_regenerate_id(true)
不带 true 参数的 session_regenerate_id() 只换 ID,不删旧会话文件——攻击者仍能用原来的 PHPSESSID 继续访问,防御完全失效。
- ✅ 正确写法:
session_regenerate_id(true),它会立即销毁旧会话存储(如/tmp/sess_*文件),并生成新 ID - ❌ 错误写法:
session_regenerate_id()或session_id(bin2hex(random_bytes(16))),旧会话残留,等于白换 - ⚠️ 必须在
session_start()之后、用户认证通过之后立刻执行;若放在session_start()前,会报 “headers already sent” 或静默失败 - ⚠️ 若登录逻辑里先写了
setcookie()或输出了任何内容,再调用该函数也会失败——PHP 的 session header 必须在响应头发送前完成
session.use_strict_mode = 1 是防固定攻击的底层开关
这个 php.ini 配置项让 PHP 拒绝所有未初始化的 Session ID——比如攻击者发一个 ?PHPSESSID=abc123 链接诱你点击,服务器不会复用这个 ID,而是强制分配新的、随机的 ID。
- ✅ 开启后,
session_start()遇到未知或无效 ID 时,会丢弃它并新建会话,从根本上堵住固定入口 - ❌ 关闭时(默认值为 0),PHP 会直接接受 URL 或 Cookie 中传来的任意 ID,哪怕它根本没被服务端创建过
- ⚠️ 它和
session_regenerate_id(true)是互补关系:前者防“首次绑定”,后者防“登录后延续”,缺一不可 - ⚠️ 修改后需重启 PHP-FPM 或 Apache,仅改
ini_set()在运行时无效
别忘了配套的 Cookie 安全设置
就算 ID 换得勤、模式开得严,如果 Session ID 能被 JS 读取或 HTTP 明文传输,照样会被 XSS 或嗅探截走。
- ✅ 强制 HttpOnly:
ini_set('session.cookie_httponly', 1),阻止document.cookie读取 - ✅ 强制 Secure:
ini_set('session.cookie_secure', 1),确保只走 HTTPS,杜绝明文传输 - ✅ 禁用 URL 透传:
ini_set('session.use_trans_sid', 0),避免 ID 泄露到 Referer 或日志中 - ⚠️ 这些设置必须在
session_start()之前调用,否则会被忽略
常见误判:为什么换了 ID 还被劫持?
不是 session_regenerate_id(true) 失效,而是它被放在了错误上下文中——最常踩的坑是没关 saveUninitialized 类似逻辑,或者忽略了会话生命周期边界。
- ❌ 在登录页就提前调用
session_start(),又没做严格模式,攻击者可预设 ID 并等你登录 - ❌ 登录成功后没清空旧会话数据(如
$_SESSION['logged_in']仍为 false),导致新会话状态不一致 - ❌ 使用了
session_set_cookie_params()但没配合session_start()重置,新参数对当前请求无效 - ⚠️ 最容易被忽略的是:
session_regenerate_id(true)返回布尔值,但它不抛异常;失败时静默返回false,务必检查返回值或加日志










