ajoo Posted June 7, 2016 Share Posted June 7, 2016 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 ! Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/ Share on other sites More sharing options...
kicken Posted June 7, 2016 Share Posted June 7, 2016 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. 1 Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533462 Share on other sites More sharing options...
ajoo Posted June 7, 2016 Author Share Posted June 7, 2016 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. Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533466 Share on other sites More sharing options...
kicken Posted June 7, 2016 Share Posted June 7, 2016 (edited) 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 June 7, 2016 by kicken Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533476 Share on other sites More sharing options...
ajoo Posted June 8, 2016 Author Share Posted June 8, 2016 Thanks for the clarifications once again. Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533478 Share on other sites More sharing options...
ajoo Posted June 8, 2016 Author Share Posted June 8, 2016 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. Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533480 Share on other sites More sharing options...
Solution Jacques1 Posted June 8, 2016 Solution Share Posted June 8, 2016 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 1 Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533483 Share on other sites More sharing options...
ajoo Posted June 9, 2016 Author Share Posted June 9, 2016 Thank you Guru Jacques!! for those inputs. I'll look into them and revert soon with the changes. Thanks !! Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533513 Share on other sites More sharing options...
ajoo Posted June 9, 2016 Author Share Posted June 9, 2016 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 ! Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533517 Share on other sites More sharing options...
Jacques1 Posted June 9, 2016 Share Posted June 9, 2016 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. 1 Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533525 Share on other sites More sharing options...
ajoo Posted June 9, 2016 Author Share Posted June 9, 2016 Thank you Sir ! Quote Link to comment https://forums.phpfreaks.com/topic/301305-email-verification-security-concerns/#findComment-1533527 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.