Sheath rule
security-csrf-field
@csrf directive.
(dangerous)
#Why
CSRF tokens prevent other sites from submitting authenticated requests on a user's behalf. Laravel validates the token before handling state-changing form submissions.
#Examples
#Bad
<!-- Missing @csrf -->
<form action="/users" method="POST">
<input name="name" value="John">
<button>Create</button>
</form>
<!-- Missing @csrf on PUT/PATCH/DELETE -->
<form action="/users/1" method="POST">
@method('DELETE')
<button>Delete</button>
</form>
<!-- A submit control can override a GET form to POST -->
<form id="profile" action="/profile" method="GET"></form>
<button form="profile" formmethod="POST">Save</button>
#Good
<!-- POST form with @csrf -->
<form action="/users" method="POST">
@csrf
<input name="name" value="John">
<button>Create</button>
</form>
<!-- PUT/DELETE forms with both @csrf and @method -->
<form action="/users/1" method="POST">
@csrf
@method('DELETE')
<button>Delete</button>
</form>
<!-- GET forms don't need @csrf -->
<form action="/search" method="GET">
<input name="q" placeholder="Search...">
<button>Search</button>
</form>
<!-- Cross-origin POSTs are handled by the destination, not Laravel's middleware -->
<form action="https://payments.example/checkout" method="POST">
<button>Pay</button>
</form>
<!-- POST override protected by a token in its form owner -->
<form id="profile" action="/profile" method="GET">
@csrf
</form>
<button form="profile" formmethod="POST">Save</button>
<!-- Client interception owns the request and its CSRF mechanism -->
<form method="POST" wire:submit="save">
<button type="submit">Save</button>
</form>
<form method="POST" x-data @submit.prevent="save()">
<button type="submit">Save</button>
</form>
#Non-POST Submissions Are Exempt
Forms with method="GET" don't require CSRF protection because:
- GET requests should not modify data
- They're used for reading/searching, not writing
- Browsers handle them differently
HTML also defaults missing, empty, and invalid form method values to GET.
method="PUT", for example, does not make a browser submit PUT; Laravel forms
use method="POST" plus @method('PUT'). A submit-capable <button> or
<input> can override its form owner with formmethod="POST", so the rule
checks those controls as well, including controls linked by form="id".
#Application Submission Targets
The rule requires a token only when a possible POST target belongs to the Laravel application. It recognizes:
- Missing, empty, and relative
actionorformactionvalues - Complete
route(),url(),secure_url(), andaction()helper echoes - Absolute URLs whose host matches
config('app.url') - Additional exact hosts configured through
applicationHosts
Static absolute and protocol-relative URLs with any other host are external and do not need Laravel's CSRF token. An arbitrary dynamic destination is reported without a fix because it may resolve to an application route, while inserting a token could leak it if the destination instead resolves externally.
<?php
'security-csrf-field' => ['error', [
'applicationHosts' => ['admin.example', 'legacy.example'],
]],
Host matching is case-insensitive and ignores ports. A form that has an application POST target plus any external or unresolved submission target is still reported for the application route, but no fix is offered: the inserted field would also be submitted by GET and could send the session token to the other destination.
#Auto-fix
The fixer adds @csrf after the opening form tag:
<!-- Before -->
<form action="/users" method="POST">
<input name="name">
</form>
<!-- After -->
<form action="/users" method="POST">
@csrf
<input name="name">
</form>
The fix is dangerous and requires --dangerous, because adding a valid token
can change a form submission from a CSRF rejection into a working state-changing
request.
#How CSRF Protection Works
- Laravel generates a unique token per session
@csrfrenders:<input type="hidden" name="_token" value="...">- Laravel middleware verifies the token matches on submission
- Invalid/missing tokens result in 419 HTTP error
#Notes
- The
@csrfdirective must render a successful control owned by the form. Tokens inside<template>,<datalist>, or disabled fieldsets do not count; a token inside the first<legend>of a disabled fieldset remains successful. - A manual token outside the form counts when
form="id"associates it with the form and it is guaranteed to render. {{ csrf_field() }}and{!! csrf_field() !!}are recognized as equivalent Laravel helper calls; strings, methods, and similarly named functions are not- A manually written
<input name="_token">counts only when its entirevalueis a Blade echo consisting solely of a call to Laravel's globalcsrf_token()helper - Dynamic
methodorformmethodvalues are treated as potentially POST - Dynamic
actionorformactionvalues report without a fix unless they are a complete call to a recognized Laravel application URL helper - Missing
@csrfis not reported when the form body contains dynamic content such as{{ $slot }},@include,@stack,@yield, a nested component, or a raw echo - A POST path durably intercepted by
wire:submit,x-on:submit.prevent, or@submit.preventis not a native form submission, so this rule does not require a hidden token on that path. Non-durable.once,.passive,.outside, and.awaylisteners remain subject to the rule.
#References
- OWASP: Cross-Site Request Forgery - Detailed explanation of CSRF attacks
- Laravel: CSRF Protection - Laravel's built-in CSRF protection documentation
#Related Rules
- blade-method-field - Forms need @method for PUT/PATCH/DELETE