From the translator: when developing Web-payment.ru on the CakePHP framework, we faced from the very beginning that the user logout occurred every few hours, and this is too short a period of time. However, no matter how large the timeout and cookieTimeout values ​​we set in the kernel settings, nothing changed. This article solved this problem for us.Last year, I addressed this issue at least twice, but to no avail. After a long search for a solution, I again postponed it indefinitely. It should also be said here that when it comes to debugging session / cookie things (in this case, authentication), the process of finding bugs has never been easy, since it depends on many factors that add up and make it harder to detect problems.
The Cake documentation also does not say that for long-running php sessions, it is necessary to increase the value of the internal variable max_lifetime. In general, I stumbled upon it by chance, because I always thought that the framework itself took care of this, but even after I made the database a container of sessions, I did not notice any improvements. Therefore, I thought that cleaning the memory on the server side here seems to have nothing to do with it, well, or at least, it affects not only it alone.
So, in the end, we have the following:
- Setting timeouts was a waste of time, as well as changing the Security level, which I set to low .
- Moving from php / cake to the database didn’t work either, although it certainly provides a bit more control.
- Increasing the value of gc_maxlifetime in php.ini again, did not give a special effect.
After all these actions, the logouts continued at completely different intervals — sometimes in a few minutes, sometimes in a few hours, but no more. This problem is important not only for creating social networks, but also for other sites working on Cake.
')
I tried to add encrypted cookies several times, such as
RememberMe for a quick re-login at that moment, but because of the
PHP bug related to srand / mt_srand and suhosin, this solution did not work. Thanks to
Miles '
post about a bug, I decided to take up the problem seriously and do something about it (many users complained about unexplained logouts over the past few months).
There is also another problem remotely related to this situation, the essence of which is that PHP (Sake) stores session cookies, defining a fixed lifetime for them, however, their lifetime is not updated during queries, and thus it turns out that A certain point session cookies become invalid. Nowadays,
nothing can be done about it ...
Automatic login return as a solution
A few years passed, and finally, I finally took advantage of Miles' rough work and set up its
AutoLogin component to work with my applications.
The essence of the component is that it saves the user data in a cookie during login, and as soon as the session is lost for an unknown reason, the component restores it. This happens quietly and does not interfere with the user's work.
You can find the component code in my
Tools plugin (use this repository).
Features of use
Having successfully tested it in several Cake2.x applications, I want to share with you a simple guide to using it. To connect it, you just need to add the component globally to the AppController:
public $components = array('Session', 'RequestHandler', 'AutoLogin', 'Auth', ...);
It is important to announce its connection before connecting the authorization component to avoid the occurrence of any erroneous “no authorization” messages.
You can use /Config/configs.php (or any other configuration directory) to determine its settings:
$config['AutoLogin'] = array( 'controller' => 'account', 'username' => 'login', 'requirePrompt' => false ...
I ask you to be careful when using the last parameter -
requirePrompt . When it is disabled, AutoLogin will be used for all attempts to login. This requires users to constantly make the correct exit from their accounts while using the site in public networks (especially dangerous for Internet cafes, where unauthorized people can take possession of a user account even a few days after the last use). Therefore, you should make sure that the component is used only on sites where all users are warned and aware of this.
That is, in the usual case, you need to add the checkbox to the authorization form:
if (Configure::read('AutoLogin.active')) { echo $this->Form->input('auto_login', array('type'=>'checkbox', 'label'=>__(' '))); }
That's all. The component can be easily tested by saving sessions in the database and trimming the session table after login. The component will return the user back, and will also create a new line for the session in the database table. If everything is done as it should, the user will not even notice that he lost authorization for a split second.
The above if construct can be omitted. I use it to dynamically enable / disable a component depending on the environment in which it works, but if you are going to use it, make sure that you set active to
true in Configure.
Now for the bug fix suhosin. According to the Miles explanation from the links above, most Linux Apache environments come with a default suhosin patch, which changes the way srand works. In the case of my WAMP server, this is not the case, so everything works immediately on it, however, in order for the component to work in a Linux environment, you just need to add to the end of
/etc/php5/apache2/php.ini this line :
suhosin.srand.ignore = Off
And do not forget to restart Apache or do a force-reload for
/etc/init.d/apache2 .
Tip: I added an example to check for the presence of this bug (see the file for checking in the repository). If you are not sure if you have this problem, run it in your environment.
Other tips: If you want to disable AutoLogin for a specific site, you just need to change the
active value to
false in the site config, or do it dynamically in the controller builder. Always remember: you will not be able to add it to the list of components dynamically, since you need to turn it on before the Auth part, but you can always turn off the component if it has already been added there beforehand.
The default debag mode is auto-detect. In the development process (debug> 0), it is initially enabled, but you, of course, can overwrite this value in your configs.
Another important parameter is
expires - by default it is two weeks and is easy to change.
Alternatives
2.x now also has alternative solutions to the problem. One of them is to use the special
CookieAuthenticate adapter.
Analysis of the mechanism of working with sessions in Cake
It took me a long time to understand in detail the principle of the sessions. Now I finally understood. I want to pay special attention to the fact that using the above code without a deep understanding of it will most likely just disguise the problem ... well, or cure only the symptoms of the disease, if you want. Yes, this method will work, but I think you would like it more if everything worked without using the session memorization tricks described above.
First of all, we need to understand how sessions are created, validated and updated on clickthrough:
- The user arrives for the first time, session cookies are generated (and are valid for x seconds), the session timeout value is stored in them (y seconds)
- The user clicks again: Of course, we increase the duration of the session (determined by y seconds + another z seconds), storing it in a cookie as a value
- However, cookies, by themselves, do not receive a new lifetime (all the same x seconds). This restriction in the processing of cookies, the time of their timeout is fixed.
Having figured this out, it becomes absolutely clear that when the cookie expired when it was created expires, the session will be destroyed even if you increase its lifetime to several years. Thus, it is extremely important to understand that in order for these settings to make sense, you need to set a large value for the session duration and even more for the cookie lifetime:
Configure::write('Session', array( 'defaults' => 'php', 'timeout' => 14400,
Both values ​​are set in minutes in the CakePHP config.
With these changes, the user exits the session if he:
- did not produce any session activity (clicks, ajax) within 4 hours
- reached a 20-hour cookie timeout (regardless of activity)
And since such settings will make him log out every 20 hours, it makes sense to make more values ​​(several months, for example) or use it in combination with the component described above to memorize the session.
So again:
'session.gc_maxlifetime' => , 'session.cookie_lifetime' => ,
And do not forget to check your ini-parameters that relate to
session.gc_divisor and
andsession.gc_probability to make sure that the memory clearing is working and performed at the right time.
