以最优雅的方式从无效的会话id中恢复


Recovering from an invalid session id in the most elegant manner

最近,我的日志中出现了以下错误:

PHP Warning:  session_start(): The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in [...] on line [..]
PHP Warning:  Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
PHP Warning:  Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php5) in Unknown on line 0

前面这个问题的答案足以让检测转移这样的场景,这样就不会产生错误,但是我对最优雅的恢复感兴趣;也就是说,以一个有效的新(空)会话结束。

首先,对检测&上一个问题中的导流代码,现在已经是六年前的了:

  1. 这些天,会话更有可能通过cookie单独处理。session.use_only_cookies标志在我的php.ini是启用的,因为我不记得改变它,我认为这是默认值。

  2. 在有效的会话id中使用哪些字符,以及这些字符的数量取决于php.ini中的session.hash_functionsession.hash_bits_per_character值。我的值分别是0和5,这(除非我搞错了)意味着我自己的会话id应该匹配正则表达式/^[a-v0-9]{26}$/

  3. 用于存储会话的cookie的名称可以在php.ini中使用session.name自定义。正确的值总是可以通过session_name()函数检索到的。

考虑到这些,最优雅的转移方法(可能)是:

function my_session_start() {
  if (!isset($_COOKIE[session_name()]) || preg_match('/^[a-v0-9]{26}$/', $_COOKIE[session_name()]) !== 0) {
    return session_start(); // since 5.3, returns TRUE on success, FALSE on failure.
  }
  else {
    return false;
  }
}

至于恢复(它将取代else块中的return false;),对上一个问题的一个回答建议如下:

session_id(uniqid());
session_start();
session_regenerate_id();

然而,我担心的是,这意味着恢复,只有两个赞成,没有评论,是不够的审查。

这个问题的答案表明内部session_start()函数直接依赖于 $_COOKIE[session_name()]的值,而不是该值的其他内部表示。是这样吗?如果是这样,具有检测和恢复功能的my_session_start()函数可以像以下这样简单:

function my_session_start() {
  if (isset($_COOKIE[session_name()]) && preg_match('/^[a-v0-9]{26}$/', $_COOKIE[session_name()]) === 0) {
    unset($_COOKIE[session_name()]);
  }
  return session_start();
}

我只看到这种情况发生在$_COOKIE['PHPSESSID'] === ''。当有人使用Firebug"清除cookie"时,就会发生这种情况。我只想处理这种特殊情况;如果会话ID以其他方式无效,我希望得到警告。处理我的特定用例很简单:

$session_name = session_name();
if (isset($_COOKIE[$session_name]) and empty($_COOKIE[$session_name])) {
    // This happens when someone does "clear cookie" in Firebug; it causes session_start()
    // to trigger a warning. session_start() relies on $_COOKIE[$session_name], thus:
    unset($_COOKIE[$session_name]);
}
session_start();

出于好奇,除了它是一个空字符串之外,你真的需要处理任何场景吗?如果是恶意的(为了触发警告),它只能是其他东西,但是由于您在生产中禁用了display_errors,因此没有人可以从中获得任何信息。