Race Condition For Persistent Remember Me Cookies

According to the Persistent Login Cookie Best Practice , you should never allow the “remember me” token to be used more than once:

Persistent cookie is suitable for single sign-on. When authentication is confirmed, the random number used to log in is invalid and a new cookie is assigned. Standard session management processes the credentials for the life of the session, so the newly assigned cookie will not be checked until the next session (after which it will also be invalid after use).

Then how do you handle the race condition when a user visits multiple URLs on your site at the same time? I have this problem right now.

Say two requests are simultaneously sent from the browser to the server. The requests do not contain a session cookie, but the same remember me. One of the requests will be processed before the other and will receive a response with an authenticated session cookie and an updated Remember Me cookie.

The Remember Me icon in the second request is now invalid , and a different session identifier is created on the server. This request is not executed because the user cannot be authenticated.

I came up with several possible solutions, but none of them seem very good. Did I miss something?

+6
source share
2 answers

Old question, but I did not find the answer anywhere. I have the same problem. My solution was to store the old token in the database and use it as a backup if the main token was not found. But I was convinced that the old token is valid only for a short period of time, as a few seconds after the token has changed. Then I change the token only if some time has passed since the previous update, otherwise there will be cases when the token changes several times in a row.

+2
source

To clarify the response of vangose, I would add that you need to use some kind of locking mechanism. Consider the PHP-ish pseudo-code here:

if (isFallback($token)) { // Log the user in } else { // Usual processing // If token has to be updated, save old token as fallback for a few seconds } 

If there are parallel requests, they may appear in the else branch, one request will lead to an update, and the other will lead to the invalidation of the token. The way I solved this is to use a named lock named after $token to wrap the else branch. In addition, all concurrent requests, except for one, will not be able to get a lock, in which case we will sleep a bit and try again (when we try again, we find that the token has become a rollback token).

 if (isFallback($token)) { // Log the user in } else { $couldLock = lock($token); if (!$couldLock) { usleep(10000); // Retry, possibly a recursive call } else { // Usual processing // If token has to be updated, save old token as fallback for a few seconds unlock($token); } } 

I hope these considerations can be helpful.

+1
source

Source: https://habr.com/ru/post/887708/


All Articles