Chapter 5.4: Working with CSRF Tokens
Study time: 30 minutes
1. What is a CSRF Attack? "Hijacking" Your Ship
Imagine you are logged into the control panel of your space fleet (space-api.test
). In the next tab, you open a harmless website with cats (evil-cats.com
). This site has a hidden form that automatically sends a request to your site at POST /api/planets/1/delete
.
Since you are already authenticated on space-api.test
, your browser will kindly attach all your cookies to this request. The Laravel server will see a valid session and think that you yourself decided to decommission the planet. The planet will be deleted without your knowledge.
This is CSRF (Cross-Site Request Forgery) — an attack in which an attacker forces the browser of an authenticated user to perform an unwanted action on a trusted site.
💡 Space Analogy:
You are the captain of a ship, and you have a key card (session/cookie). An attacker cannot steal your card. But he can trick you into applying it to a resource decommissioning terminal while you are distracted. A CSRF token is like a PIN code that you need to enter along with the card. The attacker does not know the PIN code, and his attack fails.
2. How Laravel Protects Against CSRF?
By default, Laravel protects all "unsafe" web requests (POST, PUT, PATCH, DELETE) with a CSRF token.
- When generating a page, Laravel creates a unique, random token for the user's session.
- This token is embedded in HTML forms.
- When the form is submitted, the token is sent along with the request.
- On the server, the
VerifyCsrfToken
middleware compares the token from the request with the token stored in the session. - If the tokens do not match, Laravel aborts the request with a 419 error (Session Expired/Page Expired).
Important: API routes in routes/api.php
are not protected by CSRF, as they assume a different authentication mechanism (e.g., Sanctum tokens), rather than cookie-based sessions. Our current problem concerns the web routes and pages that we create in routes/web.php
.
3. Using a CSRF Token in HTML Forms
This is the simplest scenario. Laravel provides a special Blade directive for this.
Example: Form for creating a planet
Let's create a simple form in the file resources/views/planets/create.blade.php
:
<h2>Form for launching a new planet</h2>
<form action="/planets" method="POST">
@csrf {{-- Here it is, the magic! --}}
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
<label for="solar_system">Solar System:</label>
<input type="text" id="solar_system" name="solar_system" required>
{{-- ... other fields ... --}}
<button type="submit">Launch</button>
</form>
The @csrf
directive will automatically generate a hidden field in the form:
This is enough to protect standard HTML forms.
4. Using a CSRF Token in AJAX/Fetch Requests
In the previous chapter, we sent a DELETE
request using JavaScript. Now Laravel will block it with a 419 error. We need to add a CSRF token to the headers of our Fetch request.
Step 1: Make the token available to JavaScript
Add a meta tag with the token to the <head>
of your master layout resources/views/app.blade.php
. This is standard practice in Laravel.
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{-- Add the CSRF token to a meta tag --}}
<meta name="csrf-token" content="{{ csrf_token() }}">
{{-- ... --}}
</head>
The csrf_token()
function returns the current token.
Step 2: Modify the JavaScript to send the token
Now in our public/js/planets.js
, we can read this token and add it to the headers of all "unsafe" requests.
// ... in the file public/js/planets.js ...
document.addEventListener('DOMContentLoaded', () => {
// Get the token from the meta tag
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
const deleteButtons = document.querySelectorAll('.delete-btn');
deleteButtons.forEach(button => {
button.addEventListener('click', async (event) => {
// ... confirmation logic ...
try {
const response = await fetch(apiUrl, {
method: 'DELETE',
headers: {
'Accept': 'application/json',
'X-CSRF-TOKEN': csrfToken // <-- Add the token to the headers!
}
});
// ... rest of the response handling logic ...
} catch (error) {
// ...
}
});
});
});
- The header name
X-CSRF-TOKEN
is the standard that Laravel checks by default.
Now our AJAX requests are also protected. Try deleting a planet again — this time the request will be successful.
Quiz to Reinforce
🚀 Chapter Summary:
You have installed an "identification friend or foe" system on your spaceship, protecting it from CSRF attacks. You have learned to:
- Understand the nature and danger of CSRF attacks.
- Protect standard HTML forms with the
@csrf
directive. - Pass the CSRF token to JavaScript via a meta tag.
- Include the token in the headers of AJAX/Fetch requests for their successful execution.
Your web interfaces are now not only interactive, but also secure. In the next chapter, we will finish creating our web interface by looking at how to properly organize routing for web pages.