Most modern web applications use some form of session management system to link multiple HTTP requests to the same user. Every web application that has some concept of authentication relies on a session management system. Typically, session management involves some sort of unique identifier stored in a cookie (e.g. `PHPSESSID=usuheickmc37dbesu8a2oe3kns`), established by the server, and returned by the client on every subsequent request. Anyone who can steal a user's session identifier can impersonate the user who legitimately possesses it. Let's make sure that doesn't happen. ## Common Vulnerabilities in Session Management There are three primary threats to the security of a session management system in the context of a web application: 1. **Man-in-the-Middle**: If you can intercept unencrypted HTTP traffic between a user and the webserver, you can steal any user's session identifier. 2. **Session Fixation**: By tricking the client into using a session ID known to an attacker, it's possible to impersonate the user later. 3. **Predictable Session IDs**: In low-entropy implementations, by establishing a series of new sessions and studying the IDs the server provides, you can predict what the next session ID will be. ## Transport Layer Security If you do nothing else to secure your users' browsing sessions to your website, [use TLS](http://istlsfastyet.com/). Mozilla developed a [TLS configuration tool](https://mozilla.github.io/server-side-tls/ssl-config-generator/) for taking a lot of the guesswork and ciphersuite selection out of deploying a website over HTTPS. Be sure to set a **Hypertext Strict Transport Security** header so your users' browsers will know to only use HTTPS for your domain. The Mozilla tool linked above generates this header for you too. For added security, redirect plaintext HTTP requests to HTTPS so coax first-time visitors into a secure zone. ## PHP Configuration After ensuring that users will connect to the server over a secure channel, we should configure PHP for optimal session management by editing the appropriate `php.ini` file. Some of the decisions to make here depend on your requirements and resources; some prefer to use memcached for session storage, others prefer storing them in a SQL table. By default, PHP stores session state on disk. The two most important configuration options to change are: 1. **`session.cookie_httponly` should be set to `1`.** This tells the user's browser not to make this cookie available to Javascript, which limits the damage of a cross-site scripting attack. 2. **`session.cookie_secure` should be set to `1`.** This tells the user's browser not to send the cookie at all unless over HTTPS. For the remaining security-related configuration decisions: * Only allow sessions to be established to your domain. * 32 bytes of entropy from `/dev/urandom` hashed with `sha256` is sufficient for generating session identifiers. Make sure to [use `/dev/urandom`](http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers) and not `/dev/random`. * We want to use only cookies. ### Example Configuration session.save_handler = files session.use_cookies = 1 session.cookie_secure = 1 session.use_only_cookies = 1 session.cookie_domain = "example.com" session.cookie_httponly = 1 session.entropy_length = 32 session.entropy_file = /dev/urandom session.hash_function = sha256 session.hash_bits_per_character = 5 ## Other Considerations Still concerned about session fixation attacks? There are two ways to mitigate this: With a session canary, and by regenerating your session ID when privileges are escalated (or, optionally, at a regular interval). session_start(); // Make sure we have a canary set if (!isset($_SESSION['canary'])) { session_regenerate_id(true); $_SESSION['canary'] = time(); } // Regenerate session ID every five minutes: if ($_SESSION['canary'] < time() - 300) { session_regenerate_id(true); $_SESSION['canary'] = time(); } Some systems like to bind a session to a particular IP address. This is not generally recommended; Tor users, in particular, will have difficulty staying authenticated. You can enforce this restriction here too. session_start(); // Make sure we have a canary set if (!isset($_SESSION['canary'])) { session_regenerate_id(true); $_SESSION['canary'] = [ 'birth' => time(), 'IP' => $_SERVER['REMOTE_ADDR'] ]; } if ($_SESSION['canary']['IP'] !== $_SERVER['REMOTE_ADDR'])) { session_regenerate_id(true); // Delete everything: foreach (array_keys($_SESSION) as $key) { unset($_SESSION[$key]); } $_SESSION['canary'] = [ 'birth' => time(), 'IP' => $_SERVER['REMOTE_ADDR'] ]; } // Regenerate session ID every five minutes: if ($_SESSION['canary']['birth'] < time() - 300) { session_regenerate_id(true); $_SESSION['canary']['birth'] = time(); } ### Storing Session State in a Cookie Some frameworks and developers eschew server-side storage of session state and instead to offload it into the HTTP cookie. We generally advise against doing this. While it can be an appropriate choice for some applications, it is not without problems: 1. HTTP Cookies are limited to 4 KB, which severely hinders what you can do with session data. 2. HTTP Cookies are sent on every HTTP request. 3. HTTP Cookies can be tampered with by the end user, which can pose a security risk. Problem 3 is usually solved in frameworks by [using encryption](https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly). We only recommend that developers skilled at breaking cryptography applications and protocols implement these strategies, due to the delicacy involved in cryptography engineering. ## In Summary The security of your application likely depends on the security of your session management system. Make sure you [use HTTPS](https://paragonie.com/white-paper/2015-secure-php-data-encryption#https-made-easy) and configure PHP for optimal security. ### Recommended Next Steps to Building Secure PHP Applications * Learn how to implement a [secure login form with a "remember me" checkbox in PHP](https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence) * Learn the [easy and definitive way to prevent SQL injection in PHP applications](https://paragonie.com/blog/2015/05/preventing-sql-injection-in-php-applications-easy-and-definitive-guide) * Read this cautionary piece on [generating random numbers and strings in PHP](https://paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php) * Consider our recommendations on [secure PHP cryptography libraries](https://paragonie.com/blog/2015/11/choosing-right-cryptography-library-for-your-php-project-guide)