#native_company# #native_desc#
#native_cta#

E-mail Verification in PHP

By Voja Janjic
on April 18, 2016

One of the most common security features during the user registration process is e-mail verification. It is important to create it according to industry best practices in order to avoid potential security risks. Let’s discuss these best practices and see how to create e-mail verification in PHP.

We will start with the registration form:

<form method="post" action="http://mydomain.com/registration/">
	<fieldset class="form-group">
		<label for="fname">First Name:</label>
		<input type="text" name="fname" class="form-control" required />
    </fieldset>

    <fieldset class="form-group">
		<label for="lname">Last Name:</label>
		<input type="text" name="lname" class="form-control" required />
    </fieldset>

    <fieldset class="form-group">
		<label for="email">Last name:</label>
		<input type="email" name="email" class="form-control" required />
    </fieldset>

    <fieldset class="form-group">
		<label for="password">Password:</label>
		<input type="password" name="password" class="form-control" required />
    </fieldset>

    <fieldset class="form-group">
		<label for="cpassword">Confirm Password:</label>
		<input type="password" name="cpassword" class="form-control" required />
    </fieldset>

    <fieldset>
        <button type="submit" class="btn">Register</button>
    </fieldset>
</form> 

And with the following database structure:

CREATE TABLE IF NOT EXISTS `user` (
	`id` INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`fname` VARCHAR(255) ,
	`lname` VARCHAR(255) ,
	`email` VARCHAR(50) ,
	`password` VARCHAR(50) ,
	`is_active` INT(1) DEFAULT '0',
	`verify_token` VARCHAR(255) ,
	`created_at` TIMESTAMP,
	`updated_at` TIMESTAMP,
); 

Once the form has been submitted, we need to validate the input and create a new user:

// Validation rules
$rules = array(
    'fname' => 'required|max:255',
    'lname' => 'required|max:255',
	'email' => 'required',
	'password' => 'required|min:6|max:20',
	'cpassword' => 'same:password'
);

$validator = Validator::make(Input::all(), $rules);

// If input not valid, go back to registration page
if($validator->fails()) {
	return Redirect::to('registration')->with('error', $validator->messages()->first())->withInput();
}

$user = new User();
$user->fname = Input::get('fname');
$user->lname = Input::get('lname');
$user->password = Input::get('password');

// You will generate the verification code here and save it to the database

// Save user to the database
if(!$user->save()) {
	// If unable to write to database for any reason, show the error
	return Redirect::to('registration')->with('error', 'Unable to write to database at this time. Please try again later.')->withInput();
}

// User is created and saved to database
// Verification e-mail will be sent here

// Go back to registration page and show the success message
return Redirect::to('registration')->with('success', 'You have successfully created an account. The verification link has been sent to e-mail address you have provided. Please click on that link to activate your account.'); 

Upon registration, the user’s account remains inactive until the e-mail has been verified. This feature confirms that the user is the owner of the entered e-mail address and helps prevent spam, unauthorized e-mail usage and information leaks.

The process is quite simple  —  when a new user is created, an e-mail containing the verification link is sent to the e-mail address entered during the registration process. The user will not be able to login and use the Web application until he/she clicks on the verification link and confirms the e-mail address.

There are a few things to note regarding the verification link. It should contain a randomly generated token that should be long enough and valid for a specific amount of time in order to make brute-force attacks infeasible. Also, the user identifier should be included, so that a potential attack could not brute-force across multiple accounts.

Let’s see how to generate the verification link in practice:

// We will generate a random 32 alphanumeric string
// It is almost impossible to brute-force this key space
$code = str_random(32);
$user->confirmation_code = $code; 

Once is it generated and saved to the database, send it to the user:

Mail::send('emails.email-confirmation', array('code' => $code, 'id' => $user->id), function($message)
{
$message->from('[email protected]', 'Mydomain.com')->to($user->email, $user->fname . ' ' . $user->lname)->subject('Mydomain.com: E-mail confirmation');
}); 

The content of verification e-mail:

<!DOCTYPE html>
<html lang="en-US">
	<head>
		<meta charset="utf-8" />
	</head>

	<body>
		<p style="margin:0">
			Please confirm your e-mail address by clicking the following link:
			<a href="http://mydomain.com/verify?code=<?php echo $code; ?>&user=<?php echo $id; ?>"></a>
		</p>
	</body>
</html> 

Now let’s check to make sure it is valid:

$user = User::where('id', '=', Input::get('user'))
			->where('is_active', '=', 0)
			->where('verify_token', '=', Input::get('code'))
			->where('created_at', '>=', time() - (86400 * 2))
			->first();

if($user) {
	$user->verify_token = null;
	$user->is_active = 1;

	if(!$user->save()) {
		// If unable to write to database for any reason, show the error
		return Redirect::to('verify')->with('error', 'Unable to connect to database at this time. Please try again later.');
	}

	// Show the success message
	return Redirect::to('verify')->with('success', 'You account is now active. Thank you.');
}

// Code not valid, show error message
return Redirect::to('verify')->with('error', 'Verification code not valid.'); 

Conclusion

The code shown in this tutorial is for educational purposes and is not thoroughly tested. Please test it before using it in a live Web application. It is written in Laravel framework, but can be easily adjusted to any other PHP framework. Also, the verification code has a time limit of 48 hours and after that it will expire. It would be good to implement a cron job that will remove inactive users with expired verification codes from time to time.