【全栈修炼】CORS和CSRF修炼宝典

《全栈修炼》系列

  1. 《【全栈修炼】OAuth2修炼宝典》

CORS 和 CSRF 太容易混淆了,看完本文,你就清楚了。php

1、CORS 和 CSRF 区别

先看下图:html

CORS 和 CSRF 区别

二者概念彻底不一样,另外经常咱们也会看到 XSS ,这里一块儿介绍:前端

  1. CORS : Cross Origin Resourse-Sharing 跨站资源共享json

  2. CSRF : Cross-Site Request Forgery 跨站请求伪造segmentfault

  3. XSS : Cross Site Scrit 跨站脚本攻击(为与 CSS 区别,因此在安全领域叫 XSS)api

2、CORS

1. 概念

跨来源资源共享(CORS),亦译为跨域资源共享,是一份浏览器技术的规范,提供了 Web 服务从不一样网域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不一样,CORS 除了 GET 请求方法之外也支持其余的 HTTP 请求。用 CORS 可让网页设计师用通常的 XMLHttpRequest,这种方式的错误处理比 JSONP 要来的好。另外一方面,JSONP 能够在不支持 CORS 的老旧浏览器上运做。现代的浏览器都支持 CORS。跨域

—— 维基百科浏览器

核心知识: CORS是一个W3C标准,它容许浏览器向跨源服务器,发出XMLHttpRequest 请求,从而克服 AJAX 只能同源使用的限制。安全

所以,实现 CORS 通讯的关键是服务器。只要服务器实现了 CORS 接口,就能够跨源通讯,即为了解决跨域问题服务器

2. CORS 请求类型

浏览器将 CORS 请求分红两类:简单请求(simple request)和非简单请求(not-so-simple request)。

简单请求通常包括下面两种状况:

状况 描述
请求方法 请求方法为:HEADGETPOST
HTTP 头信息 HTTP 头信息不超出如下几种字段:Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值 application/x-www-form-urlencodedmultipart/form-datatext/plain

凡是不一样时知足上面两个条件,就属于非简单请求

3. 简单请求的 CORS 流程

当浏览器发现咱们的 AJAX 请求是个简单请求,便会自动在头信息中,增长一个 Origin 字段。

Origin 字段用来讲明本次请求的来源(包括协议 + 域名 + 端口号),服务端根据这个值来决定是否赞成这次请求。

简单请求的 CORS 流程

Origin 指定的源不在许可范围,服务器会返回一个正常的 HTTP 回应,但浏览器会在响应头中发现 Access-Control-Allow-Origin 字段,便抛出异常。

Origin 指定的源在许可范围,服务器返回的响应头中会多出几个头信息字段:

简单请求的 CORS 流程

除了上面图中的头信息,通常会有如下三个相关头信息:

  1. Access-Control-Allow-Origin

该字段是必须的。表示许可范围的域名,一般有两种值:请求时 Origin 字段的值或者 *(星号)表示任意域名。

  1. Access-Control-Allow-Credentials

该字段可选。布尔值,表示是否容许在 CORS 请求之中发送 Cookie 。若不携带 Cookie 则不须要设置该字段。

当设置为 trueCookie 包含在请求中,一块儿发送给服务器。还须要在 AJAX 请求中开启 withCredentials 属性,不然浏览器也不会发送 Cookie

let xhr = new XMLHttpRequest();
xhr.withCredentials = true;
复制代码

注意: 若是前端设置 Access-Control-Allow-Credentialstrue 来携带 Cookie 发起请求,则服务端 Access-Control-Allow-Origin 不能设置为 *

  1. Access-Control-Expose-Headers

该字段可选。能够设置须要获取的字段。由于默认 CORS 请求时,XMLHttpRequest 对象的getResponseHeader()方法只能拿到如下 6 个基本字段:

Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma

4. 非简单请求的 CORS 流程

非简单请求状况如:请求方法是 PUT / DELETE 或者 Content-Type:application/json 类型的请求。

在非简单请求发出 CORS 请求时,会在正式通讯以前增长一次 “预检”请求(OPTIONS方法),来询问服务器,本次请求的域名是否在许可名单中,以及使用哪些头信息。

“预检”请求 经过之后,才会正式发起 AJAX 请求,不然报错。

4.1 预检请求

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
User-Agent: Mozilla/5.0...
...
复制代码

“预检”请求 信息中包含两个特殊字段:

  1. Access-Control-Request-Method

该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法,上例是 PUT

  1. Access-Control-Request-Headers

