>웹 프론트엔드 >프런트엔드 Q&A >자바스크립트의 동일 출처 정책은 무엇인가요?

자바스크립트의 동일 출처 정책은 무엇인가요?

青灯夜游
青灯夜游원래의
2022-01-26 14:14:312513검색

JavaScript에서 동일 출처 정책은 클라이언트측 스크립트(특히 Javascript)에 대한 중요한 보안 측정 기준입니다. 즉, 동일한 프로토콜(프로토콜), 포트 및 호스트(도메인 이름)를 가진 두 페이지가 동일한 페이지에 속합니다. 원천.

자바스크립트의 동일 출처 정책은 무엇인가요?

이 튜토리얼의 운영 환경: Windows 7 시스템, JavaScript 버전 1.8.5, Dell G3 컴퓨터.

동일원산지 정책이 뭔가요?

동일 출처 정책은 클라이언트 측 스크립트, 특히 Javascript에 대한 중요한 보안 지표입니다. Netscape Navigator 2.0에서 처음 등장했으며 그 목적은 문서나 스크립트가 여러 다른 소스에서 로드되는 것을 방지하는 것이었습니다.

동일 출처 정책, 즉 프로토콜, 포트(지정된 경우), 호스트(도메인 이름)가 동일한 두 페이지는 동일한 소스에 속합니다.

예를 들어 http://www.example.com/dir/page.html URL의 경우
프로토콜은 http://이고
도메인입니다. 이름은 www.example.com,
포트는 80(기본 포트는 생략 가능)입니다. 그 상동성은 다음과 같습니다. http://www.example.com/dir/page.html这个网址,
协议是http://,
域名是www.example.com,
端口是80(默认端口可以省略)。它的同源情况如下。

    http://www.example.com/dir2/other.html:同源
    http://example.com/dir/other.html:不同源(域名不同)
    http://v2.www.example.com/dir/other.html:不同源(域名不同)
    http://www.example.com:81/dir/other.html:不同源(端口不同)

精髓:

它的精髓很简单:它认为自任何站点装载的信赖内容是不安全的。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源。

为什么要有同源限制?

我们举例说明:比如一个黑客程序,他利用IFrame把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名,密码登录时,他的页面就可以通过Javascript读取到你的表单中input中的内容,这样用户名,密码就轻松到手了。

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。

设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?

很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。

由此可见,”同源政策”是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。

限制范围

随着互联网的发展,”同源政策”越来越严格。目前,如果非同源,共有三种行为受到限制。

(1) Cookie、LocalStorage 和 IndexDB 无法读取。

(2) DOM 无法获得。

(3) Ajax 请求不能发送。

虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。下面,我将详细介绍,如何规避上面三种限制。

规避方法

1、Cookie

Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。

举例来说,A网页是http://w1.example.com/a.html,B网页是http://w2.example.com/b.html,那么只要设置相同的document.domain,两个网页就可以共享Cookie。

document.domain = 'example.com';

现在,A网页通过脚本设置一个 Cookie

document.cookie = "test1=hello";

B网页就可以读到这个 Cookie。

var allCookie = document.cookie;

这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法,
规避同源政策,而要使用下文介绍的PostMessage API。

另外,服务器也可以在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如.example.com。

 Set-Cookie: key=value; domain=.example.com; path=/

这样的话,二级域名和三级域名不用做任何设置,都可以读取这个Cookie。

2、iframe

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。

比如,父窗口运行下面的命令,如果iframe窗口不是同源,就会报错

document.getElementById("myIFrame").contentWindow.document
 // Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.

上面命令中,父窗口想获取子窗口的DOM,因为跨源导致报错。

反之亦然,子窗口获取主窗口的DOM也会报错。

 window.parent.document.body    // 报错

如果两个窗口一级域名相同,只是二级域名不同,那么设置上一节介绍的document.domain

var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;

핵심:

  • 핵심은 간단합니다. 모든 사이트에서 로드된 신뢰할 수 있는 콘텐츠는 안전하지 않은 것으로 간주합니다. 브라우저가 신뢰하지 않는 스크립트가 샌드박스에서 실행되는 경우 동일한 사이트의 리소스에만 액세스할 수 있어야 하며 악의적일 수 있는 다른 사이트의 리소스에는 액세스할 수 없습니다.
  • 동일출처 제한은 왜 있나요?
  • 예를 들어 보겠습니다. 예를 들어 해커 프로그램은 IFrame을 사용하여 실제 은행 로그인 페이지를 자신의 페이지에 삽입합니다. 실제 사용자 이름과 비밀번호로 로그인하면 Javascript를 통해 해당 페이지에 액세스할 수 있습니다. 사용자 이름과 비밀번호를 쉽게 얻을 수 있도록 양식에 내용을 입력하십시오.
