>  기사  >  웹 프론트엔드  >  JavaScript 원본 정책 및 교차 도메인 액세스 소개

JavaScript 원본 정책 및 교차 도메인 액세스 소개

不言
不言원래의
2018-07-03 17:19:111921검색

이 글에서는 주로 자바스크립트 동일 출처 정책과 크로스 도메인 접근을 소개하며, 자바스크립트 동일 출처 정책과 크로스 도메인 접근의 원리, 구현, 사용법, 관련 주의사항을 예시 형식으로 자세히 분석합니다. 다음을 참조하세요.

이 문서의 예에서는 JavaScript 동일 출처 정책 및 도메인 간 액세스에 대해 설명합니다. 참고를 위해 모든 사람과 공유합니다. 세부 사항은 다음과 같습니다.

1. 동일 출처 정책이란 무엇입니까?

크로스 도메인을 이해하려면 먼저 동일 출처 정책을 이해해야 합니다. 동일 출처 정책은 보안상의 이유로 브라우저에 구현되는 매우 중요한 보안 정책입니다.

동일한 출처란 무엇입니까?

URL은 프로토콜, 도메인 이름, 포트 및 경로로 구성됩니다. 두 URL의 프로토콜, 도메인 이름 및 포트가 동일하면 출처가 동일하다는 의미입니다.

동일 출처 정책:

브라우저의 동일 출처 정책은 다른 소스의 "문서" 또는 스크립트가 현재 "문서"의 특정 속성을 읽거나 설정하는 것을 제한합니다. (White Hat은 웹 보안에 대해 이야기합니다 [1])

한 도메인에서 로드된 스크립트는 다른 도메인의 문서 속성에 액세스할 수 없습니다.

예:

예를 들어, 악성 웹사이트 페이지는 iframe을 통해 은행의 로그인 페이지를 삽입합니다(둘은 출처가 다릅니다). 출처 제한이 없는 경우 악성 웹페이지의 자바스크립트 스크립트는 다음과 같은 경우에 얻을 수 있습니다. 사용자가 은행에 로그인합니다.

브라우저에서 3f1c4e4b6b16bbbd69b2ee476dc4f83a, a1f02c36ba31691bcfe87b2722de723b, d5ba1642137c3f32f4f4493ae923989c, 2cdf5bf648cf2f33323966d7f58a7f3f와 같은 태그는 동일한 출처에 의해 제한되지 않고 도메인 간 리소스를 로드할 수 있지만 브라우저는 JavaScript의 권한을 제한합니다. . 로드된 콘텐츠를 읽거나 쓸 수 없도록 만듭니다.

또한 동일 원본 정책은 웹 페이지의 HTML 문서만 제한하며, 자바스크립트, CSS, 이미지 등과 같이 로드된 기타 정적 리소스는 여전히 동일한 원본에 속하는 것으로 간주됩니다.

