PHP Session Security
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
What are some guidelines for maintaining responsible session security with PHP? There’s information all over the web and it’s about time it all landed in one place!
13 Answers 13
There are a couple of things to do in order to keep your session secure:
- Use SSL when authenticating users or performing sensitive operations.
- Regenerate the session id whenever the security level changes (such as logging in). You can even regenerate the session id every request if you wish.
- Have sessions time out
- Don’t use register globals
- Store authentication details on the server. That is, don’t send details such as username in the cookie.
- Check the $_SERVER[‘HTTP_USER_AGENT’] . This adds a small barrier to session hijacking. You can also check the IP address. But this causes problems for users that have changing IP address due to load balancing on multiple internet connections etc (which is the case in our environment here).
- Lock down access to the sessions on the file system or use custom session handling
- For sensitive operations consider requiring logged in users to provide their authenication details again
Using SSL only for some operations is not enough, unless you have separate sessions for encrypted and unencrypted traffic. If you use single session over HTTPS and HTTP, attacker will steal it on first non-HTTPS request.
-1 the user agent is trivial to spoof. What you are describing wastes code and is not a security system.
@The Rook, it may be a trivial barrier (the attacker can capture a victim’s user-agent using their own site) and relies on security through obscurity but it is still one extra barrier. If the User-Agent HTTP was to change during the session use, it would be extremely suspicious and most likely an attack. I never said you can use it alone. If you combine it with the other techniques you have a much more secure site.
@grom I think its like putting a piece of scotch tape across your door and saying it will prevent people from breaking in.
If you’re checking the user agent, you’ll block all requests from IE8 users when they toggle compatibility mode. See the fun I had tracking down this problem in my own code: serverfault.com/questions/200018/http-302-problem-on-ie7. I’m taking the user agent check out, because it’s such a trivial thing to spoof, as others have said.
One guideline is to call session_regenerate_id every time a session’s security level changes. This helps prevent session hijacking.
- Trust no one
- Filter input, escape output (cookie, session data are your input too)
- Avoid XSS (keep your HTML well formed, take a look at PHPTAL or HTMLPurifier)
- Defense in depth
- Do not expose data
There is a tiny but good book on this topic: Essential PHP Security by Chris Shiflett.
On the home page of the book you will find some interesting code examples and sample chapters.
You may use technique mentioned above (IP & UserAgent), described here: How to avoid identity theft
+1 for XSS-prevention. Without that it’s impossible to protect against CSRF, and thus somebody can «ride» the session without even getting the session ID.
I think one of the major problems (which is being addressed in PHP 6) is register_globals. Right now one of the standard methods used to avoid register_globals is to use the $_REQUEST , $_GET or $_POST arrays.
The «correct» way to do it (as of 5.2, although it’s a little buggy there, but stable as of 6, which is coming soon) is through filters.
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
$username = filter_input(INPUT_POST, 'username');
Really? Then why in the accepted answer do they mention not to use register globals? Wouldn’t, as far as most run-of-the-mill developers are concerned, register globals and form variable handling fall under the umbrella of «sessions» even if it isn’t technically part of the «session» object?
I agree, this does not fully answer the question, but it is definitely PART of the answer to the question. Again, this fleshes out a bullet point in the accepted answer, «Don’t use register globals». This tells what to do instead.
Using IP address isn’t really the best idea in my experience. For example; my office has two IP addresses that get used depending on load and we constantly run into issues using IP addresses.
Instead, I’ve opted for storing the sessions in a separate database for the domains on my servers. This way no one on the file system has access to that session info. This was really helpful with phpBB before 3.0 (they’ve since fixed this) but it’s still a good idea I think.
This is pretty trivial and obvious, but be sure to session_destroy after every use. This can be difficult to implement if the user does not log out explicitly, so a timer can be set to do this.
Here is a good tutorial on setTimer() and clearTimer().
The main problem with PHP sessions and security (besides session hijacking) comes with what environment you are in. By default PHP stores the session data in a file in the OS’s temp directory. Without any special thought or planning this is a world readable directory so all of your session information is public to anyone with access to the server.
As for maintaining sessions over multiple servers. At that point it would be better to switch PHP to user handled sessions where it calls your provided functions to CRUD (create, read, update, delete) the session data. At that point you could store the session information in a database or memcache like solution so that all application servers have access to the data.
Storing your own sessions may also be advantageous if you are on a shared server because it will let you store it in the database which you often times have more control over then the filesystem.
Is it enough for PHP secure session class?
I need you help with my php session class. I can not understand, the class implements adequate work with sessions. By adequate work, I understand the security and correctness of the methods.
timeToLive = $time_to_live; // Изменяется имя сеанса (по умолчанию) на указанное (если есть) имя для конкретного приложения $this->name = $name; $this->cookie = $cookie; // session.cookie_path определяет устанавливаемый путь в сессионной cookie // session.cookie_domain определяет устанавливаемый домен в сессионной cookie $this->cookie += [ 'lifetime' => 0, 'path' => ini_get('session.cookie_path'), 'domain' => ini_get('session.cookie_domain'), 'secure' => isset($_SERVER['HTTPS']), 'httponly' => true ]; /* * Указывается, что сеансы должны передаваться только с помощью файлов cookie, * исключая возможность отправки идентификатора сеанса в качестве параметра «GET». * Установка параметров cookie идентификатора сеанса. Эти параметры могут быть переопределены при инициализации * обработчика сеанса, однако рекомендуется использовать значения по умолчанию, разрешающие отправку * только по HTTPS (если имеется) и ограниченный доступ HTTP (без доступа к сценарию на стороне клиента). */ // Определяет, будет ли модуль использовать cookies для хранения идентификатора сессии на стороне клиента ini_set('session.use_cookies', 1); // Определяет, будет ли модуль использовать только cookies для хранения идентификатора сессии на стороне клиента ini_set('session.use_only_cookies', 1); session_set_cookie_params( $this->cookie['lifetime'], $this->cookie['path'], $this->cookie['domain'], $this->cookie['secure'], $this->cookie['httponly'] ); > public function __get($name) < switch ($name) < case 'isActive': return $this->getActive(); case 'id': return $this->getId(); case 'name': return isset($this->name) ? $this->name : $this->getName(); case 'isValid': return $this->isValid(); > > public function __set($name, $value) < switch ($name) < case 'id': $this->setId($value); break; case 'name': $this->setName($value); break; case 'timeToLive': $this->timeToLive = $value * 60; break; > > /** * Получение статуса активности сессии * @see https://secure.php.net/manual/en/function.session-status.php PHP session_status * @return bool Статус активности сессии */ private function getActive() < return session_status() === PHP_SESSION_ACTIVE; >/** * Получение идентификатора текущей сессии. * Метод является оберткой для реализации стандартного метода. * @see https://secure.php.net/manual/ru/function.session-id.php PHP session_id * @return string */ private function getId() < return session_id(); >/** * Получение имени сессии * Метод является оберткой для реализации стандартного метода * @see http://php.net/manual/ru/function.session-name.php PHP session_name * @return string|null */ private function getName() < return $this->isActive ? session_name() : null; > private function isValid() < return !$this->isExpired() && $this->isFingerprint(); > /** * Проверка срока действия сессии * @return bool */ private function isExpired() < $activity = isset($_SESSION['_last_activity']) ? $_SESSION['_last_activity'] : false; if ($activity && ((time() - $activity) >$this->timeToLive)) < return true; >$_SESSION['_last_activity'] = time(); return false; > /** * Проверка клиента * @return bool */ private function isFingerprint() < $hash = sha1($_SERVER['HTTP_USER_AGENT'] . (ip2long($_SERVER['REMOTE_ADDR']) & ip2long('255.255.0.0'))); if (isset($_SESSION['_fingerprint'])) < return $_SESSION['_fingerprint'] === $hash; >$_SESSION['_fingerprint'] = $hash; return true; > /** * Назначение идентификатора текущей сессии. * Метод является оберткой для реализации стандартного метода. * @see https://secure.php.net/manual/ru/function.session-id.php PHP session_id * @param string $id Идентификатор сессии для текущей сессии */ private function setId($id) < session_id($id); >/** * Установка имени сессии * Метод является оберткой для реализации стандартного метода * @see http://php.net/manual/ru/function.session-name.php PHP session_name * @param $name */ public function setName($name) < if ($this->isActive) < session_name($name); >> /** * Инициализация сессии */ public function open() < // Бездействие, если сессия была инициализирована ранее if ($this->isActive) < return; >session_start(); // Проверка на корректность инициализированнйо сессии if (!$this->isActive) < // TODO: Вывод исключения >> /** * Уничтожение сессии, включая все атрибуты. Метод имеет эффект только при наличии активной сессии. * @see http://php.net/manual/ru/function.setcookie.php PHP setcookie */ public function destroy() < if ($this->isActive) < $this->deleteAll(); setcookie( $this->name, time() - 42000, $this->cookie['path'], $this->cookie['domain'], $this->cookie['secure'], $this->cookie['httponly'] ); session_destroy(); > > /** * Удаление всех значений сессии * Метод является оберткой для реализации стандартного метода * @see http://php.net/manual/ru/function.session-unset.php PHP session_unset */ public function deleteAll() < if ($this->isActive) < session_unset(); >> /** * Обновление текущего ID на новый. Метод имеет эффект только при наличии активной сессии. * @see https://secure.php.net/session_regenerate_id PHP session_regenerate_id * @param bool $delete_old_session */ public function refresh($delete_old_session = true) < if ($this->isActive) < session_regenerate_id($delete_old_session); >> /** * Получение значение сессии по ключу. * @param string $key Ключ, по которому необходимо получить значения * @return null|mixed Значение сессии по ключу */ public function get($key) < if ($this->isActive) < return isset($_SESSION[$key]) ? $_SESSION[$key] : null; >return null; > /** * Добавление или установка значений в сессию по ключу * @param string $key Ключ, в который необходимо добавить значения * @param string $value Значение добавления */ public function set($key, $value) < if ($this->isActive) < $_SESSION[$key] = $value; >> /** * Удаление значения сессии по ключу * @param string $key Ключ, по которому необходимо удалить значения */ public function delete($key) < if ($this->isActive && isset($_SESSION[$key])) < unset($_SESSION[$key]); >> /** * Проверка наличия ключа у сессии * @param string $key Ключ, в который необходимо найти * @return bool */ public function hasKey($key) < return ($this->isActive && isset($_SESSION[$key])); > >
$session = new Session(); $session->open(); // If AFK more than access - logout if (!$session->isValid) < $session->destroy(); > .
\$\begingroup\$ Welcome to Code Review! The (doc) comments would be of little value for me but for web translators. \$\endgroup\$