동일 출처 정책의 목적은 사용자 정보의 보안을 보장하고 악성 웹사이트의 데이터 도용을 방지하는 것입니다.

다음과 같은 상황을 상상해 보세요. 웹사이트 A는 은행입니다. 사용자는 로그인한 후 다른 웹사이트를 탐색합니다. 다른 웹사이트가 웹사이트 A의 쿠키를 읽을 수 있으면 어떻게 되나요?

물론, 쿠키에 개인 정보(예: 총 입금액)가 포함되어 있으면 해당 정보가 유출됩니다. 더욱 무서운 점은 쿠키가 사용자의 로그인 상태를 저장하는 데 자주 사용된다는 것입니다. 사용자가 로그아웃하지 않으면 다른 웹사이트가 사용자를 사칭하여 원하는 대로 할 수 있습니다. 브라우저는 또한 양식 제출이 동일한 출처 정책에 의해 제한되지 않는다고 규정하기 때문입니다. 🎜🎜"동일 출처 정책"이 필요하다고 볼 수 있습니다. 그렇지 않으면 쿠키가 공유될 수 있으며 인터넷은 전혀 안전하지 않게 됩니다. 🎜🎜🎜🎜제한된 범위🎜🎜🎜🎜인터넷이 발달하면서 "동일 출처 정책"이 점점 더 엄격해지고 있습니다. 현재 동일한 출처가 아닌 경우 제한되는 세 가지 동작이 있습니다. 🎜
window.onhashchange = checkMessage;

    function checkMessage() {
      var message = window.location.hash;
      // ...
    }
🎜이러한 제한은 필요하지만 때로는 불편하고 합리적인 목적에 영향을 미치기도 합니다. 아래에서는 위의 세 가지 제한 사항을 우회하는 방법을 자세히 소개하겠습니다. 🎜🎜🎜🎜방지 방법 🎜🎜🎜🎜🎜 1. 쿠키 🎜🎜🎜쿠키는 서버가 브라우저에 기록하는 작은 정보 조각으로, 동일한 출처를 가진 웹페이지만 공유할 수 있습니다. 그러나 두 웹페이지의 1차 도메인 이름은 동일하지만 2차 도메인 이름은 다릅니다. 브라우저는 document.domain을 설정하여 쿠키 공유를 허용합니다. 🎜🎜예를 들어 웹페이지 A는 http://w1.example.com/a.html이고, 웹페이지 B는 http://w2.example.com/b.html입니다. , 동일한 document.domain이 설정되어 있는 한 두 웹페이지는 쿠키를 공유할 수 있습니다. 🎜<pre class="brush:js;toolbar:false;"> parent.location.href= target + &quot;#&quot; + hash;</pre>🎜이제 웹페이지 A는 스크립트를 통해 쿠키를 설정합니다🎜<pre class="brush:js;toolbar:false;">window.name = data;</pre>🎜웹페이지 B는 이 쿠키를 읽을 수 있습니다. 🎜<pre class="brush:js;toolbar:false;">location = &amp;#39;http://parent.url.com/xxx.html&amp;#39;;</pre>🎜이 방법은 쿠키 및 iframe 창에만 적용 가능합니다. LocalStorage 및 IndexDB는 이 방법을 통해 <br> 동일한 원본 정책을 우회할 수 없지만 아래에 소개된 PostMessage API를 사용해야 합니다. 🎜🎜또한 서버는 쿠키를 설정할 때 .example.com과 같이 쿠키의 도메인 이름을 1차 도메인 이름으로 지정할 수도 있습니다. 🎜<pre class="brush:js;toolbar:false;">var data = document.getElementById(&amp;#39;myFrame&amp;#39;).contentWindow.name;</pre>🎜이 경우 2차 도메인 이름과 3차 도메인 이름은 아무런 설정 없이 이 쿠키를 읽을 수 있습니다. 🎜🎜🎜2.iframe🎜🎜🎜두 웹페이지의 소스가 다를 경우 상대방의 DOM을 가져올 수 없습니다. 일반적인 예로는 iframe 창과 부모 창과 통신할 수 없는 window.open 메서드로 열린 창이 있습니다. 🎜🎜예를 들어 상위 창에서 다음 명령을 실행하는 경우 iframe 창이 동일한 소스에서 나오지 않으면 오류가 보고됩니다.🎜<pre class="brush:js;toolbar:false;"> var popup = window.open(&amp;#39;http://bbb.com&amp;#39;, &amp;#39;title&amp;#39;); popup.postMessage(&amp;#39;Hello World!&amp;#39;, &amp;#39;http://bbb.com&amp;#39;);</pre>🎜위 명령에서 상위 창은 하위 창의 DOM을 얻으려고 합니다. , 교차 출처로 인해 오류가 보고됩니다. 🎜🎜 반대로 하위 창이 기본 창의 DOM을 얻을 때도 오류가 보고됩니다. 🎜<pre class="brush:js;toolbar:false;">window.opener.postMessage(&amp;#39;Nice to see you&amp;#39;, &amp;#39;http://aaa.com&amp;#39;);</pre>🎜두 창의 첫 번째 수준 도메인 이름이 동일하지만 두 번째 수준 도메인 이름이 다른 경우 이전 섹션에서 소개한 <code>document.domain 속성을 ​​설정하면 동일 출처 정책을 적용하고 DOM을 가져옵니다. 🎜🎜출처가 완전히 다른 웹사이트의 경우 현재 크로스 도메인 창의 통신 문제를 해결하는 세 가지 방법이 있습니다. 🎜🎜🎜조각 식별자🎜🎜window.name🎜🎜교차 문서 메시징 API(교차 문서 메시징)🎜🎜🎜🎜2.1 조각 식별자🎜🎜

