Jump to content

email verification security concerns


ajoo
Go to solution Solved by Jacques1,

Recommended Posts

Hi all, 

 

I am using the following code snippet to send a mail on registration for the purpose of account verification by the user. 

<?php
$user = 'Jack';
$pass = 'You may pass';

// a random string to be checked against intself stored in the DB
$mc = md5($_SERVER['REMOTE_ADDR'].microtime().rand(1,100000));

function send_mail($from,$to,$subject,$body)
{
        $headers = '';
        $headers .= "From: $from\n";
        $headers .= "Reply-to: $from\n";
        $headers .= "Return-Path: $from\n";
        $headers .= "Message-ID: <" . md5(uniqid(time())) . "@" . $_SERVER['SERVER_NAME'] . ">\n";
        $headers .= "MIME-Version: 1.0\n";
        $headers .= "Date: " . date('r', time()) . "\n";

        if(mail($to,$subject,$body,$headers)=== true) return true; 
		else return false;
}

if(send_mail( 'mymail@gmail.com',
'their@gmail.com',
'Register your Account.' 
"Click on this link  http://www.yoursite.com/registeracc.php?email='their@gmail.com'&mc=".$mc." to activate your account"
) === true) echo "Success";
else echo "Failed";
?>

I would like to know if this is Ok or is there a better and more secure way to do it?  Are there any security concerns that should be taken into account here?

 

Thanks all !

 

Link to comment
Share on other sites

You do not need to include the email address in the link, just the the token. You'd search the DB for the token when the click the link then you can get the email address and other data from there.

 

Other than that, sending a token is a fairly standard way of letting someone confirm their email address. Just make sure the only thing you do with the token is mark the account active. Don't have it auto-login the user after activation and don't show any personal data on the activation page.

  • Like 1
Link to comment
Share on other sites

Hi Kicken, 

 

Thanks for the reply. The token is being used only to mark the account active. There is no autologin after that. Just a message on a page welcoming the user and a button to redirect to the login page. 

$mc = md5($_SERVER['REMOTE_ADDR'].microtime().rand(1,100000));
 

is this method of creating a random token good enough ? 

 

I am using the email to check if it's a valid email matching one in the database before I go ahead and actually activate the account but I could do that with the token as well I guess. So maybe I can remove the email in that case as suggested by you.   

 

Thanks again.

Link to comment
Share on other sites

I'd use sha1 and something related to the user rather than the IP (their email, the User ID, whatever). You'll also want to ensure generated tokens expire after a given time frame and after they have been used. That would probably be "good enough."

 

If you want something more random, then use a better random source such as openssl_random_pseudo_bytes, random_bytes(PHP 7, but there is a shim available for older versions), or mcrypt_create_iv.

 

With those you'd get pure random string of bytes, which you can then either hash or hex-encode to form your token.

Edited by kicken
Link to comment
Share on other sites

Hi Kicken, 

 

Would this be the right way to do it and is this good enuff from the security standpoint.

$user = 'Jack';
$mailcode = bin2hex(random_bytes(16));

$s = hash_hmac('sha256', $mailcode, $user, true);
$s = base64_encode($s);


And then use $s as the secure token.

 

Thanks loads.

Link to comment
Share on other sites

  • Solution

Your send_mail() function has no protection against mail header injection. The above code with mostly hard-coded arguments may not be vulnerable, but that's just a happy coincidence. As soon as you have to deal with dynamic input, you will run into security problems.

 

Using the low-level mail() function is generally a bad idea, because it's far too dangerous and error-prone. You should use a proper library like PHPMailer instead.

 

 

 

Would this be the right way to do it and is this good enuff from the security standpoint.

$user = 'Jack';
$mailcode = bin2hex(random_bytes(16));

$s = hash_hmac('sha256', $mailcode, $user, true);
$s = base64_encode($s);


And then use $s as the secure token.

 

This code makes no sense.

 

A HMAC is a message authentication code used to protect data from manipulation. You don't have this kind of data. And the third argument must be a (binary) cryptographic key, not a simple name. I'm surprised that PHP even accepts that input.

 

Neither hashes nor HMACs make sense in this context. You need to generate a binary random number and then encode it to make it human readable. A hash is only used to safely store the random number. So the procedure is as follows:

  • generate random bytes
  • encode the random bytes (hex-encoding is usually the most robust variant) and send the encoded token in an e-mail
  • hash the raw random bytes and store the hash in the database; a simple SHA-256 hash is enough in this case, because random bytes cannot easily be brute-forced like a password from a human user
  • Like 1
Link to comment
Share on other sites

Hi Guru Jacques, 

 

 

 

  • generate random bytes
  • encode the random bytes (hex-encoding is usually the most robust variant) and send the encoded token in an e-mail
  • hash the raw random bytes and store the hash in the database; a simple SHA-256 hash is enough in this case, because random bytes cannot easily be brute-forced like a password from a human user

 

 

Would this be the correct equivalent ? 

$mailcode = bin2hex(random_bytes(16));  // Use this to send as a token in the email
$s = hash('sha256', $mailcode, true);  // Store this hash in the DB for the comparison later.


if the above is OK, then I would like to ask what is the need to hash the token before storing it in the DB ?

 

Thank !

 

 

  •  
Link to comment
Share on other sites

The places where you use raw bytes and encoded bytes are mixed up. You need to store the raw random bytes in a variable. Then you send the encoded bytes by e-mail (the encoding is just for human readability) and hash the raw(!) bytes with SHA-256 so that you can store the hash in the database. The hash itself should also be encoded, which is the default of hash() when you omit the true argument.

<?php

$raw_token = random_bytes(16);

// send this to the user
$encoded_token = bin2hex($raw_token);

// store this in the database
$token_hash = hash('sha256', $raw_token);

Hashing the encoded token doesn't really make sense, because this just bloats the random input. And storing a binary hash makes it more difficult to handle the data in the database.

 

 

 

if the above is OK, then I would like to ask what is the need to hash the token before storing it in the DB ?

 

Because otherwise the token is stored as plaintext. If anybody manages to break into the database, they get a large collection of valid tokens and can abuse them. The damage might be relatively low as long as the tokens are only used for e-mail verification, but it's still an unnecessary risk. Only the user is supposed to know the token, so there's no reason to store it as plaintext.

  • Like 1
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.