跨域

浏览器安全

浏览器安全可以分为三大块--Web页面安全、浏览器网络安全和浏览器系统安全

本次主要介绍分析页面中的安全策略。在开始之前,我们先来做个假设,如果页面中没有安全策略的话,Web世界会是什么样的呢?

  • Web 世界会是开放的,任何资源都可以接入其中,我们的网站可以加载并执行别人网站的脚本文件、图片、音频 / 视频等资源,甚至可以下载其他站点的可执行文件。
  • Web 世界是开放的,这很符合 Web 理念。但如果 Web 世界是绝对自由的,那么页面行为将没有任何限制,这会造成无序或者混沌的局面,出现很多不可控的问题。

比如你打开了一个银行站点,然后又一不小心打开了一个恶意站点,如果没有安全措施,恶意站点就可以做很多事情:

  • 修改银行站点的 DOM、CSSOM 等信息;
  • 在银行站点内部插入 JavaScript 脚本;
  • 劫持用户登录的用户名和密码;
  • 读取银行站点的 Cookie、IndexDB 等数据;
  • 甚至还可以将这些信息上传至自己的服务器,这样就可以在你不知情的情况下伪造一些转账请求等信息。

所以说,在没有安全保障的 Web 世界中,我们是没有隐私的,因此需要安全策略来保障我们的隐私和数据的安全。这也就是同源策略诞生的原因。

同源策略

Q: 什么是同源?
A: 如果两个 URL 的协议、域名和端口都相同,我们就称这两个 URL 同源。比如下面这两个 URL,它们具有相同的协议 HTTPS、相同的域名 time.geekbang.org,以及相同的端口 443,所以我们就说这两个 URL 是同源的。

https://time.geekbang.org/?category=1
https://time.geekbang.org/?category=0

Q: 什么是同源策略?
A: 浏览器默认两个相同的源之间是可以相互访问资源和操作 DOM 的。两个不同的源之间若想要相互访问资源或者操作 DOM,那么会有一套基础的安全策略的制约,我们把这称为同源策略。

具体来讲,同源策略主要表现在 DOM、Web 数据和网络这三个层面。

第一个,DOM 层面。同源策略限制了来自不同源的 JavaScript 脚本对当前 DOM 对象读和写的操作。

{
let dom = opener.document
dom.body.style.display = "none"
}

第二个,数据层面。同源策略限制了不同源的站点读取当前站点的 Cookie、IndexDB、LocalStorage 等数据。由于同源策略,我们依然无法通过第二个页面的 opener 来访问第一个页面中的 Cookie、IndexDB 或者 LocalStorage 等内容。

opener.document.cookie

第三个,网络层面。同源策略限制了通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点。

var express = require("express");
var app = express();
app.use();

app.get("/hello", function (req, res) {
  res.json(hello).end();
});

app.listen(3000, function () {
  console.log("Example app listening on port 3000!");
});
function handleHi() {
    axios.get("http://localhost:3000/hi").then((res) => {
      console.log("====================================");
      console.log(res);
      console.log("====================================");
    });
  }

基于同源策略,采取的方式

为了使项目能够得以开发和使用,我们可以采用以下方法:

  • 页面中可以嵌入第三方资源
    弊端:极易引发XSS攻击以及CSRF攻击
    解决方法:在浏览器中引入内容安全策略,称为 CSP。CSP 的核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览器是否能够执行内联 JavaScript 代码。通过这些手段就可以大大减少 XSS 攻击。

  • 引入跨域资源共享策略(CORS)
    使用 XMLHttpRequest 和 Fetch 都是无法直接进行跨域请求的,因此浏览器又在这种严格策略的基础之上引入了跨域资源共享策略,让其可以安全地进行跨域操作

  • 在浏览器中实现跨文档消息机制
    可以通过 window.postMessage 的 JavaScript 接口来和不同源的 DOM 进行通信

跨域资源共享(CORS)

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求

