241 lines
7.1 KiB
Markdown
241 lines
7.1 KiB
Markdown
# CSRF Protection
|
||
|
||
* Introduction
|
||
* Preventing CSRF Requests
|
||
* Excluding URIs
|
||
* X-CSRF-Token
|
||
* X-XSRF-Token
|
||
|
||
## Introduction
|
||
|
||
Cross-site request forgeries are a type of malicious exploit whereby
|
||
unauthorized commands are performed on behalf of an authenticated user.
|
||
Thankfully, Laravel makes it easy to protect your application from [cross-site
|
||
request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery)
|
||
(CSRF) attacks.
|
||
|
||
#### An Explanation of the Vulnerability
|
||
|
||
In case you're not familiar with cross-site request forgeries, let's discuss
|
||
an example of how this vulnerability can be exploited. Imagine your
|
||
application has a `/user/email` route that accepts a `POST` request to change
|
||
the authenticated user's email address. Most likely, this route expects an
|
||
`email` input field to contain the email address the user would like to begin
|
||
using.
|
||
|
||
Without CSRF protection, a malicious website could create an HTML form that
|
||
points to your application's `/user/email` route and submits the malicious
|
||
user's own email address:
|
||
|
||
|
||
|
||
1<form action="https://your-application.com/user/email" method="POST">
|
||
|
||
2 <input type="email" value="[[email protected]](/cdn-cgi/l/email-protection)">
|
||
|
||
3</form>
|
||
|
||
4
|
||
|
||
5<script>
|
||
|
||
6 document.forms[0].submit();
|
||
|
||
7</script>
|
||
|
||
|
||
<form action="https://your-application.com/user/email" method="POST">
|
||
<input type="email" value="[[email protected]](/cdn-cgi/l/email-protection)">
|
||
</form>
|
||
|
||
<script>
|
||
document.forms[0].submit();
|
||
</script>
|
||
|
||
If the malicious website automatically submits the form when the page is
|
||
loaded, the malicious user only needs to lure an unsuspecting user of your
|
||
application to visit their website and their email address will be changed in
|
||
your application.
|
||
|
||
To prevent this vulnerability, we need to inspect every incoming `POST`,
|
||
`PUT`, `PATCH`, or `DELETE` request for a secret session value that the
|
||
malicious application is unable to access.
|
||
|
||
## Preventing CSRF Requests
|
||
|
||
Laravel automatically generates a CSRF "token" for each active [user
|
||
session](/docs/12.x/session) managed by the application. This token is used to
|
||
verify that the authenticated user is the person actually making the requests
|
||
to the application. Since this token is stored in the user's session and
|
||
changes each time the session is regenerated, a malicious application is
|
||
unable to access it.
|
||
|
||
The current session's CSRF token can be accessed via the request's session or
|
||
via the `csrf_token` helper function:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Request;
|
||
|
||
2
|
||
|
||
3Route::get('/token', function (Request $request) {
|
||
|
||
4 $token = $request->session()->token();
|
||
|
||
5
|
||
|
||
6 $token = csrf_token();
|
||
|
||
7
|
||
|
||
8 // ...
|
||
|
||
9});
|
||
|
||
|
||
use Illuminate\Http\Request;
|
||
|
||
Route::get('/token', function (Request $request) {
|
||
$token = $request->session()->token();
|
||
|
||
$token = csrf_token();
|
||
|
||
// ...
|
||
});
|
||
|
||
Anytime you define a "POST", "PUT", "PATCH", or "DELETE" HTML form in your
|
||
application, you should include a hidden CSRF `_token` field in the form so
|
||
that the CSRF protection middleware can validate the request. For convenience,
|
||
you may use the `@csrf` Blade directive to generate the hidden token input
|
||
field:
|
||
|
||
|
||
|
||
1<form method="POST" action="/profile">
|
||
|
||
2 @csrf
|
||
|
||
3
|
||
|
||
4 <!-- Equivalent to... -->
|
||
|
||
5 <input type="hidden" name="_token" value="{{ csrf_token() }}" />
|
||
|
||
6</form>
|
||
|
||
|
||
<form method="POST" action="/profile">
|
||
@csrf
|
||
|
||
<!-- Equivalent to... -->
|
||
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
|
||
</form>
|
||
|
||
The `Illuminate\Foundation\Http\Middleware\ValidateCsrfToken`
|
||
[middleware](/docs/12.x/middleware), which is included in the `web` middleware
|
||
group by default, will automatically verify that the token in the request
|
||
input matches the token stored in the session. When these two tokens match, we
|
||
know that the authenticated user is the one initiating the request.
|
||
|
||
### CSRF Tokens & SPAs
|
||
|
||
If you are building an SPA that is utilizing Laravel as an API backend, you
|
||
should consult the [Laravel Sanctum documentation](/docs/12.x/sanctum) for
|
||
information on authenticating with your API and protecting against CSRF
|
||
vulnerabilities.
|
||
|
||
### Excluding URIs From CSRF Protection
|
||
|
||
Sometimes you may wish to exclude a set of URIs from CSRF protection. For
|
||
example, if you are using [Stripe](https://stripe.com) to process payments and
|
||
are utilizing their webhook system, you will need to exclude your Stripe
|
||
webhook handler route from CSRF protection since Stripe will not know what
|
||
CSRF token to send to your routes.
|
||
|
||
Typically, you should place these kinds of routes outside of the `web`
|
||
middleware group that Laravel applies to all routes in the `routes/web.php`
|
||
file. However, you may also exclude specific routes by providing their URIs to
|
||
the `validateCsrfTokens` method in your application's `bootstrap/app.php`
|
||
file:
|
||
|
||
|
||
|
||
1->withMiddleware(function (Middleware $middleware) {
|
||
|
||
2 $middleware->validateCsrfTokens(except: [
|
||
|
||
3 'stripe/*',
|
||
|
||
4 'http://example.com/foo/bar',
|
||
|
||
5 'http://example.com/foo/*',
|
||
|
||
6 ]);
|
||
|
||
7})
|
||
|
||
|
||
->withMiddleware(function (Middleware $middleware) {
|
||
$middleware->validateCsrfTokens(except: [
|
||
'stripe/*',
|
||
'http://example.com/foo/bar',
|
||
'http://example.com/foo/*',
|
||
]);
|
||
})
|
||
|
||
For convenience, the CSRF middleware is automatically disabled for all routes
|
||
when [running tests](/docs/12.x/testing).
|
||
|
||
## X-CSRF-TOKEN
|
||
|
||
In addition to checking for the CSRF token as a POST parameter, the
|
||
`Illuminate\Foundation\Http\Middleware\ValidateCsrfToken` middleware, which is
|
||
included in the `web` middleware group by default, will also check for the
|
||
`X-CSRF-TOKEN` request header. You could, for example, store the token in an
|
||
HTML `meta` tag:
|
||
|
||
|
||
|
||
1<meta name="csrf-token" content="{{ csrf_token() }}">
|
||
|
||
|
||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||
|
||
Then, you can instruct a library like jQuery to automatically add the token to
|
||
all request headers. This provides simple, convenient CSRF protection for your
|
||
AJAX based applications using legacy JavaScript technology:
|
||
|
||
|
||
|
||
1$.ajaxSetup({
|
||
|
||
2 headers: {
|
||
|
||
3 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
||
|
||
4 }
|
||
|
||
5});
|
||
|
||
|
||
$.ajaxSetup({
|
||
headers: {
|
||
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
||
}
|
||
});
|
||
|
||
## X-XSRF-TOKEN
|
||
|
||
Laravel stores the current CSRF token in an encrypted `XSRF-TOKEN` cookie that
|
||
is included with each response generated by the framework. You can use the
|
||
cookie value to set the `X-XSRF-TOKEN` request header.
|
||
|
||
This cookie is primarily sent as a developer convenience since some JavaScript
|
||
frameworks and libraries, like Angular and Axios, automatically place its
|
||
value in the `X-XSRF-TOKEN` header on same-origin requests.
|
||
|
||
By default, the `resources/js/bootstrap.js` file includes the Axios HTTP
|
||
library which will automatically send the `X-XSRF-TOKEN` header for you.
|
||
|