A look into sessions and security
Part #1: The setup
[Updated on 2018–07–05: Fixed a mistake on the referer_check option]
In the first article of this series we explored the concept and theory behind sessions. Now that we understand what a session is it’s time to see how we can set one up.
Once again, although I’ll focus on PHP, don’t let this stray you away: what is really important here are the concepts, so look in the documentation for the programming language you use to see how you can replicate what is written here!
PHP is a highly configurable language. There are tons of changes that can be made to how the language operates and this is also true when it comes to sessions. Also, starting on PHP 7.0.0, we can also set up a session by providing an array of options to the session_start() funcrion. This article will explore a few scenarios on how to configure the session and what I consider the best options to improve security.
How should I set up a session?
There are basically two ways you can set up PHP options, including the ones regarding session security:
- For all projects — By setting these on your php.ini or httpd.conf;
- Per project — By setting these on a .htaccess file, a .user.ini file or by passing the options to session_start() as mentioned before.
There is no “preferrable” way of doing so, but since I started my career as a web developer in the time when all we had was shared hosts occupying the same machine (and this is also usually true for a developer environment), I prefer to do so per project.
The permission for setting the majority of session options is either PHP_INI_ALL or PHP_INI_PER_DIR, which for our purposes means we can set it per project.
Which options and which values should I use to improve security?
- Option: referer_check Recommended Value: “something”
Yes, the referer can be easily spoofed, but that doesn’t mean we shouldn’t lock it (redundant security). Although beyond the scope of this article, in a development environment you should use Virtual Hosts for your projects, enabling you to use this option regardless of environment.
[EDIT] Note that this should contain a substring to be checked in the referer, meaning it would probably be a good idea to use something along the lines of a randomly generated token (more on this on a future article).
- Option: use_only_cookies Recommended Value: 1
This option will specify that the session ID will be set using only cookies on the client side, avoiding a possible attack that transmits the ID trhough the query string.
- Option: use_trans_sid Recommended Value: 0 (zero) [Default]
In a similar way to the one above, this option will prevent using the session ID on the query string. I’d call this the “register globals of sessions”: something that was implemented to make the programmer’s life easier, but when used in a poorly secured system it can lead to disaster.
- Option: cookie_httponly Recommended Value: 1
- Option: cookie_secure Recommended Value: 1
This option will make the session cookie available through HTTPS only. This means that if a HTTP connection is used, it will always generate a new session, since the cookie won’t be “transmitted” to the client. Since certificates are not so complicated to achieve anymore, thanks to the Let’s Encrypt initiative, this one should be a no-brainer.
- Option: cookie_lifetime Recommended Value: 0 (zero) [Default]
This is the most controversial one. So much so I could write an entire article about it, but let’s try to sum things up: There’s always a concern in developing web applications regarding the balance between security and “user comfort”.
Nowadays it’s pretty common to set “eternal sessions”, meaning you need to authenticate yourself only once to access the application in question. This poses a great risk for obvious reasons: if your session or even your computer end up being operated by someone else they will have free access to your pre-authenticated sessions.
We should, for the best (in my opinion) security x comfort ratio, leave this as zero, meaning: “The cookie is destroyed when the browser is closed”, but it’s a known fact that this is highly unpopular nowadays.
Summing it up and keeping it real, you probably won’t be able to use my recommendation, but I’ll talk more about this soon.
- Option: save_handler Recommended Value: “files” [Default]
Although PHP uses a very simple logic to store the data (it’s basically the serialized version of the $_SESSION array stored in a file), in the interest of avoiding reinventing the wheel you should keep this as is.
- Option: save_path Recommended Value: NOT “/tmp” or “”
PHP saves session data on the server on the temporary “/tmp” folder by default. This folder is accessible to everyone (0777), so an attacker could exploit flaws on your code to access it, for an example. Although folder permissions can be a tricky subject and it’s beyond the scope of this article, it’s my recommendation that you choose a path not only outside the web root (for obvious reasons), but one that has it’s mode set to 0770 (User and Group full access, no access to Others).
- Option: session_name Recommended Value: NOT “PHPSESSID”
As the majority of security professionals, I’m not a huge fan of “security through obscurity” but to avoid an attack that would easily guess the session name it’s a good practice to set it to something else (I’d say that this is more about “redundant security” than obscurity).
- Option: use_strict_mode Recommended Value: 1
This option will prevent the browser to send an uninitialized session, and furthermore aid in protecting against session fixation attacks.
- Option: sid_length Recommended Value: 128
Here we have another common “enemy” of security: performance.
The rule is obvious: the longer the length (it can go to 256 characters) the better, since it makes for a harder-to-guess ID, but this would obviously mean a heavier load on the server. Unless your application have dramatic performance concerns (hint: most applications don’t!!!) 128 characters should be secure enough and as fast as the default 32 characters setting.
- Option: hash_function Recommended Value: NOT “0” (zero)
The default value means using MD5, which is considered a vulnerable hashing solution for quite a while now. PHP supports any algos that can be used with the hash functions (see below), which provide much better options.
To find out the possible algos to use with the hash_function option run the following code:
You might be asking yourself why didn’t I made this available in a nifty Gist. Well, first of all the idea here is not to give you a ready made solution: in security that’s a huge no-no; second, things change, specially in security so even though this is quite apropriate at the moment, in the future some or even all of what’s written here may change.
ALWAYS pay attention to concepts! ALWAYS consult the documentation! ALWAYS do your own research!
See you in Part 2!
 I’m all for reinventing the wheel, but I’ll talk more about this in a near future.