简单请求
某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”,请注意,该术语并不属于 Fetch (其中定义了 CORS)规范。若请求满足所有下述条件,则该请求可视为“简单请求”:

  • 使用下列方法之一:
    GET
    HEAD
    POST

  • Fetch 规范定义了对 CORS 安全的首部字段集合,不得人为设置该集合之外的其他首部字段。该集合为:
    Accept
    Accept-Language
    Content-Language
    Content-Type (需要注意额外的限制)
    DPR
    Downlink
    Save-Data
    Viewport-Width
    Width

  • Content-Type 的值仅限于下列三者之一:
    text/plain
    multipart/form-data
    application/x-www-form-urlencoded

  • 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

  • 请求中没有使用 ReadableStream 对象。

预检请求
与前述简单请求不同,“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求(其中包括:当前网页所在的域名是否在服务器的许可名单之中(Access-Control-Allow-Origin)以及可以使用哪些HTTP动词和头信息字段(Access-Control-Allow-Methods、Access-Control-Allow-Headers))。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

  • 使用了下面任一 HTTP 方法:
    PUT
    DELETE
    CONNECT
    OPTIONS
    TRACE
    PATCH

  • 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:
    Accept
    Accept-Language
    Content-Language
    Content-Type (需要注意额外的限制)
    DPR
    Downlink
    Save-Data
    Viewport-Width
    Width

  • Content-Type 的值不属于下列之一:
    application/x-www-form-urlencoded
    multipart/form-data
    text/plain

  • 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。

  • 请求中使用了ReadableStream对象。

预检请求完成之后,发送实际请求

注:CORS 最初要求该行为,不过在后续的修订中废弃了这一要求。

HTTP 响应首部字段

1、Access-Control-Allow-Origin

Access-Control-Allow-Origin: <origin> | *

其中,origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。

2、Access-Control-Expose-Headers
Access-Control-Expose-Headers 头让服务器把允许浏览器访问的头放入白名单,因为在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

3、Access-Control-Max-Age
Access-Control-Max-Age 头指定了preflight请求的结果能够被缓存多久

Access-Control-Max-Age: <delta-seconds>

4、Access-Control-Allow-Credentials
Access-Control-Allow-Credentials 响应头表示是否可以将对请求的响应暴露给页面。返回true则可以,其他值均不可以。
Credentials可以是 cookies, authorization headers 或 TLS client certificates。

Access-Control-Allow-Credentials: true

虽可以用在对preflight预检测请求的响应中,但由于预检请求被废弃了,所以暂不讨论

5、Access-Control-Allow-Methods
Access-Control-Allow-Methods 首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。

Access-Control-Allow-Methods: <method>[, <method>]*

6、Access-Control-Allow-Headers
Access-Control-Allow-Headers 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段。

Access-Control-Allow-Headers: <field-name>[, <field-name>]*

HTTP 请求首部字段

1、Origin
Origin 首部字段表明预检请求或实际请求的源站。

Origin: <origin>

2、Access-Control-Request-Method
Access-Control-Request-Method 首部字段用于预检请求。其作用是,将实际请求所使用的 HTTP 方法告诉服务器。

Access-Control-Request-Method: <method>

3、Access-Control-Request-Headers
Access-Control-Request-Headers 首部字段用于预检请求。其作用是,将实际请求所携带的首部字段告诉服务器。

Access-Control-Request-Headers: <field-name>[, <field-name>]*

JOSNP

利用了使用src引用静态资源时不受跨域限制的机制。主要在客户端搞一个回调做一些参数接收与操作的处理,并把这个回调函数告知服务器,而服务器端需要做的是按照JavaScript的语法把数据放到约定好的回调函数之中即可
它只支持get以及简单请求,兼容性好,相对来说不是很安全,因为它不是跨域规范,callback参数注入和资源访问授权设置未做限制。

参考:浏览器工作原理与实践-同源策略
HTTP访问控制(CORS)
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Server-Side_Access_Control

posted @ 2020-05-06 21:28  小蜗蜗蜗牛^o^  阅读(190)  评论(0编辑  收藏  举报