Protection against cross-site request forgery attacks
Route::post('/update-profile', 'ProfileController@update');
class ProfileController extends Controller
{
public function update(Request $request)
{
$user = Auth::user();
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->save();
return redirect('/profile');
}
}
The above PHP code is a typical example of a Laravel controller that updates a user's profile. The user's new name and email are taken from the request inputs and saved to the user's profile in the database.
However, this code is vulnerable to Cross-Site Request Forgery (CSRF) attacks. Here's why:
1. The code doesn't check for a CSRF token in the request. In a CSRF attack, an attacker tricks a victim into submitting a malicious request. It inherits the identity and privileges of the victim to perform an undesired function on their behalf. In most cases, the user is not aware that this has happened.
2. The
update
function doesn't verify if the request is coming from a trusted source. It blindly trusts the incoming request and performs the update operation.
3. Since this function can change critical user information such as name and email, a successful CSRF attack can lead to serious consequences. For example, an attacker can change the victim's email to their own email and then use the "forgot password" function to take over the account.
The lack of CSRF protection in this code means that any authenticated user can be tricked into sending a request to this endpoint, which will update their profile with the attacker's provided details.
Route::post('/update-profile', 'ProfileController@update');
class ProfileController extends Controller
{
public function update(Request $request)
{
$this->validate($request, [
'_token' => 'required',
]);
$user = Auth::user();
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->save();
return redirect('/profile');
}
}
The updated code now includes a CSRF token in the form that is submitted when updating the profile. This token is generated by Laravel using the
@csrf
Blade directive. This token is then included in the POST request when the form is submitted.
In the
update
method of the
ProfileController
, we now validate that the
_token
field is present in the request. This is the field where Laravel automatically stores the CSRF token. If the
_token
field is not present or does not match the token stored in the session, Laravel will throw an
Illuminate\\Session\\TokenMismatchException
, effectively preventing the request from being processed.
This way, we ensure that the request is indeed made by the authenticated user and not by a potential attacker. This is because the CSRF token is unique per session and is unknown to potential attackers.
For AJAX requests, you should include the CSRF token in the header of your requests. If you are using Axios, you can do this globally like this:
axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
<meta name="csrf-token" content="{{ csrf_token() }}">
regenerateToken
method on the session:
session()->regenerateToken();