Оценте/поругайте класс для работы с сессией

ps2007

Новичок
Оценте/поругайте класс для работы с сессией

Собственно говоря, у меня нет вопроса, просто хочу поделиться своей работой и заодно услышать критику.
Написал для себя класс для работы с сессиями. Решает следующие проблемы безопасности: угон сессии, фиксация сессии, есть возможность автоматически регенерировать идентификатор сессии. Дополнительные фичи: "привязка" сессии к браузеру и IP адресу пользователя, lazy evaluation.
Методы:
function getVal($key) - получить значение элемента $key
function setVal($key, $value) - присвоить элементу $key значение $value
function unsetVal($key) - уничтожить элемент $key
function cutVal($key) - получить значение элемента $key и удалить его из сессии
function clear() - удалить все элементы, которые хранятся в сессии
function regenerateID() - сгенерировать новый идентификатор сессии (этот метод понадобится, если отключена автоматическая регенерация идентификатора сессии)

Вот простой пример использования:
PHP:
require_once('class.CNativeSession.php');

$sess = new CNativeSession();

$now = $sess->getVal('now');
$count = $sess->getVal('count');
echo "now=$now, count=$count<br>\n";

$sess->setVal('now', time());
$sess->setVal('count', (int)$count+1);

Ну и сам код вышеописанного класса:
PHP:
<?
// todo: сделать конфигурирование

class CNativeSession
{
	protected $sessionName = '_encrypted';
	protected $lifetime = 3600;
	protected $checkIP = TRUE;
	protected $autoRegenerateID = TRUE;
	
	protected $running = FALSE;

	public function __construct()
	{
		ini_set('session.use_cookies', 1);
		ini_set('session.use_only_cookies', 1);
		ini_set('session.gc_maxlifetime', $this->lifetime);
	}

	public function getVal($key)
	{
		if (!$this->running) $this->start();

		if (isset($_SESSION[$key])) {
			return $_SESSION[$key];
		}else{
			return NULL;
		}
	}
	
	public function setVal($key, $value)
	{
		if (!$this->running) $this->start();

		$_SESSION[$key] = $value;
	}

	public function unsetVal($key)
	{
		if (!$this->running) $this->start();

		unset($_SESSION[$key]);
	}

	public function cutVal($key)
	{
		$val = $this->getVal($key);
		$this->unsetVal($key);

		return $val;
	}

	public function clear()
	{
		if (!$this->running) $this->start();

		$_SESSION = array();
	}

	// Метод стартует сессию, проверяет время жизни сессии и идентификатор клиента
	protected function start()
	{
		$this->phpSessionInit();
		
		if ($this->autoRegenerateID) $this->regenerateID();
		
		if ($this->isExpired() || $this->isWrongFingerprint())
		{
			if (!$this->autoRegenerateID) $this->regenerateID();
			$_SESSION = array();
		}
		
		$this->running = TRUE;
	}

	// Метод инициализирует механизм сессий PHP
	protected function phpSessionInit()
	{
		if (!isset($_SESSION)) {
			session_name($this->sessionName);
			session_start();
		}else{
			$sn = session_name();

			// ????? сделать обработку ошибок
			if ($sn != $this->sessionName) die('Error: hostile session "'.$sn.'" already started.');
		}
	}

	public function regenerateID()
	{
		session_regenerate_id(TRUE);
	}

	protected function isExpired()
	{
		$la = '__lastActivity';
		
		$now = time();
		$limit = $now - $this->lifetime;
		
		if (isset($_SESSION[$la]) && $_SESSION[$la] < $limit)
		{
			return TRUE;
		}
		
		$_SESSION[$la] = $now;
		return FALSE;
	}

	protected function isWrongFingerprint()
	{
		$cf = '__clientFingerprint';
		
		$fingerprint = $_SERVER['HTTP_USER_AGENT'].
			$_SERVER['HTTP_ACCEPT_LANGUAGE'].
			$_SERVER['HTTP_ACCEPT_CHARSET'].
			$_SERVER['HTTP_ACCEPT_ENCODING'].
			$_SERVER['HTTP_CONNECTION'];
			
		if ($this->checkIP) $fingerprint .= $_SERVER['REMOTE_ADDR'];
		
		if (!isset($_SESSION[$cf]))
		{
			$_SESSION[$cf] = md5($fingerprint);
			return FALSE;
		}
		
		if ($_SESSION[$cf] != md5($fingerprint)) return TRUE;
		
		return FALSE;
	}


}

