同源策略及其绕过详解

同源策略及其绕过详解
前言

最近在看吴翰清写的 白帽子讲 web安全 一书,遇到同源策略一词,我便开始深入学习相关内容,查阅了许多资料后,写该博客梳理、记录所学知识


基础知识
Origin(源)
  • Origin: < scheme > “😕/” <host> [ “:” < port > ],origin由用于访问它的URL的scheme(协议)、port(端口)、host(IP或域名)定义
  • scheme:请求所使用的协议,通常是HTTP协议或者它的安全版本HTTPS协议;host:服务器的域名或 IP 地址;port:服务器正在监听的TCP 端口号,可选
DOM

DOM(Document Object Model,文档对象模型)将 web 页面与脚本或编程语言连接起来。DOM模型用一个逻辑树来表示一个文档,树的每个分支的终点都是一个节点(node),每个节点都包含着对象(objects)。DOM的方法(methods)让你可以用特定方式操作这个树,用这些方法你可以改变文档的结构、样式或者内容。(简单来说通过 DOM,JavaScript 能够访问和改变 HTML 文档的所有元素)节点可以关联上事件处理器,一旦某一事件被触发了,那些事件处理器就会被执行。具体参考文章

BOM

BOM(Browser Object Model,浏览器对象模型)允许 JavaScript 与浏览器对话

XMLHttpRequest

XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据,这允许网页在不影响用户操作的情况下,更新页面的局部内容

Ajax

AJAX(Asynchronous JavaScript And XML )是一种使用 XMLHttpRequest 技术构建更复杂,动态的网页的编程实践,简单地说就是使用 XMLHttpRequest 对象与服务器通信异步特性:可以在不重新刷新页面的情况下与服务器通信,交换数据,或更新页面;具体理解参考网站

cookie

cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上

localStorage
  • localStorage是JavaScript中window对象的一个属性,只读属性
  • localStorage用于在浏览器中存储key/value对的数据,可以长久保存网站的数据,无过期时间,直到手动删去,具体语法参考文档
IndexedDB
  • 现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过4KB,且每次请求都会发送回服务器;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景

  • IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(包括文件/二进制大型对象),它使用索引实现对数据的高性能搜索;通俗地说,IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作


SOP(同源策略)
同源
  • 定义:若两个URL的 协议、端口、IP或域名 均相同,则这两个URL同源

  • 下表给出了与 URL http://store.company.com/dir/page.html的源进行对比的示例:

    image-20211205114217644

同源策略
  • 同源策略(Same Origin Policy,SOP)是由Netscape提出的一个著名的安全策略,它限制一个源(origin)加载的文档或脚本如何与来自另一个源的资源交互同源策略主要应用于从脚本访问数据,通过相应的HTML标记嵌入跨源的资源,如图像、CSS和脚本不受限制

  • 同源策略(安全性)主要表现在 DOM、web 数据 和 网络 这三个层面

    • DOM层面,同源策略限制了来⾃不同源的 JavaScript 脚本对当前 DOM 对象读和写的操作
    • 数据层面,同源策略限制了不同源的站点读取当前站点的Cookie、IndexDB、LocalStorage数据
    • 网络层面, 同源策略限制了通过 XMLHttpRequest(Ajax请求) 等方式将站点的数据发送给不同源的站点
