x-note
  • Introduction
  • JavaScript
    • JavaScript 作用域链
    • JavaScript 数据结构与类型
    • JavaScript 原型
    • JavaScript this 关键字
    • JavaScript 函数
    • JavaScript delete 运算符
    • JavaScript 内存管理与垃圾回收
    • JavaScript 严格模式与混乱模式
    • JavaScript 数字精度丢失
    • JavaScript 并发模型
    • 利用原型链实现继承
  • ECMAScript
    • ECMAScript 6 变量及常量的声明
    • ECMAScript 6 变量的解构赋值
    • ECMAScript 6 Promise 对象
    • ECMAScript 6 Symbol
    • ECMAScript 6 Proxy
    • ECMAScript 6 Reflect
    • ECMAScript 6 new.target
    • ECMAScript 6 Set 和 WeakSet
    • ECMAScript 6 Map 和 WeakMap
    • ECMAScript 6 Iterator
    • ECMAScript 6 Generator
    • ECMAScript 6 class
    • ECMAScript 7
    • ECMAScript 8 async 函数
    • ECMAScript 8 内存共享与原子性
    • ECMAScript 8 Others
    • ECMAScript 2018
    • ECMAScript 2019
  • CSS
    • CSS 块格式化上下文(BFC)
    • CSS 盒模型
    • CSS 外边距合并
    • CSS Float
    • CSS Position
    • CSS Border-Image
    • CSS BEM
    • CSS 表布局详解
    • 页面布局之单列布局
    • 页面布局之多列布局
  • React
    • React 组件的生命周期
    • React 虚拟 DOM
    • React Reconciliation
    • React Diff 算法核心
    • React Fiber
    • React Scheduling
    • React Context API
    • React Refs
    • React HMR
    • React Hook
  • VUE
    • VUE 响应式系统
    • VUE 渲染机制
    • 关于 Vue 的思考
  • Webpack
    • Webpack 基本概念
    • Webpack HMR
  • Babel
    • @babel/preset-env
  • WEB
    • WEB 基础知识及概念
      • 屏幕测量单位
      • 重绘与重排
      • 前端模块化系统
      • WEB 客户端存储
      • 浏览器的渲染过程
    • WEB 性能优化
      • WEB 性能指标
      • WEB 图片优化
      • 懒加载资源
    • WEB 安全
      • XSS
      • XSRF
      • 点击劫持
      • 同源策略(Same Origin Policy,SOP)
    • WEB 解决方案
      • webp 兼容方案
      • WEB 拖拽实现方案
    • WEB SEO
  • Git
    • Git 工作流
    • Git 内部原理
  • 传输协议
    • UDP
      • UDP 基本概念
    • TCP
      • TCP 基本概念
    • HTTP
      • HTTP 基础
      • HTTP 缓存
      • HTTP-2
      • HTTP-3
      • HTTPS
      • 自定义 HTTPS 证书
  • Protocol Buffers
    • Protocol Buffers 基础
  • gRPC
    • gRPC 简介
    • gRPC 基础概念
    • GRPC with GraphQL and TypeScript
  • 正则表达式
    • 正则表达式基础
    • 正则表达式的悲观回溯
  • 基础算法
    • 冒泡排序
    • 插入排序
    • 选择排序
    • 快速排序
    • 归并排序
    • 希尔排序
    • 堆排序
    • 桶排序
    • 计数排序
    • 基数排序
    • 二叉树的遍历
    • 动态规划
    • 回溯
  • 压缩算法
    • HPACK
    • QPACK
  • 设计模式
    • DDD
      • 模型元素的模式
    • 常见设计模式
      • 工厂方法
      • 抽象工厂
      • 构造器
      • 原型
      • 单例模式
      • 适配器模式
      • 桥接模式
      • 组合模式
      • 外观模式
      • 享元模式
      • 代理模式
      • 责任链模式
      • 命令模式
      • 迭代器模式
      • 中介者模式
      • 备忘录模式
      • 观察者模式
      • 状态模式
      • 策略模式
      • 模版方法模式
      • 访问者模式
      • 依赖注入
    • MVC
    • MVP
    • MVVM
  • 颜色空间
    • LCH