片段标识符(fragment identifier)指的是,URL的#号后面的部分,比如http://example.com/x.html#fragment的#fragment。如果只是改变片段标识符,页面不会重新刷新。

父窗口可以把信息写入子窗口的片段标识符。

var src = originURL + &#39;#&#39; + data;
document.getElementById(&#39;myIFrame&#39;).src = src;

子窗口通过监听hashchange事件得到通知。

window.onhashchange = checkMessage;

    function checkMessage() {
      var message = window.location.hash;
      // ...
    }

同样的,子窗口也可以改变父窗口的片段标识符。

 parent.location.href= target + "#" + hash;

2.2 window.name

浏览器窗口有window.name属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。

父窗口先打开一个子窗口,载入一个不同源的网页,该网页将信息写入window.name属性。

window.name = data;

接着,子窗口跳回一个与主窗口同域的网址。

location = &#39;http://parent.url.com/xxx.html&#39;;

然后,主窗口就可以读取子窗口的window.name了。

var data = document.getElementById(&#39;myFrame&#39;).contentWindow.name;

这种方法的优点是,window.name容量很大,可以放置非常长的字符串;缺点是必须监听子窗口window.name属性的变化,影响网页性能。

2.3 window.postMessage

上面两种方法都属于破解,HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。

这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。

举例来说,父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。

 var popup = window.open(&#39;http://bbb.com&#39;, &#39;title&#39;);
 popup.postMessage(&#39;Hello World!&#39;, &#39;http://bbb.com&#39;);

postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即”协议 + 域名 + 端口”。也可以设为*,表示不限制域名,向所有窗口发送。

子窗口向父窗口发送消息的写法类似。

window.opener.postMessage(&#39;Nice to see you&#39;, &#39;http://aaa.com&#39;);

父窗口和子窗口都可以通过message事件,监听对方的消息。

window.addEventListener(&#39;message&#39;, function(e) {
  console.log(e.data);
},false);

message事件的事件对象event,提供以下三个属性。

    event.source:发送消息的窗口
    event.origin: 消息发向的网址
    event.data: 消息内容

下面的例子是,子窗口通过event.source属性引用父窗口,然后发送消息。

    window.addEventListener(&#39;message&#39;, receiveMessage);
    function receiveMessage(event) { 
       event.source.postMessage(&#39;Nice to see you!&#39;, &#39;*&#39;);
    }

event.origin属性可以过滤不是发给本窗口的消息。

window.addEventListener(&#39;message&#39;, receiveMessage);
 function receiveMessage(event) {
   if (event.origin !== &#39;http://aaa.com&#39;) return;
   if (event.data === &#39;Hello World&#39;) {
       event.source.postMessage(&#39;Hello&#39;, event.origin);
   } else {
     console.log(event.data);
   }
 }

2.4 LocalStorage

通过window.postMessage,读写其他窗口的 LocalStorage 也成为了可能。

下面是一个例子,主窗口写入iframe子窗口的localStorage。

window.onmessage = function(e) {
   if (e.origin !== &#39;http://bbb.com&#39;) {
        return;
   }   
   var payload = JSON.parse(e.data);
   localStorage.setItem(payload.key, JSON.stringify(payload.data));
 };

上面代码中,子窗口将父窗口发来的消息,写入自己的LocalStorage。

父窗口发送消息的代码如下。

var win = document.getElementsByTagName(&#39;iframe&#39;)[0].contentWindow;
var obj = { name: &#39;Jack&#39; };
win.postMessage(JSON.stringify({key: &#39;storage&#39;, data: obj}), &#39;http://bbb.com&#39;);

加强版的子窗口接收消息的代码如下。

window.onmessage = function(e) {
 if (e.origin !== &#39;http://bbb.com&#39;) return;
 var payload = JSON.parse(e.data);
 switch (payload.method) {
   case &#39;set&#39;:
     localStorage.setItem(payload.key, JSON.stringify(payload.data));
     break;
   case &#39;get&#39;:
     var parent = window.parent;
     var data = localStorage.getItem(payload.key);
     parent.postMessage(data, &#39;http://aaa.com&#39;);
     break;
   case &#39;remove&#39;:
     localStorage.removeItem(payload.key);
     break;
 }
};

加强版的父窗口发送消息代码如下。

var win = document.getElementsByTagName(&#39;iframe&#39;)[0].contentWindow;
 var obj = { name: &#39;Jack&#39; };
 // 存入对象
 win.postMessage(JSON.stringify({key: &#39;storage&#39;, method: &#39;set&#39;, data: obj}), &#39;http://bbb.com&#39;);
 // 读取对象
 win.postMessage(JSON.stringify({key: &#39;storage&#39;, method: "get"}), "*");
 window.onmessage = function(e) {
   if (e.origin != &#39;http://aaa.com&#39;) return;
   // "Jack"
   console.log(JSON.parse(e.data).name);
 };

3、Ajax

同源政策规定,AJAX请求只能发给同源的网址,否则就报错。

除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制。

    JSONP
    WebSocket
    CORS

3.1 JSONP

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。

它的基本思想是,网页通过添加一个3f1c4e4b6b16bbbd69b2ee476dc4f83a元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

首先,网页动态插入3f1c4e4b6b16bbbd69b2ee476dc4f83a元素,由它向跨源网址发出请求。

   function addScriptTag(src) {
      var script = document.createElement(&#39;script&#39;);
      script.setAttribute("type","text/javascript");
      script.src = src;
      document.body.appendChild(script);
    }

    window.onload = function () {
      addScriptTag(&#39;http://example.com/ip?callback=foo&#39;);
    }    function foo(data) {
      console.log(&#39;Your public IP address is: &#39; + data.ip);
    };

上面代码通过动态添加3f1c4e4b6b16bbbd69b2ee476dc4f83a元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。

服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。

foo({
  "ip": "8.8.8.8"
});

由于3f1c4e4b6b16bbbd69b2ee476dc4f83a元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

3.2 WebSocket

WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

下面是一个例子,浏览器发出的WebSocket请求的头信息(摘自维基百科)。

    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
    Sec-WebSocket-Protocol: chat, superchat
    Sec-WebSocket-Version: 13
    Origin: http://example.com

上面代码中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪个域名。

正是因为有了Origin这个字段,所以WebSocket才没有实行同源政策。因为服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

3.3 CORS

CORS는 Cross-Origin Resource Sharing의 약어입니다. 이는 W3C 표준이며 교차 출처 AJAX 요청을 위한 기본 솔루션입니다. GET 요청만 보낼 수 있는 JSONP와 비교하여 CORS는 모든 유형의 요청을 허용합니다.

【관련 권장 사항: javascript 학습 튜토리얼

위 내용은 자바스크립트의 동일 출처 정책은 무엇인가요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.