Secure password reset functionality
public function sendPasswordResetLink(Request $request)
{
$user = User::where('email', $request->email)->first();
if (!$user) {
return response()->json(['error' => 'User not found'], 404);
}
$token = str_random(60);
PasswordReset::create([
'email' => $request->email,
'token' => $token,
]);
$link = url('/password/reset/' . $token);
Mail::to($request->email)->send(new PasswordResetMail($link));
return response()->json(['message' => 'Password reset link sent']);
}
The above code is an example of a vulnerable password reset function in a Laravel application. The function
sendPasswordResetLink()
is used to generate a password reset link for the user who requests it.
The function first checks if the user with the given email exists in the database. If the user exists, it generates a random token and stores it along with the user's email in the
PasswordReset
table.
Then, it creates a password reset link by appending the token to the application's URL. The problem here is that the
url()
helper function in Laravel uses the
APP_URL
environment variable to generate the URL. If an attacker can manipulate the
APP_URL
to point to a domain under their control, they can receive the password reset links.
Finally, the function sends the password reset link to the user's email and returns a success message. If an attacker can poison the
APP_URL
, they can intercept the password reset link and potentially gain unauthorized access to the user's account.
public function sendPasswordResetLink(Request $request)
{
$request->validate([
'email' => 'required|email|exists:users,email',
]);
$user = User::where('email', $request->email)->first();
$token = hash('sha256', Str::random(60));
DB::table('password_resets')->insert([
'email' => $request->email,
'token' => $token,
'created_at' => Carbon::now()
]);
$link = secure_url('password/reset', $token);
Mail::to($request->email)->send(new PasswordResetMail($link));
return response()->json(['message' => 'Password reset link sent']);
}
The updated code now includes several security improvements to mitigate the risk of password reset poisoning:
1. Email Validation: The email address from the request is validated to ensure it is in the correct format and exists in the users table in the database. This is done using Laravel's built-in validation rules in the
$request->validate()
method.
2. Secure Token Generation: A secure, random token is generated using Laravel's
Str::random()
method, which is then hashed using SHA-256 for additional security.
3. Secure Token Storage: The token and the user's email are stored in a 'password_resets' table in the database. This table should be secured and only accessible by trusted parts of the application.
4. Secure Link Generation: The password reset link is generated using Laravel's
secure_url()
function, which creates a URL using HTTPS. The token is included as a parameter in the URL.
5. Secure Email Sending: The password reset link is sent to the user's email address using Laravel's
Mail::to()
function. This should be configured to use a secure email service.
6. Server-Side Validation: The server-side code that handles the password reset request (not shown here) should validate the token and the user's email address before allowing the password to be reset. This ensures that only legitimate password reset requests are processed.