Enforce email uniqueness verification during registration process
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255',
'password' => 'required|string|min:8',
]);
if ($validator->fails()) {
return response()->json($validator->errors()->toJson(), 400);
}
$user = User::create([
'name' => $request->get('name'),
'email' => $request->get('email'),
'password' => Hash::make($request->get('password')),
]);
return response()->json(compact('user'),201);
}
In the above code, the
register
function is used to register a new user. This function first validates the request data using Laravel's built-in validation. It checks if the 'name', 'email', and 'password' fields are present and meet certain criteria.
The 'email' field is validated as a string, having a maximum length of 255, and as an email. However, Laravel's email validation allows the plus character (+) in the local part of the email address, as it is valid according to the email format specification (RFC 5322).
This means that an attacker can register multiple accounts with the same email address by appending a plus sign and any string to the local part of the email. For example, 'attacker@example.com', 'attacker+1@example.com', 'attacker+2@example.com', and so on, will all be treated as unique by the system but will actually deliver emails to the same address, 'attacker@example.com'.
After validation, the
User::create
method is used to create a new user with the validated data. The password is hashed before storing it in the database. The newly created user is then returned in the response.
The vulnerability lies in the fact that the system does not properly verify the uniqueness of the email address, allowing an attacker to create multiple accounts with the same email address.
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8',
]);
if ($validator->fails()) {
return response()->json($validator->errors()->toJson(), 400);
}
$email = $request->get('email');
$email = $this->normalizeEmail($email);
$user = User::create([
'name' => $request->get('name'),
'email' => $email,
'password' => Hash::make($request->get('password')),
]);
return response()->json(compact('user'),201);
}
private function normalizeEmail($email)
{
$email = strtolower($email);
$emailParts = explode('@', $email);
$emailParts[0] = explode('+', $emailParts[0])[0];
return implode('@', $emailParts);
}
The updated code includes a unique constraint on the 'email' field in the validation rules. This ensures that the email address is not only a valid email but also unique in the 'users' table.
The email address is normalized before it is stored in the database. The
normalizeEmail
function converts the email address to lowercase and removes any plus signs and the characters following them before the '@' symbol. This is done by splitting the email address into two parts at the '@' symbol, then splitting the first part at the '+' symbol and only keeping the part before the '+'. The parts are then joined back together to form the normalized email address.
This normalization process ensures that email addresses like 'example+1@test.com' and 'example+2@test.com' are treated as the same as 'example@test.com', preventing an attacker from creating multiple accounts with the same email address.