4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CodeIgniter3でセッションが維持できなくてハマった

Last updated at Posted at 2018-10-10

経緯

アクセスする度にセッションIDが新たに生成されてしまい、小一時間ほど浪費してしまった。

環境

  • XAMPP 7.2.5
  • Apache 2.4.33
  • PHP 7.2.5
  • CodeIgniter3.1.0

原因

このコードが原因だった。

system/core/libraries/Session/Session.php(L131)
// Sanitize the cookie, because apparently PHP doesn't do that for userspace handlers
if (isset($_COOKIE[$this->_config['cookie_name']])
	&& (
		! is_string($_COOKIE[$this->_config['cookie_name']])
		OR ! preg_match('/^[0-9a-f]{40}$/', $_COOKIE[$this->_config['cookie_name']])
	)
)
{
	unset($_COOKIE[$this->_config['cookie_name']]);
}

PHPのバージョンとの組み合わせが原因だった

PHP7.1未満と以降でsession_id()で生成されるsession_idの長さや含まれる文字が違うらしい。
↓CodeIgniter3.2.0(現時点ではdev版て書いてある)では対応済みなのを確認。

CodeIgniter3.2.0ではPHPの設定から正規表現を作るようにしてるっぽい
/**
 * Configure session ID length
 *
 * To make life easier, we used to force SHA-1 and 4 bits per
 * character on everyone. And of course, someone was unhappy.
 *
 * Then PHP 7.1 broke backwards-compatibility because ext/session
 * is such a mess that nobody wants to touch it with a pole stick,
 * and the one guy who does, nobody has the energy to argue with.
 *
 * So we were forced to make changes, and OF COURSE something was
 * going to break and now we have this pile of shit. -- Narf
 *
 * @return	void
 */
protected function _configure_sid_length()
{
	if (PHP_VERSION_ID < 70100)
	{
		$hash_function = ini_get('session.hash_function');
		if (ctype_digit($hash_function))
		{
			if ($hash_function !== '1')
			{
				ini_set('session.hash_function', 1);
			}

			$bits = 160;
		}
		elseif ( ! in_array($hash_function, hash_algos(), TRUE))
		{
			ini_set('session.hash_function', 1);
			$bits = 160;
		}
		elseif (($bits = strlen(hash($hash_function, 'dummy', false)) * 4) < 160)
		{
			ini_set('session.hash_function', 1);
			$bits = 160;
		}

		$bits_per_character = (int) ini_get('session.hash_bits_per_character');
		$sid_length         = (int) ceil($bits / $bits_per_character);
	}
	else
	{
		$bits_per_character = (int) ini_get('session.sid_bits_per_character');
		$sid_length         = (int) ini_get('session.sid_length');
		if (($bits = $sid_length * $bits_per_character) < 160)
		{
			// Add as many more characters as necessary to reach at least 160 bits
			$sid_length += (int) ceil((160 % $bits) / $bits_per_character);
			ini_set('session.sid_length', $sid_length);
		}
	}

	// Yes, 4,5,6 are the only known possible values as of 2016-10-27
	switch ($bits_per_character)
	{
		case 4:
			$this->_sid_regexp = '[0-9a-f]';
			break;
		case 5:
			$this->_sid_regexp = '[0-9a-v]';
			break;
		case 6:
			$this->_sid_regexp = '[0-9a-zA-Z,-]';
			break;
	}

	$this->_sid_regexp .= '{'.$sid_length.'}';
}

解決策

主に2つ。

  1. CodeIgniterのバージョンを3.2.0に上げる
  2. 当該コードを書き換える

※追記
 v3.1.9でFixしているみたいなので3.1.9に上げるのが正しいかもしれません。

両方の手段を試してみて、自分の環境ではどちらでも解決できることを確認しました。
ちなみに自分は前者で対応しました。
サーバ環境的にもPHPのバージョンは7.2以降で進めていますし、開発も序の口なので今のうちに上げておこうと。

当該コードの書き換えに関しては、動作させるサーバ上で生成されるセッションIDのルールに合わせて正規表現を変えるだけで問題ありませんでした。

自分の環境の場合
preg_match('/^[0-9a-f]{40}$/', $_COOKIE[$this->_config['cookie_name']])
// ↓に変更
preg_match('/^[0-9a-z]{26}$/', $_COOKIE[$this->_config['cookie_name']])

引用元

記事内のコードは全てCodeIgniter3.1.0及び3.2.0から引用しています。
CodeIgniter Web Framework

4
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?