Safely remember user

Asked

Viewed 4,912 times

17

That question does not refer to password security, or data encryption itself. The question is more logical. I would like to know what’s the safest way to create that scheme "remind me".

I was thinking of the following scheme:

  1. When authenticating the user create a cookie with session ID.
  2. Save session ID to user registration.
  3. Session expires in 48 hours.
  4. Update session ID on each access.

That is, my idea was to authenticate the user based on an ID saved in a cookie in the user’s browser and the user’s registration in the database, and confirm that this ID exists, because I think save the user and password (in cookies, it was the way I most found in the network for this issue), even if encrypted with hash and salt and etc, I do not trust much.

I wonder if my method is safe, what would be the possible flaws and what other methods I could use, or how I could improve my method.

  • I am confused on two points in your question: 1) you are asking about the "remember me", but your protocol seems to me more a simple means of maintaining the session (which is equal to what 99% of the sites use, by the way). What is your goal with this remind me? Is it simply that the user does not scroll down after closing the browser? 2) "save user and password (...) do not trust much" What do you mean? Surely you have to save this in BD, otherwise how could you authenticate the user? There are ways to login without needing a saved password (certificates, SRP) but the security is +- the same.

  • 2
  • 1

    This, is exactly the "remember me" IE, keep the session after closing the browser. About this save the password, sorry I expressed badly, but is that I searched a lot before asking here, and in all places the staff saved the user and password in cookies...

  • In fact, even though the password is a shared secret between user and server - so that theoretically It would be okay to move it from there to here - there are good reasons not to do this. Among them is the fact that the cookie data is permanently saved on the user’s computer (ideally the passwords only exist in memory, and there is a lot of effort being made - without success - to achieve this goal). You have every reason not to trust this method!

1 answer

21


Some items we should take care of:

  • Cookies are easy to steal information
  • Attacks via fake request between site or Cross-site request forgery
  • Restart the session and delete all cookies after password change, so that everything can be recreated.
  • Even if the user remains active, always request the password when involving financial transactions.
  • Never store user data in cookies such as email, password, Cpf, number of cards, etc.
  • Do not base security only on session ID, as this ID can also be cloned.
  • Do not use volatile or transferable IP data to validate a user.
  • Never leave the validity of eternal authentication.
  • Provide a way for the user to disconnect all places with extended session.

Thinking about the above mentioned criteria, we can arrive at the following model and minimize the risks.

Generating the authentication token

We will need to store authentication tokens on our server, for this a similar table should be created in our database.

 - id           // Um identificador para o token, não utilize auto_increment,
                // pois pode trazer problemas, 
 - user_id      // Relacionamento com as informações do usuário
 - token        // Armazena o token de autenticação
 - browser      // Identifica qual browser foi utilizado para autenticar
 - last_access  // Último acesso (timestamp)
 - created_at   // Data de criação (timestamp)

To generate the id from this table, we can use something simple like

md5( uniqid( mt_rand(), true ) );

In this solution we will also store the browser used, to make even more difficult the attempts of attacks, whether by session cloning, CSRF, automated attacks, etc.

In PHP there are functions that make it easy to identify the user’s browser, but if you prefer some alternative form, there are several on the internet. We will not import with browser version to avoid errors due to automatic updates.

To store the last user access, always update the field last_access using the function time().

Out in the field created_at also use the function time().

Token can be generated randomly, for example:

sha1( uniqid( mt_rand() + time(), true ) );

Every time the user authenticates, we will generate a new and store in this table.

In the cookie we will save only the id of the token and the token in itself.

$id = md5( uniqid( mt_rand(), true ) );
$token = sha1( uniqid( mt_rand() + time(), true ) );

// Armazenar o token na tabela do banco de dados

$expire = ( time() + ( 30 * 24 * 3600 ) ); // O cookie não deve ser eterno.
$cookieToken = array( 
    'i' => $id,
    't' => $token
);
setcookie( 'auth', json_encode( $cookieToken ), $expire, '/', 'www.dominio.com', isset( $_SERVER["HTTPS"] ), true );

Validating the token

In the previous code we inform that the cookie is only valid for a certain domain, however this can be changed. So we should validate where the request is coming from, and the simplest way to do that is by using the variable $_SERVER['REMOTE_HOST'], the domain must be known.

After validating the request, retrieve the cookie, deserialize the data and validate with the database.

$tokenData = isset( $_COOKIE['auth'] ) ? json_decode( $_COOKIE['auth'] ) : false;
if( $tokenData !== false ) {
    $id = $tokenData['i'];
    $token = $tokenData['t'];

    // Busque no banco de dados o id e valide o token;
    // Veja tambem a validade do token usando como base o campo 'created_at'
    // e o browser.
    // Se tudo estiver correto, apague este token, gere um novo
    // e inicie a sessão.
}