由 GitBook 提供支持
在本页
  • 为什么需要同源策略
  • 源
  • Same Origin Policy 的限制
  • 解决方案
  • CORS
  • 反向代理
  • JSONP
  • iframe
  • postMessage
  • 参考
在GitHub上编辑
  1. WEB
  2. WEB 安全

同源策略(Same Origin Policy,SOP)

上一页点击劫持下一页WEB 解决方案

最后更新于1年前

同源策略是指在 Web 浏览器中,允许某个网页脚本访问另一个网页的数据,但前提是这两个网页必须有相同的 URI、主机名和端口号,一旦两个网站满足上述条件,这两个网站就被认定为具有相同来源。

为什么需要同源策略

同源策略可防止某个网页上的恶意脚本通过该页面的文档对象模型访问另一网页上的敏感数据。 同源策略对 Web 应用程序具有特殊意义,因为 Web 应用程序广泛依赖于 HTTP cookie 来维持用户会话,所以必须将不相关网站严格分隔,以防止丢失数据泄露。

而且浏览器请求常常默认携带 cookie 返回给 Server。

假设: A 网站是一家银行,用户登录以后,又去浏览其他网站。 如果其他网站可以读取 A 网站的 Cookie,那么 Cookie 包含的隐私(比如存款总额)就会泄漏。

Cookie 往往用来保存用户的登录状态,如果用户没有退出登录(用户往往不清楚自己是否退出登录),其他网站就可以冒充用户,为所欲为。 因为浏览器同时还规定,提交表单不受同源政策的限制。

Same Origin Policy 可以有效的避免

源