?>
Если есть вопросы - задавайте.
 

zerkms

TDD infected
Команда форума
1. Покажи, как у тебя будет выглядеть аналогичная конструкция:

PHP:
$_SESSION['key']['key2']['key3'] = array('key4' => 'value');
echo $_SESSION['key']['key2']['key3']['key4'];
;-)

2. и, кстати, а зачем тебе метод isExpired(), если есть стандартный сборщик мусора?

3. и ещё - вот ты установил ini_set('session.gc_maxlifetime', $this->lifetime);, но не установил путь до хранения СВОИХ сессионных данных. намёк ясен? :)

4. ну и последний вопрос: раз уж ТВОЙ КЛАСС не позволяет прозрачно сменить хранилище, то какой в нём смысл?
 

ps2007

Новичок
1. Интересная задачка :) Разве такое встречается в реальных проектах ?
PHP:
$sess = new CNativeSession();

$tmp = $sess->getVal('key');
$tmp['key2']['key3'] = array('key4' => 'value');
$sess->setVal('key', $tmp);

$tmp = $sess->getVal('key');
echo $tmp['key2']['key3']['key4'];
2. Дело в том, что стандартный сборщик мусора отрабатывает в общем случае не каждый раз при старте сессии, т.е. есть вероятность, что сессия проживет дольше, чем нужно.

3. я думал (думаю) , что проблема возникает на "кривых" хостингах (не проверял). Хотя наверное ты прав :)

4. Смысл в том, чтобы без лишних движений при написании кода иметь более защищенное решение, чем стандартные сессии. Пока мне не нужно менять хранилище, но если в будущем мне понадобится, то напишу класс для хранения сессий, скажем, в базе данных. Методы будут точно такие же, так что я смогу запросто сменить один класс на другой.
 

zerkms

TDD infected
Команда форума
3. я думал (думаю) , что проблема возникает на "кривых" хостингах (не проверял). Хотя наверное ты прав :)
кривость хостинга не при чём. если у дефолтного сборщика мусора время меньше, тогда твои сессии соберутся им же :)
4. Смысл в том, чтобы без лишних движений при написании кода иметь более защищенное решение, чем стандартные сессии. Пока мне не нужно менять хранилище, но если в будущем мне понадобится, то напишу класс для хранения сессий, скажем, в базе данных. Методы будут точно такие же, так что я смогу запросто сменить один класс на другой.
где меньше? защита от инъекций и воровства сессий - это функция, которая вызывается единожды после старта.
всё кроме этого твой класс ничего не предоставляет. а по поводу удобства - см. п.1

2. Дело в том, что стандартный сборщик мусора отрабатывает в общем случае не каждый раз при старте сессии, т.е. есть вероятность, что сессия проживет дольше, чем нужно.
кому "нужно"? когда может стоять задача, в которой критично, что сессия прожила дольше на пару минут?
 

autosoft

Новичок
zerkms
Непонял.

На мысли про memcached наводят getVal и setVal вообще-то.

ps2007
PHP:
echo "now=$now, count=$count<br>\n"; 

$sess->setVal('now', time());
Не очень хороший пример.

Особенно если учесть вот это: http://ua2.php.net/manual/en/function.session-start.php
А именно "Note: If you are using cookie-based sessions, you must call session_start() before anything is outputted to the browser".

Может обращаться к session_start нужно в конструкторе CNativeSession ?
 

zerkms

TDD infected
Команда форума
autosoft
человек пишет обёртку над СЕССИЯМИ. при чём здесь мемкэш?
 

ps2007

Новичок
zerkms, после размышлений пришел к таким выводам: :)

1) Согласен с тобой о неудобстве использования при вставке/извлечения данных из сессии.

2) Еще один момент, о котором я забыл: время жизни куки опирается на время, которое установлено на клиентском компьютере. Т.е если оно неправильно, то куки будут удаляться (в определенных условиях). Поэтому я решил сделать свой механизм.

3) Согласен, что в таком виде управление временем жизни сессии никуда не годится. Но это не значит, что оно не нужно.
Проще использовать что-то вроде
$sess->setLifetime(7200)
чем
ini_set('session.gc_maxlifetime', 7200);
ini_set('session.cookie_lifetime', 7200);
ini_set('session.save_path', '/somepath');

