
At some stage in the development of a web project, one of the following situations occurs:
- backend ceases to fit on one server and requires session storage, common to all backend servers
- for various reasons, ceases to arrange the speed of the built-in file sessions
Traditionally, in such cases, Redis, Memcached, or some other external storage are used to store user sessions. As a result, the burden of operating the database arises, which in this case should not be a single point of failure or a bottleneck in the system.
However, there is an alternative to this approach. It is possible to safely and reliably store session data in a browser cookie at the user’s own, if you verify the session data with a cryptographic signature. If, in addition to this, the data is also encrypted, then the contents of the session will not be available to the user. The main advantage of this storage method is that it does not require a centralized database for sessions, with all the advantages in terms of reliability, speed and scaling resulting from it.
')
Mechanism description
This idea is not new and is implemented in a variety of frameworks and libraries for various programming languages. Here are a couple of examples:
It is worth noting that in Ruby on Rails they make a big bet on the performance of this mechanism in comparison with all other session storage methods and use it by default.
Most of the existing implementations work as follows: write a string containing the session expiration time, session data and the
HMAC signature of the expiration time and data in some cookie. When a client requests a cookie, it is read by the corresponding handler, then the signature is checked and the current time is compared with the session expiration time. If everything matches, the handler returns the session data to the application.
However, cookies are not encrypted in common implementations of this mechanism.
Comparison with the classical approach
As a result, the storage of sessions in cookies has the following
advantages :
- The performance of the web application increases, as a small cryptographic operation is cheaper than a network exchange session or disk access to retrieve session data.
- Reliability of the web application increases, since it does not depend on external KV-storage. Even if the session storage is provided with fault tolerance, this does not give it absolute stability: switching takes time, and some problems (such as the deterioration of network connectivity between regions) are ineradicable at all. Often, sessions are stored on a single server, which is a single point of failure for the entire web application.
- Saving resources. Sessions no longer need to be stored, which means that the owners of small sites that have reduced disk activity will benefit from this, and the owners of large web projects will release several servers.
There are also
disadvantages , but without them:
- The amount of data transmitted by the client increases.
- There is a limit on the size of the data in the session, due to restrictions on the size of the cookies. This is usually slightly less than 4KB of coded data.
- The client can roll back the session state to any value issued and previously signed, the cryptographic signature of which is still valid at the current time.
Implementations for PHP
When I tried to find something similar for PHP, I was surprised to find that there is not a single library that meets the minimum requirements:
- Security: no errors when using cryptography
- Actual code base: support for modern versions of PHP, the absence of deprecated-extensions in dependencies (such as mcrypt)
- Availability of tests: sessions are one of the fundamental mechanisms, and an immature code cannot be used as the basis of a real application.
In addition, I think it is not superfluous:
- Encryption: open client session storage on the client, readable by the client, is not suitable for everyone.
- The most compact data presentation - for the sake of minimizing the overhead and the capacity of the session
- Embedding via SessionHandlerInterface
The implementations I reviewed are:
I also watched the implementation of storing sessions in cookies in the Slim 2.x framework, but there is no signature or encryption. What the authors immediately warned.
Why is signature verification and encryption important
instead of signature insufficient? Firstly, there is a noticeable probability that a cookie with garbage will be decoded in some session, especially the recording of the session is short. Secondly, the line with the session is subjected to deserialization, and the input from the deserializer cannot be submitted from untrusted sources.
After all the searches, I decided to implement such a library on my own.
Own implementation
Packagist:
packagist.org/packages/snawoot/php-storageless-sessionsGithub:
github.com/Snawoot/php-storageless-sessionsInstalling from composer:
composer require snawoot/php-storageless-sessions
Key features:
- Mandatory encryption. Algorithm and mode - any symmetric cipher to choose from, available in OpenSSL. Default: AES-256-CTR.
- HMAC-signature cookies with any hash algorithm to choose from the Hash cryptographic extension assortment. It is also used to generate derived encryption keys. Default: SHA-256.
- Implemented countermeasures against time attacks
- In addition to the main data set and expiration time, the session ID is also covered by the signature, which leaves room for associating session data with external data.
- The implementation is presented as a class compatible with SessionHandlerInterface, which means it can be used transparently with almost any PHP applications.
- Minimum overhead storage introduced by encryption and signature.
A few words about the choice of encryption mode. When using block encryption modes (ECB, CBC), the length of the ciphertext increases slightly. This is due to the fact that the length of the original message must be a multiple of the block size. Due to mandatory padding, the length increment is from one byte to the size of the cipher block. That is, for AES - from 1 to 16 bytes. When using streaming encryption modes (OFB, CFB, CTR, ...), the original message is not passed through the block cipher, instead the block cipher is used to form the gamma sequence, and then the length of the ciphertext exactly matches the length of the original message, which is better suited for the described tasks.
Examples of using
A small script illustrating how to work with this handler:
<?php require_once("vendor/autoload.php"); header('Content-Type: text/plain'); $secret = '********************'; $handler = new VladislavYarmak\StoragelessSession\CryptoCookieSessionHandler($secret); session_set_save_handler($handler, true); session_start(); if ($_GET) { foreach ($_GET as $key => $value) $_SESSION[$key] = $value; echo "Updated session:"; } else echo "Current session data:\n"; var_dump($_SESSION);
You can watch his work by setting different session values ​​in the request line at:
https://vm-0.com/sess.php .
An example of symfony integration:
framework: session: handler_id: session.handler.cookie services: session.handler.cookie: class: VladislavYarmak\StoragelessSession\CryptoCookieSessionHandler public: true arguments: ['reallylongsecretplease']
As a real demo, I connected this session handler to the first web application that came to mind that uses sessions. It turned out they DokuWiki:
wiki.vm-0.com . The site has a registration and login, and the work of the sessions can be observed in cookies.
Thank you for your attention and I hope that this article will help the development of your projects.