如果两个 URL 的 protocol、port (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。

判断跟 https://example.com/path1 同源

URL
Result

https://example.com/path2

same origin

https://example.com/path1/a

same origin

http://example.com/path1

fail by protocol difference

https://example.com:444/path1

fail by port difference

https://www.example.com/path1

fail by host differnece

根据浏览器的实现 https://example.com:443/a 和 https://example.com/a 有可能不同源

Same Origin Policy 的限制

同源策略控制不同源之间的交互,例如在使用 XMLHttpRequest 或 <img> 标签时则会受到同源策略的约束。这些交互通常分为三类:

  • 跨域写操作(Cross-origin writes)一般是被允许的。例如链接(links),重定向以及表单提交。特定少数的 HTTP 请求需要添加 preflight。

  • 跨域资源嵌入(Cross-origin embedding)一般是被允许(后面会举例说明)。

  • 跨域读操作(Cross-origin reads)一般是不被允许的,但常可以通过内嵌资源来巧妙的进行读取访问。 例如,你可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法,或 availability of an embedded resource.

以下是可能嵌入跨源的资源的一些示例:

  • <script src="..."></script> 标签嵌入跨域脚本。语法错误信息只能被同源脚本中捕捉到。

  • <link rel="stylesheet" href="..."> 标签嵌入 CSS。由于 CSS 的松散的语法规则,CSS 的跨域需要一个设置正确的 HTTP 头部 Content-Type。 不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (CVE-2010-0051) 和 Opera。

  • 通过 <img> 展示的图片。支持的图片格式包括 PNG,JPEG,GIF,BMP,SVG,...

  • 通过 <video> 和 <audio> 播放的多媒体资源。

  • 通过 <object>、 <embed> 和 <applet> 嵌入的插件。

  • 通过 @font-face 引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。

  • 通过 <iframe> 载入的任何资源。站点可以使用 X-Frame-Options 消息头来阻止这种形式的跨域交互。

解决方案

解决 SOP 的常见思路

  1. 跨域资源共享(CORS)

  2. 反向代理

  3. JSONP

  4. iframe

  5. postMessage

CORS

CORS(Cross-origin resource sharing,跨域资源共享)CORS 是 HTTP 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。

  • Access-Control-Allow-Credentials

  • Access-Control-Allow-Headers

  • Access-Control-Allow-Methods

  • Access-Control-Allow-Origin

  • Access-Control-Expose-Headers

  • Access-Control-Max-Age

  • Access-Control-Request-Headers

  • Access-Control-Request-Method

Access-Control-Allow-Origin 响应头指定了该响应的资源是否被允许与给定的 origin 共享。

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

假设 https://example.com/path1 认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源信息,如:

Access-Control-Allow-Origin: https://www.example.com, http://example.com
Vary: Origin

此时 https://www.example.com, http://example.com 能够正常通过 XMLHttpRequest 或 fetch 请求 https://example.com。

如果服务器未使用 *,而是指定了一个域,那么为了向客户端表明服务器的返回会根据 Origin 请求头而有所不同,必须在 Vary 响应头中包含 Origin。

CORS 的缺点

服务器端需要维护一份 orign list 对于 DEV 不够友好。

反向代理

正向代理隐藏真实客户端,反向代理隐藏真实服务端。

通过搭建一个反向代理服务器,使得文档资源与真实服务之间处于同一域之下。

常见的 Nginx 和 Caddy 支持反向代理。Webpack Dev Server 也支持反响代理。

如下面一个 Nginx 代理服务器配置

#proxy 服务器
server {
    listen       81;
    server_name  www.example1.com;

    location / {
        proxy_pass   http://www.example2.com:8080;  #反向代理
        proxy_cookie_domain www.example2.com www.example1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用 webpack-dev-server 等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.example1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

反向代理 的缺陷

代理服务器带来了额外的成本

  • 维护代理服务器需要额外的成本

  • 每一次的请求都需要经过代理服务器

必须为每一种应该用卡法一个反向代理服务器。

针对每一次代理,代理服务器就必须打开两个连接,对于高并发会成为瓶颈

JSONP

JSONP 利用 <script /> 未限制第三方请求和回调的方式实现跨域请求。

<!-- https://example.com/index.html -->
<script>
  function showUserInfo(userInfo) {
    // do something
  }
</script>
<script src="https://www.example.com/api/userInfo?userId=1&callback=showUserInfo"></script>
// in express server
res.send(`${req.query[callback]}(${JSON.stringfy({ name: 'alex', age: 18 })})`);

JSONP 的安全问题

JSON 劫持

JSON(JSON Hijacking),最开始提出这个概念大概是在 2008 年国外有安全研究人员提到这个 JSONP 带来的风险。 这个问题属于 CSRF( Cross-site request forgery 跨站请求伪造)攻击范畴。

<script>
  function wooyun(v) {
    alert(v.username);
  }
</script>
<script src="http://js.login.360.cn/?o=sso&m=info&func=wooyun"></script>

当被攻击者在登陆 360 网站的情况下访问了该网页时,那么用户的隐私数据可能被攻击者劫持。

防御手段

  • 验证 JSON 调用的来源( Referer )

  • token 防御

验证 JSON 调用的来源( Referer )。 这个方案是主要利用了 <script> 远程加载 JSON 文件时会发送 Referer ,在网站输出 JSON 数据时判断 Referer 是不是白名单就可以进行防御.

但需要注意 Referer 过滤不严谨以及空 Referer 的问题

<script src="http://www.attack.com/attack.htm?360.cn"></script>

在很多情况下,开发者在部署过滤 Referer 来源时,忽视了一个空 Referer 的过滤。 一般情况下浏览器直接访问某 URL 是不带 Referer 的,所以很多防御部署是允许空 Referer 的。

使用 <iframe> 调用 javscript 伪协议来实现空 Referer 调用 JSON 文件。

<iframe
  src="javascript:'<script>function JSON(o){alert(o.userinfo.userid);}</script><script src=http://www.qq.com/login.php?calback=JSON></script>'"
></iframe>

通过随机 token 来防御,如:http://r.qzone.qq.com/cgi-bin/tfriend/friend_show_qqfriends.cgi?uin=[QQ号码]&g_tk=[随机token] 来输出 JSON ,同样这个方案也是效的,但是同样可能出现防御实现的不严谨问题。

如这个 token 可以暴力

Callback 可自定义导致的安全问题

在输出 JSON 时,没有严格定义好 Content-Type( Content-Type: application/json ) 而且没有镦 callback 这个输出点没有进行过滤

直接在浏览器输入以下 URL

https://www.example.com/api/userInfo?callback=<script>alert(/xss/)</script>
https://www.example.com/api/userInfo/x.html?callback=<script>alert(/xss/)</script>

JSONP 防御

  1. 严格安全的实现 CSRF 方式调用 JSON 文件:限制 Referer 、部署一次性 Token 等。

  2. 严格安装 JSON 格式标准输出 Content-Type 及编码( Content-Type : application/json; charset=utf-8 )。

  3. 严格过滤 callback 函数名及 JSON 里数据的输出。

  4. 严格限制对 JSONP 输出 callback 函数名的长度(如防御上面 flash 输出的方法)。

  5. 其他一些比较“猥琐”的方法:如在 Callback 输出之前加入其他字符(如:/**/、回车换行)这样不影响 JSON 文件加载,又能一定程度预防其他文件格式的输出。 还比如 Gmail 早起使用 AJAX 的方式获取 JSON ,听过在输出 JSON 之前加入 while(1); 这样的代码来防止 JS 远程调用

iframe

满足某些限制条件的情况下,页面是可以修改它的源。脚本可以将 document.domain 的值设置为其当前域或其当前域的父域。 如果将其设置为其当前域的父域,则这个较短的父域将用于后续源检查。

document.domain + iframe 跨域

此方案仅限主域相同,子域不同的跨域应用场景。

实现原理:两个页面都通过 js 强制设置 document.domain 为基础主域,就实现了同域。

<!-- http://child.example.com/a.html -->
<iframe id="iframe" src="http://child.example.com/b.html"></iframe>
<script>
  document.domain = 'example.com';
</script>
<!-- http://child.example.com/b.html -->
<script>
  document.domain = 'example.com';
  // document.domain = window.parent.document.domain;
</script>

iframe 的缺陷

iframe 已被 HTML5 废弃。

HTML5 基于以下原因废弃 iframe:

  1. iframe 会阻塞主页面的 onload 事件

  2. iframe 和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。

如果 iframe 中的域名因为过期而被恶意攻击者抢注,或者第三方被黑客攻破,iframe 中的内容被替换掉了,从而利用用户浏览器中的安全漏洞下载安装木马、恶意勒索软件等等????

postMessage

postMessage 是 HTML5 XMLHttpRequest Level 2 中的 API,且是为数不多可以跨域操作的 window 属性之一,它可用于解决以下方面的问题:

  1. 页面和其打开的新窗口的数据传递

  2. 多窗口之间消息传递

  3. 页面与嵌套的 iframe 消息传递

<!-- http://www.example1.com/path -->
<iframe src="http://www.example2.com/path"></iframe>
<script>
  const iframe = document.getElementById('iframe');
  iframe.onload = function () {
    const data = {
      name: 'aym',
    };
    // 向domain2传送跨域数据
    iframe.contentWindow.postMessage(
      JSON.stringify(data),
      'http://www.example2.com',
    );
  };

  // 接受domain2返回数据
  window.addEventListener('message', function (e) {
    console.log('data from example2 ---> ' + e.data);
  });
</script>
<!-- parent.html -->
<script>
  window.addEventListener(
    'message',
    function (e) {
      console.loga('data from example1 ---> ' + e.data);
      var data = JSON.parse(e.data);
      window.parent.postMessage(
        JSON.stringify(data),
        'http://www.example1.com',
      );
    },
    false,
  );
</script>

参考

CSRF(XSRF)
Same-origin policy
浏览器同源政策及其规避方法
JSONP 跨域资源共享的安全问题