4) Согласен с тем, что безопасность можно обеспечить вызовом одной функции при старте сессии, время жизни сесси выставить 3-мя строчками кода и т.д. Но я хочу готовое решение, которое напишу один раз, а пользоваться буду много раз, не пытаясь вспомнить, какие параметры мне нужно установить для того, чтобы установить время жизни сессии.

Автор оригинала: whirlwind
Бесполезный он потому, что ни от чего не абстрагирует.
Абстрагирует. Мне не нужно помнить, как этот класс работает внутри. Автоматически он стартует сессии, автоматически он заботится об безопасности. Разве лучше это делать вручную ?
 

whirlwind

TDD infected, paranoid
Абстрагирует - это когда код, который использует интерфейс этого класса вообще не заморачивается где и каким образом хранятся переменные. Стартует ли он сессии автоматически или автоматически открывает файл для записи значений переменных абсолютно никакого отношения к абстракции не имеет.
 

ps2007

Новичок
Автор оригинала: whirlwind
Абстрагирует - это когда код, который использует интерфейс этого класса вообще не заморачивается где и каким образом хранятся переменные.
У меня именно так и работает :)
В самом первом посте у меня описан интерфейс. Там не фигурирует ни идентификатор сессии, ни сессионная кука, ни переменная, связанная с сессией, т.е. вообще ничего не связывает со стандартным механизмом сессий.
Вот пример использования:
PHP:
require_once('class.CNativeSession.php'); 

$sess = new CNativeSession(); 

// читаем из сессии
$count = $sess->getVal('count'); 

// пишем в сессию
$sess->setVal('count', (int)$count+1);
После замечаний zerkms собираюсь переделать, чтобы работало так:
PHP:
$sess = new CNativeSession(); 

// читаем из сессии
$count = $sess['count'];

// пишем в сессию
$sess['count'] = (int)$count+1;
Где у меня нарушение абстракции ?
:)
 

whirlwind

TDD infected, paranoid
Сделайте обертку над массивами, оборачивайте куки, серверные переменные и переменные реквеста, а переменные сессии сделайте частной реализацией. Вот это будет абстракция. А иначе сторонним пользователям все равно придется прокси к этому классу писать, потому что ваша абстракция ни от чего не абстрагирует и оценивать тут абсолютно нечего.
 

ps2007

Новичок
Я извиняюсь, но я ничего не понял :-(
1) Почему сторонние пользователи не могут использовать этот класс, я же могу (см. примеры выше)
2) Почему нужен прокси для того, чтобы сторонние пользователи могли использовать этот класс ?

Сделайте обертку над массивами, оборачивайте куки, серверные переменные и переменные реквеста, а переменные сессии сделайте частной реализацией.
3) Зачем мне это, что это мне даст, какую выгоду я получу ? Если можно, то хотя бы с самыми простыми примерами.

С помощью своего класса я смогу безопасно работать с сессиями, причем это происходит автоматически, без написания ни единой строки кода.
Кроме того могу управлять нужными мне параметрами сессий без запоминания механизмов. Если в дальнейшем понадобится добавить/изменить функционал, то подправлю один класс, и не придется лазить по всему коду в поисках строк, где происходит работа с сессией.
Вот какую реальную пользу я вижу от использования этого класса.

А какая польза от вышеперечисленной (см. пункт 3) обертки над массивами ?
 

Beavis

Banned
ps2007
Если бы этот класс поддерживал разные хранилища для сессий, flash messages и т.д., тогда бы от него была бы какая то польза, а так он только усложняет работу с сессией

могу посоветовать посмотреть аналогичные классы в разных фреймворках
 

ps2007

Новичок
Предположим, что нет класса, о котором идет речь выше.
Как мне лучше обеспечить управление сессиями и их параметрами:
1) Защита от кражи и фиксации сессий
2) запускать механизм сессий только тогда, когда нужно считать/записать данные из сессии (lazy evaluation)
3) простым способом управлять параметрами сессий (пока что временем жизни, в будущем может добавятся еще какие-нибудь другие параметры)
4) предусмотреть расширение возможностей (ну например стартовать сессию, если браузер передал сессионную куку).
 

dimagolov

Новичок
для п.1 достаточно сделать одну ф-ю, запускаемую после session_start (или вместо нее)
нужность п.2 и 3 и как следствие 4 лично для меня сомнительна. можно обосновать какой в этом смысл?
 
Сверху