指定浏览器 CORS 请求额外发送的头信息字段,上例是 X-Custom-Header

4.2 预检响应

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Connection: Keep-Alive
...
复制代码

当预检请求经过之后,在预检响应头中,会返回 Access-Control-Allow- 开头的信息,其中 Access-Control-Allow-Origin 表示许可范围,值也能够是 *

当预检请求拒绝之后,在预检响应头中,不会返回 Access-Control-Allow- 开头的信息,并在控制台输出错误信息。

3、CSRF

1. 概念

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,一般缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登陆的Web应用程序上执行非本意的操做的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

—— 维基百科

核心知识: 跨站点请求伪造请求。

简单理解: 攻击者盗用你的身份,以你的名义发送恶意请求。

常见场景:以你名义发送邮件,发消息,盗取你的帐号,甚至于购买商品,虚拟货币转帐等等。

形成影响:我的隐私泄露以及财产安全。

2. CSRF 攻击流程

CSRF 攻击流程

上面描述了 CSRF 攻击的流程,其中受害者完成两个步骤:

  1. 登陆受信任网站 A ,并在本地生成保存Cookie;
  2. 在不登出 A 状况下,访问病毒网站 B;

能够理解为:若以上两个步骤没有都完成,则不会受到 CSRF 攻击。

3. 服务端防护 CSRF 攻击

服务端防护的方式有不少,思想相似,都是在客户端页面增长伪随机数

3.1 Cookie Hashing(全部表单都包含同一个伪随机数)

最简单有效方式,由于攻击者理论上没法获取第三方的Cookie,因此表单数据伪造失败。以 php 代码为例:

<?php
    //构造加密的Cookie信息
    $value = "LeoDefenseSCRF";
    setcookie("cookie", $value, time()+3600);
?>
复制代码

在表单里增长Hash值,以认证这确实是用户发送的请求。

<?php
    $hash = md5($_COOKIE['cookie']);
?>
<form method="POST" action="transfer.php">
  <input type="text" name="toBankId">
  <input type="text" name="money">
  <input type="hidden" name="hash" value="<?=$hash;?>">
  <input type="submit" name="submit" value="Submit">
</form>
复制代码

而后在服务器端进行Hash值验证。

<?php
    if(isset($_POST['check'])) {
       $hash = md5($_COOKIE['cookie']);
       if($_POST['check'] == $hash) {
            doJob();
        } else {
     //...
      }
    } else {
      //...
    }
?>
复制代码

这个方法我的以为已经能够杜绝99%的CSRF攻击了,那还有1%呢....因为用户的 Cookie 很容易因为网站的 XSS 漏洞而被盗取,这就另外的1%。

通常的攻击者看到有须要算Hash值,基本都会放弃了,某些除外,因此若是须要100%的杜绝,这个不是最好的方法。

3.2 验证码

思路是:每次用户提交都须要用户在表单中填写一个图片上的随机字符串,这个方案能够彻底解决CSRF,但易用性差,而且验证码图片的使用涉及 MHTML 的Bug,可能在某些版本的微软IE中受影响。

3.3 One-Time Tokens(不一样的表单包含一个不一样的伪随机值)

须要注意“并行会话的兼容”。若是用户在一个站点上同时打开了两个不一样的表单,CSRF保护措施不该该影响到他对任何表单的提交。考虑一下若是每次表单被装入时站点生成一个伪随机值来覆盖之前的伪随机值将会发生什么状况:用户只能成功地提交他最后打开的表单,由于全部其余的表单都含有非法的伪随机值。必须当心操做以确保CSRF保护措施不会影响选项卡式的浏览或者利用多个浏览器窗口浏览一个站点。

php 实现以下:

  1. 先是 Token 令牌生成函数(gen_token())和 Session 令牌生成函数(gen_stoken()):
<?php
    function gen_token() {
        $token = md5(uniqid(rand(), true));
        return $token;
    }
  function gen_stoken() {
    $pToken = "";
    if($_SESSION[STOKEN_NAME]  == $pToken){
      $_SESSION[STOKEN_NAME] = gen_token();
    }    
    else{ }
  }
?>
复制代码
  1. WEB表单生成隐藏输入域的函数: 
<?php
    function gen_input() {
        gen_stoken();
        echo "<input type=\"hidden\" name=\"" . FTOKEN_NAME . "\"    value=\"" . $_SESSION[STOKEN_NAME] . "\"> ";
  }
?>
复制代码
  1. WEB表单结构:
<?php
    session_start();
    include("functions.php");