코드 예(http://localhost:8080/ 및 http://localhost:8081은 포트가 다르기 때문에 출처가 다름):

http://localhost:8080/test.html

<html>
  <head><title>test same origin policy</title></head>
  <body>
    <iframe id="test" src="http://localhost:8081/test2.html"></iframe>
    <script type="text/javascript">
      document.getElementById("test").contentDocument.body.innerHTML = "write somthing";
    </script>
  </body>
</html>

http://localhost:8081/test2.html

<html>
  <head><title>test same origin policy</title></head>
  <body>
    Testing.
  </body>
</html>

Firefox에서 다음 오류가 발생합니다.

오류: 'body' 속성에 액세스할 수 있는 권한이 거부되었습니다.

Document 객체의 도메인 속성 로드된 문서를 저장합니다. 서버의 호스트 이름을 설정할 수 있습니다.

예를 들어 "blog.php.cn"과 "bbs.php.cn"의 페이지가 모두 document.domain을 "php.cn"으로 설정한 경우 두 하위 도메인의 스크립트는 서로 액세스할 수 있습니다.

보안상의 이유로 다른 주요 도메인으로 설정할 수 없습니다. 예를 들어 //www.php.cn/은 sina.com으로 설정할 수 없습니다

2 Ajax 크로스 도메인

Ajax(XMLHttpRequest) 요청에는 동일한 소스 정책 제한이 적용됩니다.

Ajax는 XMLHttpRequest를 통해 원격 서버와 상호 작용할 수 있습니다. 또한 XMLHttpRequest는 순수 Javascript 객체이며 이 상호 작용 프로세스는 백그라운드에서 수행되며 사용자가 쉽게 알아차리지 못합니다.

따라서 XMLHTTP는 실제로 원래 Javascript의 보안 한계를 극복했습니다.

예:

웹사이트가 다른 사이트의 자바스크립트를 참조한다고 가정해 보겠습니다. 이 사이트는 해킹되어 자바스크립트에 추가되어 사용자 입력을 얻고 이를 ajax를 통해 다른 사이트에 제출하여 지속적으로 정보를 수집할 수 있습니다.

또는 웹사이트에 XSS가 javascript 스크립트를 삽입하는 취약점이 있을 수 있습니다. 이 스크립트는 ajax를 통해 사용자 정보를 얻고 이를 ajax를 통해 다른 사이트에 제출하여 지속적으로 정보를 수집할 수 있습니다.

새로 고침이 필요 없는 XMLHTTP의 비동기 상호 작용 기능을 활용하고 싶지만 Javascript의 보안 정책을 공개적으로 깨뜨리고 싶지 않은 경우 대안은 XMLHTTP에 엄격한 동일 출처 제한을 추가하는 것입니다.

이 보안 정책은 Applet의 보안 정책과 매우 유사합니다. IFrame의 한계는 도메인 간 HTML DOM의 데이터에 액세스할 수 없다는 점입니다. 반면 XMLHTTP는 근본적으로 도메인 간 요청 제출을 제한합니다. (실제로 CORS는 제한을 완화했다고 아래에 언급되어 있습니다.)

Ajax 기술과 네트워크 서비스의 발전으로 크로스 도메인에 대한 요구 사항이 점점 더 강력해지고 있습니다. 다음은 Ajax의 크로스 도메인 기술을 소개합니다.

2.1 JSONP

JSONP 기술은 실제로 Ajax와 아무런 관련이 없습니다. 우리는 3f1c4e4b6b16bbbd69b2ee476dc4f83a 태그가 도메인 간 자바스크립트 스크립트를 로드할 수 있으며 로드된 스크립트와 현재 문서가 동일한 도메인에 속한다는 것을 알고 있습니다. 따라서 스크립트의 데이터와 기능을 문서에서 호출/액세스할 수 있습니다. JavaScript 스크립트의 데이터가 동적으로 생성되는 경우 문서에 3f1c4e4b6b16bbbd69b2ee476dc4f83a 태그를 동적으로 생성하여 서버와의 데이터 상호 작용을 달성할 수 있습니다.

JSONP는 3f1c4e4b6b16bbbd69b2ee476dc4f83a 태그의 도메인 간 기능을 사용하여 도메인 간 데이터 액세스를 달성하고 콜백 함수 이름을 매개변수로 사용하여 동적으로 생성된 JavaScript 스크립트를 요청합니다. 그 중 콜백 함수는 로컬 문서의 자바스크립트 함수로, 서버측에서 동적으로 생성된 스크립트가 데이터를 생성하고, 생성된 데이터를 매개변수로 코드에서 콜백 함수가 호출된다. 이 스크립트가 로컬 문서에 로드되면 콜백 함수가 호출됩니다.

첫 번째 사이트의 테스트 페이지(http://localhost:8080/test.html):

<script src="http://localhost:8081/test_data.js">
  <script>
    function test_handler(data) {
      console.log(data);
    }
</script>

服务器端的Javascript脚本(http://localhost:8081/test_data.js):

test_handler(&#39;{"data": "something"}&#39;);

为了动态实现JSONP请求,可以使用Javascript动态插入3f1c4e4b6b16bbbd69b2ee476dc4f83a标签:

<script type="text/javascript">
    // this shows dynamic script insertion
    var script = document.createElement(&#39;script&#39;);
    script.setAttribute(&#39;src&#39;, url);
    // load the script
    document.getElementsByTagName(&#39;head&#39;)[0].appendChild(script);
</script>

JSONP协议封装了上述步骤,jQuery中统一是现在AJAX中(其中data type为JSONP):

http://localhost:8080/test?callback=test_handler

为了支持JSONP协议,服务器端必须提供特别的支持[2],另外JSONP只支持GET请求。

2.2 Proxy

使用代理方式跨域更加直接,因为SOP的限制是浏览器实现的。如果请求不是从浏览器发起的,就不存在跨域问题了。

使用本方法跨域步骤如下:

1. 把访问其它域的请求替换为本域的请求

2. 本域的请求是服务器端的动态脚本负责转发实际的请求

各种服务器的Reverse Proxy功能都可以非常方便的实现请求的转发,如Apache httpd + mod_proxy。

Eg.

为了通过Ajax从http://localhost:8080访问http://localhost:8081/api,可以将请求发往http://localhost:8080/api。

然后利用Apache Web服务器的Reverse Proxy功能做如下配置:

ProxyPass /api http://localhost:8081/api

2.3 CORS

2.3.1 Cross origin resource sharing

“Cross-origin resource sharing (CORS) is a mechanism that allows a web page to make XMLHttpRequests to another domain. Such "cross-domain" requests would otherwise be forbidden by web browsers, per the same origin security policy. CORS defines a way in which the browser and the server can interact to determine whether or not to allow the cross-origin request. It is more powerful than only allowing same-origin requests, but it is more secure than simply allowing all such cross-origin requests.” ----Wikipedia[3]

通过在HTTP Header中加入扩展字段,服务器在相应网页头部加入字段表示允许访问的domain和HTTP method,客户端检查自己的域是否在允许列表中,决定是否处理响应。

实现的基础是JavaScript不能够操作HTTP Header。某些浏览器插件实际上是具有这个能力的。

服务器端在HTTP的响应头中加入(页面层次的控制模式):

Access-Control-Allow-Origin: example.com
Access-Control-Request-Method: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Range, Origin
Access-Control-Expose-Headers: Content-Range
Access-Control-Max-Age: 3600

多个域名之间用逗号分隔,表示对所示域名提供跨域访问权限。"*"表示允许所有域名的跨域访问。

客户端可以有两种行为:

1. 发送OPTIONS请求,请求Access-Control信息。如果自己的域名在允许的访问列表中,则发送真正的请求,否则放弃请求发送。

2. 直接发送请求,然后检查response的Access-Control信息,如果自己的域名在允许的访问列表中,则读取response body,否则放弃。

本质上服务端的response内容已经到达本地,JavaScript决定是否要去读取。

Support: [Javascript Web Applications]
* IE >= 8 (需要安装caveat)
* Firefox >= 3
* Safari 完全支持
* Chrome 完全支持
* Opera 不支持

 2.3.2 测试

测试页面http://localhost:8080/test3.html使用jquery发送Ajax请求。

<html>
    <head><title>testing cross sop</title></head>
    <body>
      Testing.
      <script src="jquery-2.0.0.min.js"></script>
      <script type=&#39;text/javascript&#39;>
        $.ajax({
          url: &#39;http://localhost:8000/hello&#39;,
          success: function(data) {
            alert(data);
          },
          error: function() {
            alert(&#39;error&#39;);
          }
        });
      </script>
    </body>
</html>

测试Restful API(http://localhost:8000/hello/{name})使用bottle.py来host。

from bottle import route, run, response
@route(&#39;/hello&#39;)
def index():
  return &#39;Hello World.&#39;
run(host=&#39;localhost&#39;, port=8000)

测试1:

测试正常的跨域请求的行为。

测试结果:

1. 跨域GET请求已经发出,请求header中带有

    Origin    http://localhost:8080

2. 服务器端正确给出response

3. Javascript拒绝读取数据,在firebug中发现reponse为空,并且触发error回调

测试2:

测试支持CORS的服务器的跨域请求行为。

对Restful API做如下改动,在response中加入header:

def index():
  #Add CORS header#
  response.set_header("Access-Control-Allow-Origin", "http://localhost:8080")
  return &#39;Hello World.&#39;

测试结果:

1. 跨域GET请求已经发出,请求header中带有

Origin    http://localhost:8080

2. 服务器端正确给出response

3. 客户端正常获取数据

测试3:

测试OPTIONS请求获取CORS信息。
对客户端的Ajax请求增加header:

$.ajax({
 url: &#39;http://localhost:8000/hello&#39;,
 headers: {&#39;Content-Type&#39;: &#39;text/html&#39;},
 success: function(data) {
   alert(data);
 },
 error: function() {
   alert(&#39;error&#39;);
 }
});

对Restful API做如下改动:

@route(&#39;/hello&#39;, method = [&#39;OPTIONS&#39;, &#39;GET&#39;])
def index():
  if request.method == &#39;OPTIONS&#39;:
    return &#39;&#39;
  return &#39;Hello World.&#39;

测试结果:

1. Ajax函数会首先发送OPTIONS请求
2. 针对OPTIONS请求服务器
3. 客户端发现没有CORS header后不会发送GET请求

测试4:

增加服务器端对OPTIONS方法的处理。

对Restful API做如下改动:

@route(&#39;/hello&#39;, method = [&#39;OPTIONS&#39;, &#39;GET&#39;])
def index():
    response.headers[&#39;Access-Control-Allow-Origin&#39;] = &#39;http://localhost:8080&#39;
    response.headers[&#39;Access-Control-Allow-Methods&#39;] = &#39;GET, OPTIONS&#39;
    response.headers[&#39;Access-Control-Allow-Headers&#39;] = &#39;Origin, Accept, Content-Type&#39;
    if request.method == &#39;OPTIONS&#39;:
      return &#39;&#39;
    return &#39;Hello World.&#39;

测试结果:

1. Ajax函数会首先发送OPTIONS请求
2. 针对OPTIONS请求服务器
3. 客户端匹配CORS header中的allow headers and orgin后会正确发送GET请求并获取结果

测试发现,Access-Control-Allow-Headers是必须的。

CORS协议提升了Ajax的跨域能力,但也增加了风险。一旦网站被注入脚本或XSS攻击,将非常方便的获取用户信息并悄悄传递出去。

4. Cookie 同源策略

Cookie中的同源只关注域名,忽略协议和端口。所以https://localhost:8080/和http://localhost:8081/的Cookie是共享的。

5. Flash/SilverLight跨域

浏览器的各种插件也存在跨域需求。通常是通过在服务器配置crossdomain.xml[4],设置本服务允许哪些域名的跨域访问。

客户端会首先请求此文件,如果发现自己的域名在访问列表里,就发起真正的请求,否则不发送请求。

<?xml version="1.0"?>
  <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
  <cross-domain-policy>
  <allow-access-from domain="*"/>
  <allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>

通常crossdomain.xml放置在网站根目录。

6. 总结

互联网的发展催生了跨域访问的需求,各种跨域方法和协议满足了需求但也增加了各种风险。尤其是XSS和CSRF等攻击的盛行也得益于此。

了解这些技术背景有助于在实际项目中熟练应用并规避各种安全风险。

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

Javascript实现商品秒杀倒计时(时间与服务器时间同步)的解析

JS实现的JSON序列化操作

위 내용은 JavaScript 원본 정책 및 교차 도메인 액세스 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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