Description
Now that the sessions are being reworked by @Drak, I would like to introduce another feature which in my opinion is critical for secure websites: Secure session attributes.
How can sessions be hijacked in mixed HTTP/HTTPS environments?
- Alice makes a HTTP request for page A and obtains the session ID X
- Alice makes a HTTPS request for page B
- The website is programmed correctly and asks for reauthentication
- Alice authenticates and obtains a new session ID Y
- Alice makes another HTTP request for page A, this time transmitting Y in a cleartext session cookie
- Mallory intercepts the request
- Mallory makes a HTTPS request for page B using session ID Y and does evil stuff
Solution A: Secure session cookie
The only possible solution right now is to set session.cookie_secure
to on. But then HTTP pages cannot access the session.
Solution B (proposed): Split sessions
The second idea is to split the session into an insecure and a secure area like this:
+---------------------------+
| Insecure attributes |
| +-------------------+ |
| | Secure attributes | |
| +-------------------+ |
+---------------------------+
This is intended for the case when session.cookie_secure
is off. The session cookie is always transmitted, so both HTTP and HTTPS pages can access it.
Within the session, a nested bag is stored that contains the secure attributes. In order to gain access to this secure bag, a key is needed. This key is transmitted in a second cookie, the "security cookie", with secure
set to on (i.e. transmitted via SSL only).
Attributes in the outer container (=normal session) can be accessed
- on HTTP and HTTPS pages
- by hackers who hijack your session (easy, think public WiFi)
Attributes in the secure bag can be accessed
- on HTTPS pages only and
- if the value of the security cookie is correct
- by hackers who hijack your session and intercept your SSL only security cookie, which is much harder
Developers can chose to
- store data in the normal session if it is insensitive and must be accessible on HTTP pages
- store data in the secure bag if it is sensitive and must never be accessible on HTTP pages
API
// Accessible on all pages
$session->set('cart_id', $cart->getId());
// Writable/readable only if HTTPS, throws an exception otherwise
$session->getSecureBag()->set('client_data_id', $clientData->getId());
Implementation
The value of the security cookie could be calculated as a hash of
microtime(true)
session_id()
- a secret stored in the app configuration
So the session could look like this:
array(
'cart_id' => 123,
...
'__secure__fa28e1....ff07' => array(
'client_data_id' => 52,
),
)
If you erase the security cookie (or if a hacker hijacks your session and sends the wrong security cookie), a new secure bag will be created, leaving the old one untouched.
+---------------------------+
| Insecure attributes |
| +-------------------+ |
| | Your secure bag | |
| +-------------------+ |
| +-------------------+ |
| | Hacker's sec. bag | |
| +-------------------+ |
+---------------------------+
How this fixes our initial example
- Alice makes a HTTP request for page A and obtains the session ID X
- Alice makes a HTTPS request for page B and obtains the hash J in the security cookie
- The website is programmed correctly and asks for reauthentication
- Alice authenticates and obtains a new session ID Y. The security hash J could be regenerated too, not sure this is necessary.
- Alice makes another HTTP request for page A, transmitting Y in a cleartext session cookie. J is not transmitted as the cookie is SSL-only.
- Mallory intercepts the request
- Mallory makes a HTTPS request for page B using session ID Y
- Since Mallory did not send a secure session hash, a new hash K is generated and sent back in the security cookie. Mallory cannot access attributes stored in the namespace J. Mallory can still access all other session attributes.