?>
<form method="POST" action="transfer.php">
    <input type="text" name="toBankId">
    <input type="text" name="money">
    <? gen_input(); ?>
    <input type="submit" name="submit" value="Submit">
</FORM>
复制代码
  1. 服务端核对令牌

这一步很简单,不作详细介绍。

4、XSS

注意: 本文简单介绍 XSS 知识,具体详细能够阅读 FEWY 写的 《跨站脚本攻击—XSS》segmentfault.com/a/119000002…

1. 概念

跨站脚本(英语:Cross-site scripting,一般简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它容许恶意用户将代码注入到网页上,其余用户在观看网页时就会受到影响。这类攻击一般包含了HTML以及用户端脚本语言。

—— 维基百科

XSS 攻击,通常是指攻击者经过在网页中注入恶意脚本,当用户浏览网页时,恶意脚本执行,控制用户浏览器行为的一种攻击方式。

常见 XSS 危害有:

  • 窃取用户Cookie,获取用户隐私,盗取用户帐号。
  • 劫持用户(浏览器)会话,从而执行任意操做,例如进行非法转帐、强制发表日志、发送电子邮件等。
  • 强制弹出广告页面,刷流量,传播跨站脚本蠕虫,网页挂马等。
  • 结合其余漏洞,如 CSRF 漏洞,实施进一步的攻击。

2. XSS 分类

XSS 分类

3. XSS 防护

3.1 方法1:浏览器自带防护 (X-XSS-Protection )

现今主流浏览器(Internet Explorer,Chrome 和 Safari)带有 HTTP X-XSS-Protection 响应头,当检测到跨站脚本攻击(XSS)时,浏览器将中止加载页面。

X-XSS-Protection 响应头有如下 4 个值:

  • X-XSS-Protection: 0

禁止XSS过滤。

  • X-XSS-Protection: 1

启用XSS过滤(一般浏览器是默认的)。 若是检测到跨站脚本攻击,浏览器将清除页面(删除不安全的部分)。

  • X-XSS-Protection: 1; mode=block

启用XSS过滤。 若是检测到攻击,浏览器将不会清除页面,而是阻止页面加载。

  • X-XSS-Protection: 1; report=<reporting-uri>

启用XSS过滤。 若是检测到跨站脚本攻击,浏览器将清除页面并使用CSP report-uri指令的功能发送违规报告。

注意:

这并不能彻底防止反射型 XSS,并且也并非全部浏览器都支持 X-XSS-Protection,存在兼容性问题。

它只对反射型 XSS 有必定的防护力,其原理也只是检查 URL 和 DOM 中元素的相关性。

3.2 方法2:转义

即将经常使用特殊字符进行转义,避免攻击者使用构造特殊字符来注入脚本。须要在客户端和服务端,都对用户输入的数据进行转义。

常见须要转义的特殊字符如 <>&"'

转义方法:

function escapeHTML(str) {
    if (!str) return '';
    str = str.replace(/&/g, "&amp;");
    str = str..replace(/</g, "&lt;");
    str = str..replace(/>/g, "&gt;");
    str = str..replace(/"/g, "&quot;");
    str = str..replace(/'/g, "&#39;");
    return str;
};
复制代码

3.3 方法3:过滤

常见于富文本内容,由于其须要保留 HTML,因此不能直接使用转义方法,而能够经过使用白名单,来容许特定的 HTML 标签及属性,来抵御 XSS 攻击。

3.4 方法4:内容安全策略(CSP)

内容安全策略(Content Security Policy,CSP),实质就是白名单制度,开发者明确告诉客户端,哪些外部资源能够加载和执行,大大加强了网页的安全性。

两种方法能够启用 CSP。

  1. 经过 HTTP 头信息的 Content-Security-Policy 的字段:
Content-Security-Policy: script-src 'self'; 
                         object-src 'none';
                         style-src cdn.example.org third-party.org; 
                         child-src https:
复制代码
  1. 经过网页的 <meta> 标签
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">
复制代码

上面代码中,CSP 作了以下配置:

  • 脚本: 只信任当前域名
  • <object>标签: 不信任任何 URL,即不加载任何资源
  • 样式表: 只信任 cdn.example.org third-party.org
  • 页面子内容,如 <frame><iframe>: 必须使用HTTPS协议加载
  • 其余资源: 没有限制
  • 启用后,不符合 CSP 的外部资源就会被阻止加载。

参考文章

  1. 《跨域资源共享 CORS 详解》
  2. 《CSRF & CORS》
  3. 《浅谈CSRF攻击方式》
  4. 《跨站脚本攻击—XSS》