Laravel框架 之 CSRF

目录

  • 攻击条件

  • 跨域限制

  • 同源策略无效的HTML标签

    • 漏洞:-GET操作资源

    • 应对:-RESTful规范

    • 漏洞:-Form操作资源

    • 应对:-Anti-CSRF-Token

攻击条件

关于CSRF基础知识 可以参考浅谈CSRF攻击方式

这里想说的是CSRF攻击有以下两个必要条件

条件1: 某站点A登录成功Cookie有效

条件2: 站点B绕过跨域限制访问站点A

其中 条件1是无法回避的

但是 条件2却是有很大漏洞的

跨域限制

由于 浏览器的同源策略 是不允许跨域访问的

更多参考web安全 之 同源策略

当然 我们可以通过CORS允许部分域名的跨域访问 (千万不要允许所有域名跨域)

更多参考跨域资源共享 CORS 详解

同源策略无效的HTML标签

虽然 有跨域限制这一安全机制 但是同源策略对如下HTML标签却是无效的

  • script / link: 引用外部JavaScript、CSS资源

  • img / video / audio: 引用外部的图片、视频、音频资源

  • form: 提交表单数据

  • @font-face: 引用外部字体资源

  • iframe: 内联框架元素 (笔者暂未用过)

  • object / embed / applet: 插件 (笔者也没用过)

更多参考浏览器的同源策略

此时 攻击者就可以利用这些同源策略无效的HTML标签 来展开攻击

漏洞: GET操作资源

首先 某站点A登录成功Cookie有效

并且 站点A使用GET访问来操作资源如下

# GET
https://site-a.com/resource/delete?id=1

此时 站点B利用img标签来操作访问站点A


这样 站点B就成功删除了站点A的资源

JSONP跨域访问也是基于类似原理 只不过使用的HTML标签是script

应对: RESTful规范

由于 这里的img标签只支持GET请求

所以 应对此类CSRF的方法很简单

就是遵守RESTful规范 使用POST / PUT / DELETE动作来操作资源 GET动作来查询资源

更多参考理解RESTful架构

漏洞: Form操作资源

既然 GET方法不安全 那么POST动作是否就是安全的呢

首先 某站点A登录成功Cookie有效

并且 站点A使用GET访问来操作资源如下

# POST
https://site-a.com/resource # body: {"id":"1"}

此时 站点B利用form标签来操作访问站点A

这样 当用户在站点B"点击中大奖" 就成功修改了站点A的资源

应对: Anti CSRF Token

对于任何操作资源 例如POST请求 都需要在HTTP请求体中添一个加token 服务器校验token通过后才允许操作资源

例如 rememberme项目中的登录页面

// resources/views/sessions/create.blade.php

    
{{ csrf_field() }}

实际渲染的HTML内容如下


    

框架中处理CSRF的中间件是VerifyCsrfToken.php

// app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends Middleware
{
    protected $except = [
        //
    ];
}
// vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken
{
    public function handle($request, Closure $next)
    {
        if (
            $this->isReading($request) || // 忽略查询操作
            $this->runningUnitTests() ||
            $this->inExceptArray($request) ||
            $this->tokensMatch($request) // 校验token
        ) {
            return $this->addCookieToResponse($request, $next($request));
        }

        throw new TokenMismatchException;
    }

    // 校验token
    protected function tokensMatch($request)
    {
        // 获取请求体中的token
        $token = $this->getTokenFromRequest($request);

        return is_string($request->session()->token()) &&
               is_string($token) &&
               hash_equals($request->session()->token(), $token);
    }

    // 获取请求体中的token
    protected function getTokenFromRequest($request)
    {
        // 获取请求体中的token优先级: input('_token') > $request->header('X-CSRF-TOKEN') > $request->header('X-XSRF-TOKEN')
        $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

        if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
            $token = $this->encrypter->decrypt($header);
        }

        return $token;
    }
}

牺牲用户体验的情况下 也可以使用验证码的方式

参考

  • 浅谈CSRF攻击方式

  • 前端跨域解决方案

  • Web安全 之 CSRF攻击

  • Laravel之CSRF

  • Is CSRF possible with PUT or DELETE methods?

你可能感兴趣的:(Laravel框架 之 CSRF)