CSRF Protection
Cross-Site Request Forgery (CSRF) protection prevents malicious sites from performing unauthorized actions on behalf of your users.
How CSRF Attacks Work
CSRF attacks exploit the trust a site has in a user's browser. If a user is logged into your site, an attacker's site could submit a form to your site, and the browser would include the user's session cookie.
Protection Mechanism
ZephyrPHP generates a unique token for each user session. This token must be included in all POST, PUT, PATCH, and DELETE requests. Since attackers can't know this token, forged requests will be rejected.
Using CSRF Protection
HTML Forms
Include the CSRF field in every form:
<form method="POST" action="/posts">
{{ csrf_field()|raw }}
<input type="text" name="title">
<textarea name="content"></textarea>
<button type="submit">Create Post</button>
</form>
The csrf_field() function generates:
<input type="hidden" name="_token" value="abc123def456...">
Just the Token
If you need just the token value:
<meta name="csrf-token" content="{{ csrf_token() }}">
AJAX Requests
For JavaScript requests, include the token in a header:
Setup
<!-- In your HTML head -->
<meta name="csrf-token" content="{{ csrf_token() }}">
Fetch API
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': token
},
body: JSON.stringify({
title: 'My Post',
content: 'Post content...'
})
});
Axios
// Set default header for all requests
axios.defaults.headers.common['X-CSRF-TOKEN'] =
document.querySelector('meta[name="csrf-token"]').content;
// Now all requests include the token
axios.post('/api/posts', { title: 'My Post' });
jQuery
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$.post('/api/posts', { title: 'My Post' });
Session Integration
The CSRF token is managed through the session:
<?php
// Get current CSRF token
$token = session()->csrf();
// Generate a new token
$newToken = session()->regenerateCsrf();
// Verify a token manually
if (session()->verifyCsrf($submittedToken)) {
// Token is valid
}
Verifying Tokens
ZephyrPHP automatically verifies CSRF tokens for:
- POST requests
- PUT requests
- PATCH requests
- DELETE requests
The token is checked from:
_tokenin the request bodyX-CSRF-TOKENheader
Manual Verification
Verify tokens manually when needed:
<?php
use ZephyrPHP\Security\Security;
class PaymentController extends Controller
{
public function process()
{
$security = new Security();
$token = $this->request->input('_token')
?? $this->request->header('X-CSRF-TOKEN');
if (!$security->verifyCsrf($token)) {
abort(403, 'CSRF token mismatch');
}
// Process payment...
}
}
Excluding Routes
Some routes may need to accept external webhooks without CSRF verification. Configure exceptions in your middleware:
<?php
class VerifyCsrfToken
{
protected array $except = [
'/webhooks/stripe',
'/webhooks/github',
'/api/external/*'
];
public function handle($request, $next)
{
if ($this->shouldVerify($request)) {
$this->verifyCsrfToken($request);
}
return $next($request);
}
protected function shouldVerify($request): bool
{
foreach ($this->except as $pattern) {
if ($request->pathMatches($pattern)) {
return false;
}
}
return true;
}
}
Token Regeneration
Regenerate the CSRF token after sensitive actions:
<?php
class AuthController extends Controller
{
public function login()
{
// Authenticate user...
// Regenerate token after login
$this->session->regenerateCsrf();
return $this->redirect('/dashboard');
}
public function logout()
{
// Clear session and regenerate token
$this->session->destroy();
return $this->redirect('/');
}
}
Form Method Spoofing
HTML forms only support GET and POST. For PUT, PATCH, and DELETE, use method spoofing with CSRF:
<form method="POST" action="/posts/1">
{{ csrf_field()|raw }}
<input type="hidden" name="_method" value="DELETE">
<button type="submit">Delete Post</button>
</form>
<form method="POST" action="/posts/1">
{{ csrf_field()|raw }}
<input type="hidden" name="_method" value="PUT">
<input type="text" name="title" value="Updated Title">
<button type="submit">Update Post</button>
</form>
Troubleshooting
Token Mismatch Errors
If you're getting CSRF token mismatch errors:
- Check the token is included - Ensure
csrf_field()is in your form - Check session is active - The session must be started before generating/verifying tokens
- Check token hasn't expired - Sessions expire; ensure users resubmit forms after long inactivity
- Check AJAX headers - Ensure the X-CSRF-TOKEN header is being sent
Always include CSRF protection in forms, even for seemingly harmless actions like search. It's better to be consistently secure than to try to determine which actions "need" protection.