rememberme
rememberme copied to clipboard
Should not store user details in the cookie
When implementing this we stumbled upon a minor security improvement that could be done. We used this library with the username as an identifier, since we would also need something user related in order to actually know which user the cookie belongs to.
But we don't want anything user related in the cookie for making it even more secure.
So to mitigate this we decided to one way hash the user_id and use this in the cookie as a unique selector. But then we needed to store the actual user_id inside our cookie storage. But the implementation restricted us in doing so because of the storage interface.
So we worked around it to actually map this user_id in the storage to our identifier. What would you think about implementing a way to store additional information in the stores to make this a lot easier to implement?
It has more advantages, we could for instance keep track of the ip/geolocation/user-agent/device when this cookie was created. And allow our user to invalidate his cookies based on this information.
Curious about your thoughts on this!
What are the security issues with storing the username in the cookie? And, why don't you have a surrogate numeric key instead?
When the cookie is stolen, they would have that username. So i would prefer not to :) And you could use a surrogate numeric key, but then you will need to store that alongside the username or id to know which user it is.
But the library does not support storing additional information. Now i had to work around and extend the interface and create my own store. So my proposal would be to implement this so additional information can be stored.
Sorry for taking so long to respond. I'll answer now and hope it's still relevant, also as an example on how this library should be used.
As far as I understand you asked for two things: using an obscured user id as credential and storing additional information for each remember me login.
Obscuring the credential
The user "credential" stored in the cookie and used for lookup can be anything - a numeric user id corresponding to the primary key in a separate users
table, a unique user name or in your case a one-way hash of the unique user name. The login
method of the authenticator just checks if a valid login exists, the code that calls login
is then responsible for looking up which user that credential belongs to.
So on user registration you'd create an entry in your users
table with a column that contains the "secure" user id, e.g.
INSERT INTO users (name, password, rememberme_id)
VALUES (...)and you could for example hash the user name with an additional salt or secret and use that as
rememberme_id`.
On first login (with username and password), you'd call the authenticator with the rememberme_id
from your user object: $authentictor->createCookie($rememberme_id)
. This will store the cookie for the user and put the rememberme credentials (triplet) in the database.
For the rememberme login, your code could look like this:
$loginResult = $authenticator->login();
if ($loginResult->isSuccess())
$rememberMeId = $loginResult->getCredential();
$user = $userTable->findOneByRemembermeId($rememberMeId);
}
Storing additional information
Here you can use the persistent token of the triplet as the key to look up or store the additional information:
if ($loginResult->isSuccess())
$cookieValue = $authenticator->getCookie()->getValue();
$triplet = Triplet::fromString($cookieValue);
$additionalInfoTable->storeAdditionalInformation($triplet->getPersistentToken(), [
'device' => $deviceService->getDeviceId(),
'ip' => $ipService->getIp(),
// etc
]);
The class in $additionalInfoTable
can use a different table with the persistent token as the primary key or it can use the rememberme table, with additional columns. In the latter case, you must use $triplet->getSaltedPersistentToken()
with the same salt as Authenticator
. If your storage implementation hashes the tokens, your $additionalInfoTable
must use the same hash function on the persistent token before using it as a key.
Adding additional data to the storage interface and the success result would be a small performance optimization at the const of making the API more complex, so I'm not willing to do that.