CORS 和 CSRF 太容易混淆了,看完本文,你就清楚了。php
先看下图:html
二者概念彻底不一样,另外经常咱们也会看到 XSS ,这里一块儿介绍:前端
跨来源资源共享(CORS),亦译为跨域资源共享,是一份浏览器技术的规范,提供了 Web 服务从不一样网域传来沙盒脚本的方法,以避开浏览器的同源策略,是 JSONP 模式的现代版。与 JSONP 不一样,CORS 除了 GET 请求方法之外也支持其余的 HTTP 请求。用 CORS 可让网页设计师用通常的 XMLHttpRequest,这种方式的错误处理比 JSONP 要来的好。另外一方面,JSONP 能够在不支持 CORS 的老旧浏览器上运做。现代的浏览器都支持 CORS。
—— 维基百科
核心知识: CORS是一个W3C标准,它容许浏览器向跨源服务器,发出XMLHttpRequest
请求,从而克服 AJAX 只能同源使用的限制。git
所以,实现 CORS 通讯的关键是服务器。只要服务器实现了 CORS 接口,就能够跨源通讯,即为了解决跨域问题。github
浏览器将 CORS 请求分红两类:简单请求(simple request)和非简单请求(not-so-simple request)。json
简单请求通常包括下面两种状况:segmentfault
状况 | 描述 |
---|---|
请求方法 | 请求方法为:HEAD 或 GET 或 POST ; |
HTTP 头信息 | HTTP 头信息不超出如下几种字段:Accept <br/>Accept-Language <br/>Content-Language <br/>Last-Event-ID <br/>Content-Type :只限于三个值 application/x-www-form-urlencoded 、multipart/form-data 、text/plain |
凡是不一样时知足上面两个条件,就属于非简单请求。api
当浏览器发现咱们的 AJAX 请求是个简单请求,便会自动在头信息中,增长一个 Origin
字段。跨域
Origin
字段用来讲明本次请求的来源(包括协议 + 域名 + 端口号),服务端根据这个值来决定是否赞成这次请求。浏览器
当 Origin
指定的源不在许可范围,服务器会返回一个正常的 HTTP 回应,但浏览器会在响应头中发现 Access-Control-Allow-Origin
字段,便抛出异常。
当 Origin
指定的源在许可范围,服务器返回的响应头中会多出几个头信息字段:
除了上面图中的头信息,通常会有如下三个相关头信息:
Access-Control-Allow-Origin
该字段是必须的。表示许可范围的域名,一般有两种值:请求时 Origin 字段的值或者 *
(星号)表示任意域名。
Access-Control-Allow-Credentials
该字段可选。布尔值,表示是否容许在 CORS 请求之中发送 Cookie
。若不携带 Cookie
则不须要设置该字段。
当设置为 true
则 Cookie
包含在请求中,一块儿发送给服务器。还须要在 AJAX 请求中开启 withCredentials
属性,不然浏览器也不会发送 Cookie
。
let xhr = new XMLHttpRequest(); xhr.withCredentials = true;
注意: 若是前端设置 Access-Control-Allow-Credentials
为 true
来携带 Cookie 发起请求,则服务端 Access-Control-Allow-Origin
不能设置为 *
。
Access-Control-Expose-Headers
该字段可选。能够设置须要获取的字段。由于默认 CORS 请求时,XMLHttpRequest
对象的getResponseHeader()
方法只能拿到如下 6 个基本字段:
Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。
非简单请求状况如:请求方法是 PUT / DELETE 或者 Content-Type:application/json
类型的请求。
在非简单请求发出 CORS 请求时,会在正式通讯以前增长一次 “预检”请求(OPTIONS方法),来询问服务器,本次请求的域名是否在许可名单中,以及使用哪些头信息。
当 “预检”请求 经过之后,才会正式发起 AJAX 请求,不然报错。
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... ...
“预检”请求 信息中包含两个特殊字段:
Access-Control-Request-Method
该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法,上例是 PUT
。
Access-Control-Request-Headers
指定浏览器 CORS 请求额外发送的头信息字段,上例是 X-Custom-Header
。
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-
开头的信息,并在控制台输出错误信息。
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,一般缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登陆的Web应用程序上执行非本意的操做的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
—— 维基百科
核心知识: 跨站点请求伪造请求。
简单理解: 攻击者盗用你的身份,以你的名义发送恶意请求。
常见场景:以你名义发送邮件,发消息,盗取你的帐号,甚至于购买商品,虚拟货币转帐等等。
形成影响:我的隐私泄露以及财产安全。
上面描述了 CSRF 攻击的流程,其中受害者完成两个步骤:
能够理解为:若以上两个步骤没有都完成,则不会受到 CSRF 攻击。
服务端防护的方式有不少,思想相似,都是在客户端页面增长伪随机数。
最简单有效方式,由于攻击者理论上没法获取第三方的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%的杜绝,这个不是最好的方法。
思路是:每次用户提交都须要用户在表单中填写一个图片上的随机字符串,这个方案能够彻底解决CSRF,但易用性差,而且验证码图片的使用涉及 MHTML 的Bug,可能在某些版本的微软IE中受影响。
须要注意“并行会话的兼容”。若是用户在一个站点上同时打开了两个不一样的表单,CSRF保护措施不该该影响到他对任何表单的提交。考虑一下若是每次表单被装入时站点生成一个伪随机值来覆盖之前的伪随机值将会发生什么状况:用户只能成功地提交他最后打开的表单,由于全部其余的表单都含有非法的伪随机值。必须当心操做以确保CSRF保护措施不会影响选项卡式的浏览或者利用多个浏览器窗口浏览一个站点。
php 实现以下:
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{ } } ?>
<?php function gen_input() { gen_stoken(); echo "<input type=\"hidden\" name=\"" . FTOKEN_NAME . "\" value=\"" . $_SESSION[STOKEN_NAME] . "\"> "; } ?>
<?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>
这一步很简单,不作详细介绍。
注意: 本文简单介绍 XSS 知识,具体详细能够阅读 FEWY 写的 《跨站脚本攻击—XSS》https://segmentfault.com/a/11...。
跨站脚本(英语:Cross-site scripting,一般简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它容许恶意用户将代码注入到网页上,其余用户在观看网页时就会受到影响。这类攻击一般包含了HTML以及用户端脚本语言。
—— 维基百科
XSS 攻击,通常是指攻击者经过在网页中注入恶意脚本,当用户浏览网页时,恶意脚本执行,控制用户浏览器行为的一种攻击方式。
常见 XSS 危害有:
现今主流浏览器(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 中元素的相关性。
即将经常使用特殊字符进行转义,避免攻击者使用构造特殊字符来注入脚本。须要在客户端和服务端,都对用户输入的数据进行转义。
常见须要转义的特殊字符如 <
,>
,&
,"
,'
。
转义方法:
function escapeHTML(str) { if (!str) return ''; str = str.replace(/&/g, "&"); str = str..replace(/</g, "<"); str = str..replace(/>/g, ">"); str = str..replace(/"/g, """); str = str..replace(/'/g, "'"); return str; };
常见于富文本内容,由于其须要保留 HTML,因此不能直接使用转义方法,而能够经过使用白名单,来容许特定的 HTML 标签及属性,来抵御 XSS 攻击。
内容安全策略(Content Security Policy,CSP),实质就是白名单制度,开发者明确告诉客户端,哪些外部资源能够加载和执行,大大加强了网页的安全性。
两种方法能够启用 CSP。
Content-Security-Policy
的字段:Content-Security-Policy: script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:
<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协议加载本文首发在 pingan8787我的博客,如需转载请联系本人。
Author | 王平安 |
---|---|
pingan8787@qq.com | |
博 客 | www.pingan8787.com |
微 信 | pingan8787 |
每日文章推荐 | https://github.com/pingan8787... |
ES小册 | js.pingan8787.com |