跨域访问和防盗链基本原理

一、什么是防盗链

网站资源都有域的概念,浏览器加载一个站点时,首先加载这个站点的首页,一般是index.html或者index.php等。页面加载,如果仅仅 是加载一个index.html页面,那么该页面里面只有文本,最终浏览器只能呈现一个文本页面。丰富的多媒体信息无法在站点上面展现。

那么我们看到的各类元素丰富的网页是如何在浏览器端生成并呈现的?其实,index.html在被解析时,浏览器会识别页面源码中的 img,script等标签,标签内部一般会有src属性,src属性一般是一个绝对的URL地址或者相对本域的地址。浏览器会识别各种情况,并最终得到 该资源的唯一地址,加载该资源。具体的加载过程就是对该资源的URL发起一个获取数据的请求,也就是GET请求。各种丰富的资源组成整个页面,浏览器按照 html语法指定的格式排列获取到各类资源,最终呈现一个完整的页面。因此一个网页是由很多次请求,获取众多资源形成的,整个浏览器在一次网页呈现中会有 很多次GET请求获取各个标签下的src资源。

上图是一篇本站的博客网页呈现过程中的抓包截图。可以看到,大量的加载css、js和图片类资源的get请求。

观察其中的请求目的地址,可以发现有两类,一个是本站的43.242段的IP地址,这是本站的空间地址,即向本站自身请求资源,一般来说这个是必须 的,访问资源由自身托管。另外一类是访问182的网段拉取数据。这类数据不是托管站内的,是在其他站点的。浏览器在页面呈现的过程,拉取非本站的资源,这 就称“盗链”。

准确的说,只有某些时候,这种跨站访问资源,才被称为盗链。假设B站点作为一个商业网站,有很多自主版权的图片,自身展示用于商业目的。而A站点,希望在自己的网站上面也展示这些图片,直接使用:

<img src="http://b.com/photo.jpg"/>

这样,大量的客户端在访问A站点时,实际上消耗了B站点的流量,而A站点却从中达成商业目的。从而不劳而获。这样的A站点着实令B站点不快的。如何禁止此类问题呢?

HTTP协议和标准的浏览器对于解决这个问题提供便利,浏览器在加载非本站的资源时,会增加一个头域,头域名字固定为:

Referer:

而在直接粘贴地址到浏览器地址栏访问时,请求的是本站的该url的页面,是不会有这个referer这个http头域的。使用Chrome浏览器的调试台,打开network标签可以看到每一个资源的加载过程,下面两个图分别是主页面和一个页面内资源的加载请求截图:

这个referer标签正是为了告诉请求响应者(被拉取资源的服务端),本次请求的引用页是谁,资源提供端可以分析这个引用者是否“友好”,是否允许其“引用”,对于不允许访问的引用者,可以不提供图片,这样访问者在页面上就只能看到一个图片无法加载的浏览器默认占位的警告图片,甚至服务端可以返回一个默认的提醒勿盗链的提示图片。

一般的站点或者静态资源托管站点都提供防盗链的设置,也就是让服务端识别指定的Referer,在服务端接收到请求时,通过匹配referer头域与配置,对于指定放行,对于其他referer视为盗链。

二、跨域访问基本原理

在上一篇,介绍了盗链的基本原理和防盗链的解决方案。这里更深入分析一下跨域访问。先看看

跨域访问的相关原理:跨网站指令码。维基上面给出了跨站访问的危害性。从这里可以整理出跨站访问的定义:JS脚本在浏览器端发起的请求其他域(名)下的网站数据的HTTP请求。

这里要与referer区分开,referer是浏览器的行为,所有浏览器发出的请求都不会存在安全风险。而由网页加载的脚本发起请求则会不可控, 甚至可以截获用户数据传输到其他站点。referer方式拉取其他网站的数据也是跨域,但是这个是由浏览器请求整个资源,资源请求到后,客户端的脚本并不 能操纵这份数据,只能用来呈现。但是很多时候,我们都需要发起请求到其他站点动态获取数据,并将获取到底数据进行进一步的处理,这也就是跨域访问的需求。

现在从技术上有几个方案去解决这个问题。

1、JSONP跨域访问

利用浏览器的Referer方式加载脚本到客户端的方式。以:

<script type="text/javascript" src="http://api.com/jsexample.js"></script>  

这种方式获取并加载其他站点的JS脚本是被允许的,加载过来的脚本中如果有定义的函数或者接口,可以在本地使用,这也是我们用得最多的脚本加载方式。但是这个加载到本地脚本是不能被修改和处理的,只能是引用。

而跨域访问需要正是访问远端抓取到的数据。那么能否反过来,本地写好一个数据处理函数,让请求服务端帮助完成调用过程?JS脚本允许这样。

<script type="text/javascript">
var localHandler = function(data)
{
    alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
};
</script>
<script type="text/javascript" src="http://remoteserver.com/remote.js"></script>

