Laravel CSRF 保护
简介
CSRF 跨站点请求伪造是一种恶意攻击手段,利用这种手段,伪造经过身份验证的用户执行未经授权的命令。 值得庆幸的是,Laravel可以轻松保护我们应用程序免受跨站点请求伪造(CSRF)攻击。
了解CSRF漏洞
如果您不熟悉跨站点请求伪造,让我们讨论一个如何利用此漏洞。 假设我们的应用程序具有一个/user/email
路由,该路由接受POST请求以更改经过身份验证的用户的电子邮件地址。 最有可能的是,此路由希望电子邮件输入字段包含用户希望开始使用的电子邮件地址。
如果没有CSRF保护,恶意网站可能会创建一个HTML表单,该表单指向我们应用程序的/user/email
路由,并提交该恶意用户自己的电子邮件地址:
<form action="https://your-application.com/user/email" method="POST">
<input type="email" value="malicious-email@example.com">
</form>
<script>
document.forms[0].submit();
</script>
如果恶意网站在页面加载时自动提交表单,则恶意用户只需要诱使我们应用程序的一个毫无戒心的用户访问他们的网站,他们的电子邮件地址就会在应用程序中更改。
为防止此漏洞,我们需要检查每个传入的POST,PUT,PATCH或DELETE请求,以获取恶意应用程序无法访问的session 密钥。
阻止 CSRF 请求
Laravel为应用程序的每个活动用户会话自动生成CSRF“token”。 此token用于验证经过身份验证的用户是实际向应用程序发出请求的人。 由于此令牌存储在用户的会话中,并且每次重新生成会话时都会更改,因此恶意应用程序将无法访问它。
可以通过请求的session或通过csrf_token
函数来访问当前会话的CSRF token:
use Illuminate\Http\Request;
Route::get('/token', function (Request $request) {
$token = $request->session()->token();
$token = csrf_token();
// ...
});
每当在应用程序中定义“ POST”,“ PUT”,“ PATCH”或“ DELETE” HTML表单时,都应在表单中包含隐藏的CSRF _token
字段,以便CSRF保护中间件可以验证请求。 为了方便起见,我们可以使用@csrf Blade
指令生成隐藏的 token 输入字段:
<form method="POST" action="/profile">
@csrf
<!-- Equivalent to... -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>
默认情况下,Web中间件组中包含的App\Http\Middleware\VerifyCsrfToken
中间件将自动验证请求输入中的token是否与会话中存储的token匹配。 当这两个token匹配时,我们知道经过身份验证的用户就是发起请求的用户。
CSRF Tokens & SPAs
如果要将Laravel用作API后端的SPA,则应查阅 Laravel Sanctum文档,以获取有关使用API进行身份验证和防范CSRF漏洞的信息。
从CSRF保护中排除URI
有时可能希望从CSRF保护中排除一组URI。 例如,如果正在使用Stripe处理付款并使用其Webhook系统,则需要从CSRF保护中排除Stripe Webhook
的处理程序路由,因为Stripe不知道要发送到相应的路由所需的CSRF token。
通常我们需要将这些不需要CSRF保护的路由放在web
中间件组之外。App\Providers\RouteServiceProvider
会将这些中间件应用于route/web.php
中的所有的路由。其中就包括CSRF保护的中间件。其实我们可以通过将其URI添加到VerifyCsrfToken
中间件的$except
属性中来排除路由:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'stripe/*',
'http://jiyik.com/w/Laravel',
'http://jiyik.com/w/*',
];
}
为了方便起见,运行测试时,所有路由都会自动禁用CSRF中间件。
X-CSRF-TOKEN
除了将CSRF token 作为POST参数进行检查之外,App\Http\Middleware\VerifyCsrfToken
中间件还将检查X-CSRF-TOKEN
请求头。 例如,我们可以将 token 存储在HTML meta标记中:
<meta name="csrf-token" content="{{ csrf_token() }}">
然后,我们可以使用类似jQuery的库将token自动添加到所有请求标头中。 这可以使用JavaScript技术为基于AJAX的应用程序提供简单,便捷的CSRF保护:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
X-XSRF-TOKEN
Laravel将当前CSRF令牌存储在加密的XSRF-TOKEN cookie中,该cookie包含在框架生成的每个响应中。 我们可以使用cookie值来设置X-XSRF-TOKEN请求头。
由于一些JavaScript框架和库(例如Angular和Axios)会自动将其值放在同源请求的X-XSRF-TOKEN标头中,因此该cookie的发送主要是为了方便开发人员。
提示: 默认情况下,
resources/js/bootstrap.js
文件包含Axios HTTP库,它将自动为我们发送X-XSRF-TOKEN标头。