Session API in Yii with the ability to store in Redis
Not so long ago, I needed to write an API on the Yii Framework, one of the functional requirements of which is authorization. For the authorization mechanism, I decided to use sessions.
Implementation option homemade sessions
Before that, I had seen quite a few API implementations written in PHP, but at the same time I had never seen an implementation that would use the built-in PHP session mechanism. The fact that I mostly came across was the implementation of self-made sessions. In most cases, it looked like this:
1. The client sends a request to the server with data for authorization. 2. In case of successful authorization, the server generates a unique identifier (random hash), stores it in its repository (database, cache, etc.), records information about the client’s ownership of this identifier, and records the time of the last access to the server. After that, it sends a response with the identifier to the client. 3. The client, having received the session identifier and saving it for further requests, sends a request to the server with the transmitted session identifier (as a parameter or header) to receive data. 4. Having checked the session identifier, the server sends the data to the client and updates the time of the last call to the server with the given identifier.
The interaction between the client and the server in clauses 3 and 4 may occur until the session record on the server is destroyed. In the case of session destruction, it is necessary to perform items 1 and 2 again before further performing items 3 and 4. Periodically, you have to do checks of session identifiers for the last time the server was accessed and delete those that have exceeded their lifespan if the session storage used does not know how to destroy records automatically for a given lifetime. In this method, quite a lot of actions that require implementation.
Option using standard PHP sessions
And what do we get using standard PHP sessions ? 1) Automatic generation of a unique session identifier. 2) Access to the data stored in the session, as well as its management from anywhere in the application. 3) Using standard PHP functions for working with sessions, including wrappers over them. For example, class CHttpSession Yii framework. 4) Automatic recovery of a previously saved environment. For example, automatic user login when receiving an identifier of a previously created session. 5) Automatic deletion of sessions that have expired. ')
Let's take a look at how cookie-based sessions work.
1. The browser sends a request to the server to receive information at the specified URL. 2. The server returns a response with the “Set-Cookie” header, which tells the browser to write the session ID in the cookie. Example of the “Set-Cookie” header: Set-Cookie: PHPSESSID=p2799jqivvk8gnruif1lvtv5l5; path=/ 3. The browser, having successfully recorded the session identifier in the cookie, sends a request for a new URL, but with the “Cookie” header: Cookie: PHPSESSID=p2799jqivvk8gnruif1lvtv5l5 4. The server gives the page to the browser.
All subsequent requests from the browser come with a “Cookie” header, which contains information about the session identifier. All this automatically works in your browser, unless cookies are disabled. But what to do if cookies are disabled, or if the client is not a browser? In this case, everything will not be so simple. Of course, you can use the receive and send headers “Set-Cookie” and “Cookie” on the client side, but let's consider another solution to this problem, presented below.
Using PHP sessions in the API
Before you start using sessions, you need to pay attention to the parameters in php.ini related to the sessions. Pay particular attention to the following parameters: session.use_cookies , session.use_only_cookies , session.use_trans_sid . In order to start using the PHP session mechanism for the API, you need to configure these parameters as follows:
Of course, it is not necessary to set these settings directly in php.ini, it is enough to set them through the PHP function ini_set . With these settings, we disable the ability to use cookies to store identifiers on the client side, since it implies the use of the API not only by the browser, but also by other applications, mobile devices, etc. Enabling the session.use_trans_sid parameter will enable us to pass the session identifier as a GET or POST parameter. If you are going to develop a REST API, then passing an identifier through a POST parameter is not the best option, since REST still uses methods such as PUT and DELETE, which will not work for the session identifier. Therefore, it is better to pass the identifier as a GET parameter that will work with any of the methods in the REST API. We will also set the name of the GET parameter in the session.name parameter, which by default is called PHPSESSID. A URL with a passed session ID will look like this:
Now let's look at how this mechanism can be used in the Yii Framework. To work with sessions in Yii provides the class CHttpSession . To use it, you need to register the following settings in the components array:
Where 'cookieMode' => 'none' sets the php.ini settings in session.use_cookies = 0 and session.use_only_cookies = 0 'useTransparentSessionID' => true puts php.ini in session.use_trans_sid = 1
For an API with a not very large number of hits, this would be enough, but by default sessions are stored as a “Plain text” file on disk, which can become a weak link when intensively reading and writing sessions in high-loaded APIs. In this case, you can use one of the following solutions: 1) replace the drive on the SSD; 2) put a raid level 10 of SSD drives; 3) use a ram disk. For example, the Tmpfs file system in Linux; 4) storage of sessions in Memcached (data storage in RAM); 5) storage of sessions in Redis (data storage in RAM).
Storing Sessions in Redis
I would like to turn my attention to Redis, due to its diverse data storage structures. I also want to note the important possibility of data recovery (sessions in our case) after rebooting the server. Before you can use Redis as a session repository, you need to install the Redis server and the PHP Extension for Redis . How to install both of them can be found here . After successful installation, you will be able to use the PHP Session handler from the PHP Extension for Redis. In order to use PHP Session handler Redis without directly changing php.ini and being able to set Redis as a session store in the Yii config, I had to change the CHttpSession a bit, inheriting from it and writing my RedisSessionManager class.
Now the config for the session component will look like this:
What's going on here? First we get the username and password that came from the request. Then we try to log in using this login and password and in case of successful login we return the session identifier for further use when accessing other API methods.
In the case of successful authentication, the user component is entered into the user information, which will be automatically substituted during the following calls to api with the specified session identifier.
Logout method:
publicfunctionactionLogout(){ if(Yii::app()->session->destroySession()){ $this->sendResponse(Status::OK, 'Successful logout'); }else{ $this->sendResponse(Status::BAD_REQUEST, 'Logout was not successful'); } }
Everything is simple here. Just destroy the current session with all its contents.
I also want to note a few tips on using sessions in the API:
1) It is important to use an encrypted connection to communicate between the client and the server, in order to prevent the attacker from intercepting the session identifier for further use. For example, you can use the HTTPS protocol.
2) You can use additional session authentication algorithms in case the session was intercepted by an attacker. For example, to bind a session to the user's IP, additionally saving the IP within the session and checking if it has changed during the next call. If the previously saved IP does not match the current one, you need to destroy the session.
3) Set a limit on the lifetime of the session. Since this time is updated automatically during the next request to the API, I would set, for example, 2 hours. Thus, if the user is inactive for 2 hours, the session is automatically destroyed. This will reduce the chance of overflowing the session repository.
Finally, a short demo video about how authorization works in the REST API, written in Yii, c storing sessions in Redis.