跨域–>同源策略的绕过
CORS(Cross-Origin Resource Sharing)
  • CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种基于HTTP头的机制,其中的HTTP头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应,该机制允许 Web 应用服务器进行跨源访问控制,从而使跨源数据传输得以安全进行

  • CORS还通过浏览器(OPTIONS方法)发起一个到服务器托管的跨源资源的**"预检"请求**,在预检中,浏览器发送的头中标示有HTTP方法和真实请求中会用到的头

  • 简单请求与预检请求

    • 简单请求:一些CORS请求不会触发预检请求,这样的请求称为简单请求

      • 若满足下述所有条件,则该请求可视为简单请求

        • 请求方法是GET、HEAD、POST三者之一
        • 允许人为设置的CORS 安全的首部字段集为:Accept、Accept-Language、Content-Language、Content-Type(有限制)
        • Content-Type的值仅限于 text/plain 、multipart/form-data 、application/x-www-form-urlencoded 三者之一
        • 请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问
        • 请求中没有使用 ReadableStream 对象
      • 示例

        • 假如站点 http://foo.example 的网页应用想要访问 http://bar.other 的资源http://foo.example 的网页中可能包含类似于下面的 JavaScript 代码

          var invocation = new XMLHttpRequest();
          var url = 'http://bar.other/resources/public-data/';
          
          function callOtherDomain() {
            if(invocation) {
              invocation.open('GET', url, true);
              invocation.onreadystatechange = handler;
              invocation.send();
            }
          }
          
        • 请求报文与响应报文

          请求报文  
          GET /resources/public-data/ HTTP/1.1
          Host: bar.other
          User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
          Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
          Accept-Language: en-us,en;q=0.5
          Accept-Encoding: gzip,deflate
          Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
          Connection: keep-alive
          Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
          Origin: http://foo.example   //origin表明该请求来源于 http://foo.example
          
          服务器响应
          HTTP/1.1 200 OK
          Date: Mon, 01 Dec 2008 00:23:53 GMT
          Server: Apache/2.0.61
          Access-Control-Allow-Origin: *   //表明该资源可以被任意外域访问
          Keep-Alive: timeout=2, max=100
          Connection: Keep-Alive
          Transfer-Encoding: chunked
          Content-Type: application/xml
          
          [XML Data]
          

          使用 OriginAccess-Control-Allow-Origin 就能完成最简单的访问控制;上述例子中,将 Access-Control-Allow-Origin: * 设置为 Access-Control-Allow-Origin: http://foo.example,那么除了 http://foo.example,其它外域均不能访问该资源

    • 预检请求:在 CORS 中,可以使用 OPTIONS 方法发起一个预检请求(一般都是浏览检测到请求跨域时,会自动发起),以检测实际请求是否可以被服务器所接受

      • 当请求满足下述任一条件时,应该先发送预检请求

        • 使用了 PUT、DELETE、CONNECT、TRACE、PATCH 方法之一
        • 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段
        • Content-Type 的值不属于 application/x-www-form-urlencoded、multipart/form-data、text/plain 三者之一
      • 示例

        • 如下是一个需要执行预检请求的HTTP请求(根据其请求内容可知需要先发起预检请求)

          var invocation = new XMLHttpRequest();
          var url = 'http://bar.other/resources/post-here/';
          var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
          
          function callOtherDomain(){
            if(invocation)
              {
                invocation.open('POST', url, true);   
                //使用POST请求发送XML文档
                invocation.setRequestHeader('X-PINGOTHER', 'pingpong');   
                //自定义的请求首部字段X-PINGOTHER
                invocation.setRequestHeader('Content-Type', 'application/xml');   
                //该请求的Content-Type为 application/xml
                invocation.onreadystatechange = handler;
                invocation.send(body);
              }
          }
          ......
          
        • 发起预检请求

          预检请求报文
          OPTIONS /resources/post-here/ HTTP/1.1   //使用OPTIONS方法
          Host: bar.other
          User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
          Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
          Accept-Language: en-us,en;q=0.5
          Accept-Encoding: gzip,deflate
          Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
          Connection: keep-alive
          Origin: http://foo.example
          Access-Control-Request-Method: POST   //该字段告知服务器实际请求将使用POST方法
          Access-Control-Request-Headers: X-PINGOTHER, Content-Type   //该字段告知服务器实际请求携带的两个自定义首部字段
          
          服务器响应报文
          HTTP/1.1 200 OK
          Date: Mon, 01 Dec 2008 01:15:39 GMT
          Server: Apache/2.0.61 (Unix)
          Access-Control-Allow-Origin: http://foo.example
          Access-Control-Allow-Methods: POST, GET, OPTIONS   //表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求
          Access-Control-Allow-Headers: X-PINGOTHER, Content-Type   //表明表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type
          Access-Control-Max-Age: 86400   //该字段表明该响应的有效时间为 86400 秒,也就是 24 小时,在有效时间内,浏览器无须为同一请求再次发起预检请求
          Vary: Accept-Encoding, Origin
          Content-Encoding: gzip
          Content-Length: 0
          Keep-Alive: timeout=2, max=100
          Connection: Keep-Alive
          Content-Type: text/plain
          
