由“Content-Security-Policy“头缺失引起的总结

概述

最近,使用APPSCAN扫描系统时,扫描出存在"Content-Security-Policy"头缺失漏洞。

1. 同源策略是什么

协议相同
域名相同
端口相同

同源策略可分为以下两种情况:
1、DOM 同源策略:禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
2、XMLHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。
同源策略的目的

因为存在浏览器同源策略,所以才会有跨域问题。那么浏览器是出于何种原因会有跨域的限制呢。其实不难想到,跨域限制主要的目的就是为了用户的上网安全。

如果浏览器没有同源策略,会存在什么样的安全问题呢。下面从 DOM 同源策略和 XMLHttpRequest 同源策略来举例说明:

  • 如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:
    1、做一个假网站,里面用 iframe 嵌套一个银行网站 http://mybank.com。
    2、把 iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
    3、这时如果用户输入账号密码,我们的主网站可以跨域访问到 http://mybank.com 的 dom 节点,就可以拿到用户的账户密码了。

  • 如果没有 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:
    1、用户登录了自己的银行页面 http://mybank.com,http://mybank.com 向用户的 cookie 中添加用户标识。
    2、用户浏览了恶意页面 http://evil.com,执行了页面中的恶意 AJAX 请求代码。
    3、http://evil.com 向 http://mybank.com 发起 AJAX HTTP 请求,请求会默认把 http://mybank.com 对应 cookie 也同时发送过去。
    4、银行页面从发送的 cookie 中提取用户标识,验证用户无误,response 中返回请求数据。此时数据就泄露了。
    5、而且由于 Ajax 在后台执行,用户无法感知这一过程。

2.跨域解决方法

一、请求跨域

CORS
JSONP
WebSocket
代理转发

二、页面跨域

postMessage
document.domain
window.name
location.hash

请求跨域解决方法

  • CORS
优点:

1、CORS 通信与同源的 AJAX 通信没有差别,代码完全一样,容易维护。
2、支持所有类型的 HTTP 请求。

缺点:

1、存在兼容性问题,特别是 IE10 以下的浏览器。
2、第一次发送非简单请求时会多一次请求。

  • JSONP 实现跨域
    由于 script 标签不受浏览器同源策略的影响,允许跨域引用资源。因此可以通过动态创建 script 标签,然后利用 src 属性进行跨域,这也就是 JSONP 跨域的基本原理。

直接通过下面的例子来说明 JSONP 实现跨域的流程:

// 1. 定义一个 回调函数 handleResponse 用来接收返回的数据
function handleResponse(data) {
    console.log(data);
};

// 2. 动态创建一个 script 标签,并且告诉后端回调函数名叫 handleResponse
var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.src = 'http://www.laixiangran.cn/json?callback=handleResponse';
body.appendChild(script);

// 3. 通过 script.src 请求 `http://www.laixiangran.cn/json?callback=handleResponse`,
// 4. 后端能够识别这样的 URL 格式并处理该请求,然后返回 handleResponse({"name": "laixiangran"}) 给浏览器
// 5. 浏览器在接收到 handleResponse({"name": "laixiangran"}) 之后立即执行 ,也就是执行 handleResponse 方法,获得后端返回的数据,这样就完成一次跨域请求了。
优点

使用简便,没有兼容性问题,目前最流行的一种跨域方法。

缺点

只支持 GET 请求。
由于是从其它域中加载代码执行,因此如果其他域不安全,很可能会在响应中夹带一些恶意代码。
要确定 JSONP 请求是否失败并不容易。虽然 HTML5 给 script 标签新增了一个 onerror 事件处理程序,但是存在兼容性问题。

  • WebSocket跨域

Websocket 是 HTML5 规范提出的一个应用层的全双工协议,适用于浏览器与服务器进行实时通信场景。

全双工通信传输的一个术语,这里的“工”指的是通信方向。

“双工”是指从客户端到服务端,以及从服务端到客户端两个方向都可以通信,“全”指的是通信双方可以同时向对方发送数据。与之相对应的还有半双工和单工,半双工指的是双方可以互相向对方发送数据,但双方不能同时发送,单工则指的是数据只能从一方发送到另一方。

下面是一段简单的示例代码。在 a 网站直接创建一个 WebSocket 连接,连接到 b 网站即可,然后调用 WebScoket 实例 ws 的 send() 函数向服务端发送消息,监听实例 ws 的 onmessage 事件得到响应内容。

let ws = new WebSocket("ws://b.com");
ws.onopen = function(){
  // ws.send(...);
}
ws.onmessage = function(e){
  // console.log(e.data);
}
  • 请求代理

我们知道浏览器有同源策略的安全限制,但是服务器没有限制,所以我们可以利用服务器进行请求转发。

以 webpack 为例,利用 webpack-dev-server 配置代理, 当浏览器发起前缀为 /api 的请求时都会被转发到 http://localhost:3000 服务器,代理服务器将获取到响应返回给浏览器。对于浏览器而言还是请求当前网站,但实际上已经被服务端转发。

// webpack.config.js
module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': 'http://localhost:3000'
    }
  }
};

// 使用 Nginx 作为代理服务器
location /api {
    proxy_pass   http://localhost:3000;
}

页面跨域解决方法

请求跨域之外,页面之间也会有跨域需求,例如使用 iframe 时父子页面之间进行通信。常用方案如下:

postMessage
document.domain
window.name(不常用)
location.hash + iframe(不常用)

  • postMessage

window.postMessage(message,targetOrigin) 方法是 HTML5 新引进的特性,可以使用它来向其它的 window 对象发送消息,无论这个 window 对象是属于同源或不同源。这个应该就是以后解决 dom 跨域通用方法了。
调用 postMessage 方法的 window 对象是指要接收消息的那一个 window 对象,该方法的第一个参数 message 为要发送的消息,类型只能为字符串;第二个参数 targetOrigin 用来限定接收消息的那个 window 对象所在的域,如果不想限定域,可以使用通配符 *。
需要接收消息的 window 对象,可是通过监听自身的 message 事件来获取传过来的消息,消息内容储存在该事件对象的 data 属性中。

页面 http://www.laixiangran.cn/a.html 的代码: