[TOC]
:-: ![](http://static.laravelacademy.org/wp-content/uploads/2017/09/15060154227985.png)
# 简介
跨站请求伪造(CSRF)是一种通过伪装授权用户的请求来攻击授信网站的恶意漏洞。
Laravel 通过自带的 CSRF 保护中间件让避免应用遭到跨站请求伪造攻击变得简单:Laravel 会自动为每一个被应用管理的有效用户会话生成一个 CSRF “令牌”,然后将该令牌存放在 Session 中,该令牌用于验证授权用户和发起请求者是否是同一个人。
任何时候在 Laravel 应用中定义 HTML 表单,都需要在表单中引入 CSRF 令牌字段,这样 CSRF 保护中间件才能够对请求进行验证。要想生成包含 CSRF 令牌的隐藏输入字段,可以使用 Blade 指令 `@csrf`:
~~~
<form method="POST" action="/profile">
@csrf
...
</form>
~~~
中间件组 `web` 中的`中间件 VerifyCsrfToken` 会自动为我们验证请求输入的 token 值和 Session 中存储的 token 是否一致,如果没有传递该字段或者传递过来的字段值和 Session 中存储的数值不一致,则会抛出异常。
为了演示该功能,我们在 `routes/web.php` 中定义一组测试路由:
~~~
Route::get('form_without_csrf_token', function (){
return '<form method="POST" action="/hello_from_form"><button type="submit">提交</button></form>';
});
Route::get('form_with_csrf_token', function () {
return '<form method="POST" action="/hello_from_form">' . csrf_field() . '<button type="submit">提交</button></form>';
});
Route::get('hello_from_form', function (){
return 'hello laravel!';
});
~~~
我们在浏览器中访问 `http://blog.test/form_without_csrf_token` 并点击页面上的提交按钮时,页面报错,抛出 `MethodNotAllowedHttpException` 异常,出现这个异常往往就是意味着没有传递 CSRF 令牌字段或者传递的令牌字段不正确
而当我们访问 `http://blog.test/form_csrf_token` 并点击页面上的提交按钮时,页面显示正常。
> 注:CSRF 中间件只只作用于 `routes/web.php` 中定义的路由,因为该文件下的路由分配了 `web` 中间件组,而 `VerifyCsrfToken` 位于 `web` 中间件组中。
**CSRF 令牌 & JavaScript**
构建 JavaScript 驱动的应用时,为方便起见,可以让 JavaScript HTTP 库自动在每个请求中添加 CSRF 令牌。默认情况下,`resources/assets/js/bootstrap.js` 文件会将 `csrf-token` meta 标签值注册到 Axios HTTP 库。如果你没有使用这个库,则需要手动在应用中配置该实现。
# 排除指定 URL 不做 CSRF 安全校验
有时候我们需要从 CSRF 保护中间件中排除一些 URL,例如,如果你使用了第三方支付系统(如支付宝或微信支付)来处理支付并用到他们提供的回调功能,这时候就需要从 Laravel 的 CSRF 保护中间件中排除回调处理器路由,因为第三方支付系统并不知道要传什么 token 值给我们定义的路由。
通常我们需要将这种类型的路由放到文件 `routes/web.php` 之外,比如 `routes/api.php`。不过,如果必须要加到 `routes/web.php` 中的话,你也可以在 `VerifyCsrfToken` 中间件中将要排除的 URL 添加到 `$except` 属性数组:
~~~
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* 从 CSRF 验证中排除的 URL
*
* @var array
*/
protected $except = [
'alipay/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}
~~~
> 注:运行测试时 CSRF 中间件会自动禁止。
# X-CSRF-Token
除了将 CSRF 令牌作为 POST 参数进行验证外,还可以通过设置 `X-CSRF-Token` 请求头来实现验证,`VerifyCsrfToken` 中间件会检查 `X-CSRF-TOKEN` 请求头。实现方式如下,首先创建一个 `meta` 标签并将令牌保存到该 meta 标签:
~~~
<meta name="csrf-token" content="{{ csrf_token() }}">
~~~
然后在 js 库(如 jQuery)中添加该令牌到所有请求头,这为基于 AJAX 的请求提供了简单、方便的方式来避免 CSRF 攻击:
~~~
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
~~~
# X-XSRF-Token
Laravel 还会将 CSRF 令牌保存到名为 `XSRF-TOKEN` 的 Cookie 中,你可以使用该 Cookie 值来设置 `X-XSRF-TOKEN` 请求头。
一些 JavaScript 框架,比如 Angular 和 Axios,会为你自动进行上述设置,基本上你不太需要手动设置这个值。
最后,`VerifyCsrfToken` 中间件框架底层实现源码位于 `vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php`,感兴趣的同学可以去一窥究竟。
- 序言
- 新版特性
- 快速入门
- 升级指南
- 贡献指南
- API文档
- 安装配置
- 目录结构
- Homestead
- Valet
- 部署
- 核心概念
- 请求生命周期
- 服务容器
- 服务提供者
- 门面(Facades)
- 契约(Contracts)
- 框架基础
- 路由
- 中间件
- CSRF 保护
- 控制器
- 请求
- 响应
- 视图
- 生成 URL
- Session
- 验证
- 错误处理
- 日志
- 前端开发
- Blade 模板
- 本地化
- 前端脚手架
- 编译前端资源(Laravel Mix)
- 安全系列
- 登录认证
- API 认证
- 授权
- 加密
- 哈希
- 重置密码
- 进阶系列
- Artisan 控制台
- 集合
- 广播
- 缓存
- 事件
- 文件存储
- 辅助函数
- 邮件
- 通知
- 扩展包开发
- 队列
- 任务调度
- 数据库操作
- 快速入门
- 查询构建器
- 分页
- 迁移
- 数据填充
- Redis
- Eloquent ORM
- 快速入门
- 关联关系
- 集合
- 访问器 & 修改器
- API 资源类
- 序列化
- 应用测试
- 快速入门
- HTTP 测试
- 浏览器测试
- 数据库测试
- 模拟
- 官方扩展包
- Cashier(订阅支付解决方案)
- Envoy(远程操作解决方案)
- Horizon(队列系统解决方案)
- Passport(API 认证解决方案)
- Scout(全文搜索解决方案)
- Socialite(第三方登录解决方案)
- 相关下载
- Laravel 5.6 中文文档离线版
- Laravel 5.6 一键安装包