远端的服务器上面定义的remote.js是这样的:

localHandler({"result":"我是远程js带来的数据"});

上面首先在本地定义了一个函数localHandler,然后远端返回的JS的内容是调用这个函数,返回到浏览器端执行。同时在JS内容中将客户端需要的数据返回,这样数据就被传输到了浏览器端,浏览器端只需要修改处理方法即可。这里有一些限制:1、客户端脚本和服务端需要一些配合;2、调用的数据必须是json格式的,否则客户端脚本无法处理;3、只能给被引用的服务端网址发送get请求。

更为通用一点点方式是,浏览器端在引用该网址时,即写好回调函数的名字,服务端根据客户端指定的名字来拼装调用过程:

<script type="text/javascript">
var localHandler = function(data)
{
    alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);
};
</script>
<script type="text/javascript" src="http://remoteserver.com/remote.php?callBack=localHandler"></script>

服务端的PHP函数可能是这样的:

<?php
 
$data = ".......";
$callback = $_GET['callback'];
echo $callback.'('.json_encode($data).')';
exit;
 
?>

这样即可根据客户端指定的回调拼装调用过程。

2、CORS(Cross-origin resource sharing)跨域访问

上述的JSONP由于有诸多限制,已经无法满足各种灵活的跨域访问请求。现在浏览器支持一种新的跨域访问机制,基于服务端控制访问权限的方式。简而言之,浏览器不再一味禁止跨域访问,而是需要检查目的站点返回的消息的头域,要检查该响应是否允许当前站点访问。通过HTTP头域的方式来通知浏览器:

Response headers[edit]
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Allow-Methods
Access-Control-Allow-Headers

服务端利用这几个HTTP头域通知浏览器该资源的访问权限信息。在访问资源前,浏览器会先发出OPTIONS请求,获取这些权限信息,并比对当前站点的脚本是否有权限,然后再将实际的脚本的数据请求发出。发现权限不允许,则不会发出请求。逻辑流程图为:

浏览器也可以直接将GET请求发出,数据和权限同时到达浏览器端,但是数据是否交给脚本处理需要浏览器检查权限对比后作出决定。

一次具体的跨域访问的流程为:

因此权限控制交给了服务端,服务端一般也会提供对资源的CORS的配置。

跨域访问还有其他几种方式:本站服务端代理、跨子域时使用修改域标识等方法,但是应用场景的限制更多。目前绝大多数的跨域访问都由JSONP和CORS这两类方式组成。

原文发布于微信公众号 - php(phpdaily)

原文发表时间:2015-10-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏上善若水

S005SELinux(SEAndroid)的实际文件组成无标题文章

SEAndroid 是将SELinux 移植到Android 上的产物,可以看成SELinux 辅以一套适用于Android 的策略。

1565
来自专栏云计算教程系列

如何在CentOS 7上使用Barman备份,恢复和迁移PostgreSQL数据库

PostgreSQL是一个开源数据库平台,因其易于维护,成本效益以及与其他开源技术的简单集成而广受网络和移动应用程序开发人员的欢迎。

2890
来自专栏Jay的后台开发笔记

简单的linux系统配置故障定位与排除

本文旨在通过一些常用命令的用法示例,让有一定linux基础的开发同学能对系统进行简单配置,也能够上服务器定位或者解决一些简单基础性的问题,做出初步故障排除,或者...

2626
来自专栏北京马哥教育

70条常用Linux基础命令总结

? [root@ping ~]# tree -L 1 / #使用tree 命令查看根目录下的一层的目录结构 ls - list directory co...

3637
来自专栏跟着阿笨一起玩NET

搭建windows server 2008 r2 FTP 后 开启防火墙无法访问的解决办法

转自http://kkworms.blog.51cto.com/540865/558477

1.2K1
来自专栏ccylovehs

mysql启动报错The server quit without updating PID file

修改datadir=/var/lib/mysql     -- linux中mysql安装的默认路径

4.9K1
来自专栏jmeter高手高高手

Linux内存机制以及手动释放swap和内存

我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念...

2553
来自专栏noteless

eclipse 创建maven 项目 动态web工程完整示例

注意,以下所有需要建立在你的eclipse等已经集成配置好了maven了,说白了就是新建项目的时候已经可以找到maven了

1051
来自专栏酷玩时刻

Centos 通过 Nginx 和 vsftpd 构建图片服务器

这篇文章主要介绍了Centos 通过 nginx 和 vsftpd 构建图片服务器, 需要的朋友可以参考下

2152
来自专栏北京马哥教育

Linux自动化运维工具之ansible(一)

豌豆贴心提醒,本文阅读时间5分钟 运维自动化是运维发展的必然方向,同时也是一个运维工程师实现效率最大化的必然选择。 运维自动化的知识可以说是浩瀚如海,本文将...

3905

扫码关注云+社区