This system is for a personal file upload and sharing web application made with PHP.
When a user logs into the application, it has the option to "stay logged in" after closing and reopening their browser, just like most other web applications. What this actually does is that it sets an HTTP cookie, named auth
, with an expiration of 90 days on the client browser that contains a string in the following format: UserIdentifier:SeriesNumber:Token
where :
is the delimiter. The same data is also persisted in the database table user_auths
, but the token will be hashed before it is.
The UserIdentifier
is the user identifier of the user that opted to stay logged in, the SeriesNumber
is a generated random number between 0 and 2147483647 (inclusive) and the Token
a generated random 60 character string of alphabetic, numerical and symbolic characters. An example value would be: 1:902449381:j7j]fP%CxIzcKSg/'wG]XzJd.OsX8"K0FlY')xXQz.5.Q]+KJnXi<>p/t7nz
Every time a user visits the application and is not found in the HTTP session I will:
Try to find the
auth
cookie on the client browser. If it's found, I check if the value of it contains two:
delimiters (checking for the right format), if the correct amount of delimiters is not found I delete the cookie of the client browser and the user is not authenticated.If above test is passed, I will use the user identifier to search in the db table if the user of the given identifier has any rows in the table (meaning that the user has opted to "stay logged in" on one or more browsers). If the user identifier is not found in the table, I delete the cookie of the client browser and the user is not authenticated.
If the above test is passed, I'll now use both the user identifier and series number to check for a matching row in the db table. If found, I verify the cleartext token from the cookie with the hashed token from the corresponding matching row. If not found or the token is not verified, I assume that either a cookie theft has occurred OR the cookie value has been manipulated. I invalidate / delete ALL the authentication persistence data of this user so that it won't "stay logged in" anymore on ANY browser it opted for, and is not authenticated.
If the cookie has passed the above test, it means that the cookie and its data was valid and the user is considered authenticated. I regenerate the session identifier and put the user in the session. I generate a new token, hash and update it in the corresponding table row and issue the user browser a new cookie
auth
with the same user identifier, series number and expiration date but with the new token (cleartext version).
This all is processed per browser on each request. For every browser the process is identical. A user can opt to "stay logged in" on multiple browsers it wants to and will also have the option to invalidate all the persisted logins of their account.
To finish it off, I have a script that checks the db table every N minutes for rows older then 90 days and deletes them to keep the table clean and invalidate users persistent logins after the expiration time.