PHP 8.6 session 安全默认值变更:use_strict_mode、cookie_httponly、cookie_samesite 都改了
PHP 8.6 的 UPGRADING 文件里,Session 部分有一组向后不兼容变更。核心是三个 INI 默认值转向安全侧,另加 NUL 字节校验、session_encode() 返回值、lazy_write 写入路径的调整。对应 RFC:session_security_defaults。
NUL 字节不再被静默接受
session.cookie_path、session.cookie_domain、session.cache_limiter 被设置为包含 NUL 字节的值时,现在会发出 warning,并且保持该设置不变。
之前的行为是静默接受:
cookie_path和cookie_domain含 NUL 时,SAPI 会直接丢弃Set-Cookie头;cache_limiter含 NUL 时,值会在 NUL 字节处被静默截断。
另外,session_module_name() 的 $name 参数如果是含 NUL 字节的字符串,会抛出 ValueError。
session_encode() 对空会话返回空字符串
session_encode() 对空会话的返回值从 false 改为空字符串 ""。现在只有会话数据无法编码时才返回 false,主要出现在默认序列化 handler 下、某个 key 含有管道符 | 的情况。
依赖 === false 判断“会话为空”的代码需要改成判断 === ''。
lazy_write 下的空会话改为调用 updateTimestamp()
当 session.lazy_write 启用,且 session handler 实现了 SessionUpdateTimestampHandlerInterface 时:读取为空、写入时仍为空的会话,现在会触发 updateTimestamp() 而不是 write()。
之前空会话总是走 write(),因为 session_encode() 返回 false,绕过了 lazy_write 的比较逻辑。如果有自定义 handler 依赖“以空数据调用 write()”这个行为(例如借机销毁会话),需要把同样的逻辑放进 updateTimestamp():
public function updateTimestamp(string $id, string $data): bool
{
if ($data === '') {
return $this->destroy($id);
}
return $this->touch($id);
}
三个 INI 默认值改为安全值
session.use_strict_mode 默认 1
原值为 0。严格模式会拒绝未初始化的 session ID,用于缓解 session fixation。
受影响的是那些之前接受外部传入 ID、但存储中并不存在对应条目的自定义 handler。这类 handler 要么实现 validateId() / create_sid(),要么显式关闭严格模式:
ini_set('session.use_strict_mode', '0');
自定义 handler 侧需要能回答“这个 ID 是否存在”,以及自己生成新 ID:
public function validateId(string $id): bool
{
return $this->storage->exists($id);
}
public function create_sid(): string
{
return bin2hex(random_bytes(16));
}
session.cookie_httponly 默认 1
原值为 0。session cookie 不再能通过 JavaScript 的 document.cookie 读取。如果应用确实要从 JS 里读取 session cookie,需要显式关闭:
ini_set('session.cookie_httponly', '0');
session.cookie_samesite 默认 Lax
原值为未设置。session cookie 不再随跨站请求发送,只有使用安全 HTTP 方法的顶层导航是例外。依赖跨站 POST 提交携带 session cookie 的应用,需要显式设为 None,同时把 session.cookie_secure 设为 1:
ini_set('session.cookie_samesite', 'None');
ini_set('session.cookie_secure', '1');
php.ini 里对应写法:
session.use_strict_mode=0
session.cookie_httponly=0
session.cookie_samesite=None
session.cookie_secure=1
SessionHandler::validateId()
新增了 SessionHandler::validateId(),它委托给当前配置的 save handler。
两点子类相关行为:
- 子类声明
validateId()时如果没写返回类型,会触发 tentativebool返回类型带来的 deprecation notice; - 子类覆盖
open()且没有调用parent::open()时,保持原有行为,并在 ID 被验证时发出 warning。