Increasing

To make it even more secure, you can register all Ips and Session_ids that used a particular token.

As every time a new session is started a new token is generated, simply record in a second table the IP that generated the token and the SESSION_ID, together with this data you will record the last interaction with your system and the connection time.

The table will look like this:

 - ip            // Ip do usuário $_SERVER['REMOTE_ADDRE']
 - token_id      // Id do token que foi gerado quando o usuário iniciou a sessão.
 - session_id    // Id da sessão session_id()
 - user_agent    // Informação completa do browser $_SERVER['HTTP_USER_AGENT']
 - time          // Tempo de conexão
 - created_at    // Quando a conexão foi iniciada
 - updated_at    // Última atualização (função time() para toda atualização)

Every time the user makes a request to our server with the active session, we will update the record of his current connection, retrieving it by session_id. If nothing is recovered, his session is invalid and we will grant access.

To update we should always check if the IP and the user_agent are the same, just as we can check if it has the token that was generated when the authentication was started.

To update connection time, calculate using the field created_at

time() - $created_at

Always check if there is more than one IP using a token at the same time. This may occur when the user’s provider exchanges the IP and the session remains active.

If a token has different IP requests with a very short time between them, we should invalidate the token, and redirect any request linked to it to the login screen.

Remarks

This way has some flaws, but already makes it difficult enough to try to steal session due to the fact that the token is constantly updated.

Database insertion and update requests will greatly increase for this model. To avoid problems, this model should be based on non-relational databases or use a cache layer such as the memcached, which will receive all requests and from time to time update the database.

We should always alert the user when more than one session with is active, and provide the possibility to invalidate all tokens, and log in again.

We should never allow the exchange of password without having a way to validate the authenticity of who is changing.

It is very important to alert the user about the use of cookies and have a well-written policy about it.

  • Great answer, but I would like a clarification on the points "Cookies are easy to steal information" and "Do not base security only on the session ID, because this ID can also be cloned" (can be in the form of references, if a complete explanation is too long to post here). As far as I know, a Cookie HttpOnly and Secure is very difficult to steal, maybe except for vulnerable plugins in the browser, but I may be quite mistaken of course (so I would like more information). And as for the REMOTE_HOST, If an attacker stole the cookie he can’t forge it either?

  • Very good, but it generated a doubt. Saving the user’s browser to validate, can generate error in mobile access, no?

  • 1

    @mgibsonbr actually the cookie using Httponly gets harder to steal using javascript, but in a request other than by ajax there are attacks known as cookie Hijacking, here it is difficult to explain. If it is with Secure there is excellent, but not everyone has money to pay for a digital certificate, etc and such.

  • @mgibsonbr The REMOTE_HOST does not know how to answer you how it could be changed, but there are some other implementations to validate this. In a quick search saw that it is possible to identify directly through the server firewall.

  • @Papacharlie Really, here I just explained the operation, but in my implementations I record whether it is mobile, desktop, tablet, used platform, etc. To identify if it is mobile, the user_agent will have written "iPad", "iPod", "iPhone", "Android", and so on, and the function of native php, has ways to provide you with this information. There are also very complete implementations that extract all this as done by Chris Schuld or that Jonathan Stoppani

  • I get it, the idea is right around the corner. Mobile access "usually" will be the same, so you can validate the desktop and play the mobile in exceptions list, without running the risk of deleting the old session.

  • 2

    Oops and forgot, thanks @Papacharlie. As on mobile you will not have active session, a new token will be generated only for it. Implementation does not prevent more than one token for the same user.

  • @marcusagm Thanks for the references, I will read more on the subject! I asked why - in certain circumstances - it is desirable to keep all in the cookie (ID and session variables, signed of course, and perhaps encrypted) to prevent each request from generating a BD hit. It is not a solution applicable to all cases, but when it is, it can give a significant improvement in performance. This is called "cookie-based Sessions", here are some examples (not in PHP): https://docs.djangoproject.com/en/1.7/topics/http/sessions/#using-cookie-based-Sessions http://security.stackexchange.com/q/14193/6939

  • And although it’s not exactly the same concept, that Tom Leek response on security.SE describes well the security implications this has (the first link I passed above also gives some alerts). Reiterating: it is not a solution for all cases, there is time that is applicable and there is time that is not. And obviously, for this to work the HttpOnly and the Secure are mandatory... (P.S. nowadays get a certificate is not so expensive, and who does not have one is vulnerable to Mitm during login anyway)

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.