JSONP( JSON with padding)
  • 在segmentfault看到一个回答说“JSONP 是一种请求一段 JS 脚本,把执行这段脚本的结果当做数据的玩法”,那么JSONP实现跨域只能使用GET请求(局限性很大,现在基本都是使用CORS跨域)就得到了很好的解释:我们通过<script>标签的src属性是不能引入一段POST提交的脚本的

  • 在同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但<img>、<iframe>、<script>、<link>、<video>、<audio>等标签不受同源策略约束,这些标签可以通过src属性请求到其他服务器上的数据;如<img>的src(获取图片),<script>的src(获取javascript)

  • 所以JSONP实现跨域请求的原理简单的说,就是动态创建<script>标签,然后利用<script>的 src 不受同源策略约束来跨域获取数据,将前端方法作为参数传递到服务器端,再由服务器端注入参数之后返回,实现服务器端向客户端通信

  • JSONP请求的关键:服务端要在返回的数据外层包裹一个客户端已经定义好的函数,然后该函数会被客户端调用执行(否则浏览器会将返回的数据当作js代码执行)

iframe
  • iframe是HTML标签,<iframe> 标签规定一个内联框架,用于在当前HTML页面嵌入另一个HTML页面,基础语法参考示例参考
  • iframe跨域的基本前提是:一个页面可以嵌套非同源站点的html文件,以及某一个域名下的html页面可以通过脚本向同域名服务器发出ajax请求;一级域名相同
  • 深入剖析iframe跨域问题
document.domain
window.name

window.name实现跨域

片段识别符(fragment identifier)
跨文档通信API(Cross-document messaging)
WebSockets

参考文档

这篇博客中所学的都是一些新知识,故引用内容很多,自己的理解较少;一些的内容的引用地址在文中给出,还有一些在下面列出:

浏览器的同源策略

浏览器数据库 IndexedDB 入门教程

同源策略

同源策略以及绕过此限制的方法

跨源资源共享(CORS)

cors跨域之简单请求与预检请求(发送请求头带令牌token)

轻松搞定JSONP跨域请求

使用 JSONP 实现跨域

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用axios发送跨域请求需要在请求头中添加`Access-Control-Allow-Origin`来允许跨域访问,但是由于这个请求头是服务器端返回的,客户端无法直接添加,所以需要通过其他方式来绕过同源策略的限制。 一种常见的方法是使用JSONP(JSON with Padding)技术。JSONP是一种跨域数据请求的解决方案,它利用了script标签不受同源策略限制的特性,将需要获取的数据包装在一个回调函数中,然后通过script标签引入远程脚本,从而获取数据。 以下是一个使用axios和JSONP的示例代码: ```js import axios from 'axios'; axios.jsonp('https://h5vv.video.qq.com/getinfo', { params: { vids: vid, platform: 101001, charge: 0, otype: 'json', defn: 'sd', callback: 'jsonpCallback' } }) .then(response => { console.log(response.data); }) .catch(error => { console.log(error); }); function jsonpCallback(data) { console.log(data); } ``` 在上面的代码中,我们使用axios.jsonp()方法来发起跨域请求,并将请求的URL和参数作为参数传递给该方法。在参数中,我们需要指定一个callback参数来指定回调函数的名称,该回调函数的名称需要在服务端进行处理后返回给客户端。 在服务端处理请求时,需要根据callback参数的值将响应数据包装在一个函数中返回给客户端。例如,在Node.js中可以使用如下代码实现: ```js const callbackName = req.query.callback; const responseData = { /* 响应数据 */ }; const responseString = `${callbackName}(${JSON.stringify(responseData)})`; res.send(responseString); ``` 在客户端,我们需要定义一个与服务端返回的回调函数名称相同的函数(如上述代码中的jsonpCallback函数),该函数会在服务端返回的数据中被调用,并将响应数据作为参数传递给它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

炑虽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值