需要进行防盗链的绕过,我们必须先要了解Iframe、Referer和XMLHttpRequest对象的基本知识
目录
下面举三个将referrer设置为no-referrer的例子:
这里是referer1中使用方法1设置了no-referer
这里是referer1中使用方法2设置了no-referer
利用HTTPS网站盗链http资源网站(HTTPS->HTTP)
利用referrerpolicy="no-referrer"
标签用于在网页里面嵌入其他网页。
标签生成一个指定区域,在该区域中嵌入其他网页。它是一个容器元素,如果浏览器不支持
,就会显示内部的子元素。
- <iframe src="https://www.example.com"
- width="100%" height="500" frameborder="0"
- allowfullscreen sandbox> //这里的sandbox可以保证安全
- <p><a href="https://www.example.com">点击打开嵌入页面a>p>
- iframe>
上面的代码在当前网页嵌入https://www.example.com
,显示区域的宽度是100%
,高度是500
像素。如果当前浏览器不支持,则会显示一个链接,让用户点击。
浏览器普遍支持,所以内部的子元素可以不写。
iframe
的属性如下:
allowfullscreen
:允许嵌入的网页全屏显示,需要全屏 API 的支持,请参考相关的 JavaScript 教程。
frameborder
:是否绘制边框,0
为不绘制,1
为绘制(默认值)。建议尽量少用这个属性,而是在 CSS 里面设置样式。
src
:嵌入的网页的 URL。
width
:显示区域的宽度。
height
:显示区域的高度。
sandbox
:设置嵌入的网页的权限,详见下文。
importance
:浏览器下载嵌入的网页的优先级,可以设置三个值。high
表示高优先级,low
表示低优先级,auto
表示由浏览器自行决定。
嵌入的网页默认具有正常权限,比如执行脚本、提交表单、弹出窗口等。如果嵌入的网页是其他网站的页面,你不了解对方会执行什么操作,因此就存在安全风险。
为了限制的风险,HTML 提供了
sandbox
属性,允许设置嵌入的网页的权限,等同于提供了一个隔离层,即“沙箱”。
sandbox
可以当作布尔属性使用,表示打开所有限制。
- <iframe src="https://www.example.com" sandbox>
- iframe>
sandbox
属性可以设置具体的值,表示逐项打开限制。未设置某一项,就表示不具有该权限。
以最小权限为原则
注意,不要同时设置allow-scripts
和allow-same-origin
属性,这将使得嵌入的网页可以改变或删除sandbox
属性。
指定的网页会立即加载,有时这不是希望的行为。
滚动进入视口以后再加载,这样会比较节省带宽。
loading
属性可以触发网页的懒加载。
该属性可以取以下三个值:
auto
:浏览器的默认行为,与不使用loading
属性效果相同。
lazy
:的懒加载,即将滚动进入视口时开始加载。
eager
:立即加载资源,无论在页面上的位置如何。
<iframe src="https://example.com" loading="lazy">iframe>
上面代码会启用的懒加载。
有一点需要注意,如果是隐藏的,则
loading
属性无效,将会立即加载。
一定程度上防御crsf(客户端请求伪造)
Referer请求包含了当前请求页面的来源页面的地址,即表示当前页面是通过此来源页面的链接进入的。
Referer-policy作用就是为了控制请求头部中的referer的内容包含了以下信息:
no-referer:整个referer首部会被移除,访问来源信息不随着请求一起发送。
no-referer-when-downgrade:在没有指定任何策略的情况下用户代理的默认行为。在同安全等级的情况下,发送文件的源为引用地址(HTTPS->HTTPS);在降级的情况下不会发送此首部(HTTPS->HTTP)
origin:在任何情况下,仅发送文件的源作为引用地址
orgin-when-cross-origin:对于同源的请求,会发送完整的URL作为引用地址,但是对于非同源请求仅仅发送文件的源
same-orgin:对于同源的请求会发送引用地址,但是对于非的同源请求则不发送引用地址信息。
strict-origin:在同等安全级别的情况下,发送文件的源作为引用地址(HTPPS->HTTPS),但是在降级的情况下不会发送(HTTPS->HTTP)
strict-origin-cross-origin,对于同源的请求,会发送完整的URL作为引用地址;在同安全等级的情况下,发送文件的源为引用地址(HTTPS->HTTPS);在降级的情况下不会发送此首部(HTTPS->HTTP)
unsafe-url:无论是同源请求还是非同源请求,都发送完整的URL(移除参数信息后)作为引用地址。(最不安全了)
方法1:在html页面中的head标签中的meta中设置
<meta name="referer" content="no-referer">
方法2:在引入script的script标签的属性中设置referrerpolicy="no-referer"
(1)首先编写referer1.html
- html>
- <html lang="en">
-
- <head>
-
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- head>
-
- <body>
-
- <div>aaadiv>
- body>
-
- html>
这里并没有什么特别的,就是在页面中有一个div标签,其中写着aaa
(2)编写referer2.html
- html>
- <html lang="en">
-
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- head>
- <h1>hello worldh1>
- <a href="./referer1.html">123a>
- 这里使用链接的形式访问test.html网页
- <body>
- body>
-
- html>
这里使用a标签尝试将referer1.html插入到a标签中
(3)测试
在浏览器的URL中访问referer2.html
在referer中增加这样一行:
<meta name="referrer" content="no-referrer">
然后使用上面相同的方式查看是否有referer
可以看到,这里已经没有referer了
(1)修改referer1页面为:
- html>
- <html lang="en">
-
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- head>
-
- <body>
- <script src="./test.js" Referrerpolicy="no-referrer">
- //这里使用no-referrer禁用referrer
- script>
-
- <div>aaadiv>
- body>
-
- html>
(2)然后我们新建一个名为test.js的文件
内容为:
alert(document.cookie);
(3)然后访问referer1.html
可以看到,没有referer,由于没有了referer我们也可以看到成功的弹窗了
浏览器与服务器之间,采用 HTTP 协议通信。用户在浏览器地址栏键入一个网址,或者通过网页表单向服务器提交内容,这时浏览器就会向服务器发出 HTTP 请求。
XMLHttpRequest
对象是 AJAX 的主要接口,用于浏览器与服务器之间的通信。尽管名字里面有XML
和Http
,它实际上可以使用多种协议(比如file
或ftp
),发送任何格式的数据(包括字符串和二进制)。
XMLHttpRequest
本身是一个构造函数,可以使用new
命令生成实例。它没有任何参数。
var xhr = new XMLHttpRequest();//即初始化时没有任何参数
一旦新建实例,就可以使用open()
方法指定建立 HTTP 连接的一些细节。
xhr.open('GET', 'http://www.example.com/page.php', true);//open方法
上面代码指定使用 GET 方法,跟指定的服务器网址建立连接。
第三个参数true
,表示请求是异步的。
然后,指定回调函数,监听通信状态(readyState
属性)的变化。
- xhr.onreadystatechange = handleStateChange;
-
- function handleStateChange() {
- // ...
- }
上面代码中,一旦XMLHttpRequest
实例的状态发生变化,就会调用监听函数handleStateChange
最后使用send()
方法,实际发出请求。
xhr.send(null);
上面代码中,send()
的参数为null
,表示发送请求的时候,不带有数据体。
如果发送的是 POST 请求,这里就需要指定数据体。
一旦拿到服务器返回的数据,AJAX 不会刷新整个网页,而是只更新网页里面的相关部分,从而不打断用户正在做的事情。
注意:AJAX 只能向同源网址(协议、域名、端口都相同)发出 HTTP 请求,如果发出跨域请求,就会报错
盗链是指自己的页面上展示以下并不在自己服务器上的一些内容,获别人的资源地址,绕过别人的资源展示页面,直接在自己的页面上向最终用户提供此内容,一般被盗链的都是图片、可执行文件、音频文件、压缩文件等资源。
通过盗链的手段可以减轻自己服务器的负担。
通过referere或者签名,网站可以检测目标网页访问的来源网页,如果是资源文件,则可以追踪到显示它网页地址,一旦检测到来源不是本站点,即进行阻止或者返回指定的页面
因为通过referere或者签名,网站可以检测目标网页访问的来源网页,如果是资源文件,则可以追踪到显示它网页地址,一旦检测到来源不是本站点,即进行阻止或者返回指定的页面
三种情况下允许引用图片:
本网站
无referer信息的情况下。(服务器认为从浏览器直接访问图片的图片的URL,所以这种情况下能正常访问)
授权的网址
我们可以在https的网页中用http请求另一个https网站的资源。
此时可以不发送我们的referer字段,达到绕过防盗链的效果(由于浏览器的升级,现在这种操作已经被禁止了)
实现原理:在访问图片时让其无referrer
现在这种方式已经无法复现了
(1)首先在refrrer1文件中添加一行
<img src="https://img-blog.csdnimg.cn/5ff039c8a8cd47c98e26d694854e8962.jpeg" alt="你这图有问题啊">
(2)尝试访问
很明显图片没有加载出来,因为csdn网站将发现我们的referer并不是一个合法的网站,不会让我们加载
(3)那么我们就可以在meta中设置no-referrer的方式来尝试
<meta name="referrer" content="no-referrer">
(4)再次访问
成功访问
注:这种方式就是在script添加这样
<script src="这里是一个js页面" Referrerpolicy="no-referrer">
一句,呈现的效果和上面的meta一样,这里不再赘述
内容参考:如何绕开referrer防盗链 - 掘金 (juejin.cn)
(1)修改referrer1.html
"https://t7.baidu.com/it/u=4162611394,4275913936&fm=193&f=GIF" alt="你这图有问题啊">
(2)编辑test.js
- function showImg(src, wrapper) {
- let url = new URL(src);
- let frameid = 'frameimg' + Math.random();
- window.img = `${url}" alt="图片加载失败,请稍后再试"/> `;
-
- // 构造一个iframe
- iframe = document.createElement('iframe')
- iframe.id = frameid
- iframe.src = "javascript:parent.img;" // 通过内联的javascript,设置iframe的src
- // 校正iframe的尺寸,完整展示图片
- iframe.onload = function () {
- var img = iframe.contentDocument.getElementById("tmpImg")
- if (img) {
- iframe.height = img.height + 'px'
- iframe.width = img.width + 'px'
- }
- }
- iframe.width = 200
- iframe.height = 200
- iframe.scrolling = "no"
- iframe.frameBorder = "0"
- wrap.appendChild(iframe)
- }
- showImg('https://t7.baidu.com/it/u=4162611394,4275913936&fm=193&f=GIF', document.querySelector('#container'));
-
运行代码可以看见,通过这种方式也可以实现隐藏referer的功能,因此用作不支持referrerPolicy
的一种替代方案。
在某些不支持javascript内联运行的场景下,这种方案也是不可行的,比如在chrome扩展程序,由于content_security_policy
,使用内联JavaScript会报错
(3)测试
成功盗用!
XMLHttpRequest对象提供了setRequestHeader方法,用于向请求头添加或修改字段。我们能不能手动将修改 referer字段呢?
(1)test.js
- // 通过ajax下载图片
- function loadImage(uri) {
- return new Promise(resolve => { //使用异步下载
- let xhr = new XMLHttpRequest(); //实例化xmlhttprequest
- xhr.responseType = "blob"; //响应类型为blob(二进制)
- xhr.onload = function () {
- resolve(xhr.response);
- };
-
- xhr.open("GET", uri, true);
- xhr.setRequestHeader("Referer", "");
- // 通过setRequestHeader设置header不会生效
- xhr.send();
- });
- }
-
- // 将下载下来的二进制大对象数据转换成base64,然后展示在页面上
- function handleBlob(blob) {
- let reader = new FileReader();
- reader.onload = function (evt) { //监听事件
- let img = document.createElement('img');
- img.src = evt.target.result;
- document.getElementById('container').appendChild(img)
- };
- reader.readAsDataURL(blob);
- }
-
- const imgSrc = "https://profile-avatar.csdnimg.cn/8f0b72eabecc42df84ef2cce29ed569a_qq_68163788.jpg!1";
- loadImage(imgSrc).then(blob => {
- handleBlob(blob);
- });
(2)referrer1.html
- "container">aaa
- <script src="./test.js">script>
(3)测试
上述代码运行时会发现控制台提示错误
根据结果可以看出,浏览器拒绝将Rerrer置为空
可以看见setRequestHeader设置referer响应头是无效的,这是由于浏览器为了安全起见,无法手动设置部分保留字段,不幸的是Referer恰好就是保留字段之一,详情列表参考Forbidden header name。
可见使用xmlhttprequest提供的方法用AJAX同源请求无法完成这一操作。
使用fetch可以解决这一问题。
Fetch是浏览器提供的一个全新的接口,用于访问和操作HTTP管道部分,例如请求和响应,该接口支持referrerPolicy
,因此也可以用来操作referer。
- // 将下载下来的二进制大对象数据转换成base64,然后展示在页面上
- function handleBlob(blob) {
- let reader = new FileReader();
- reader.onload = function (evt) {
- let img = document.createElement('img');
- img.src = evt.target.result;
- document.getElementById('container').appendChild(img)
- };
- reader.readAsDataURL(blob);
- }
-
- const imgSrc = "https://img-blog.csdnimg.cn/5ff039c8a8cd47c98e26d694854e8962.jpeg";
-
-
- function fetchImage(url) {
- return fetch(url, {
- headers: {
- // "Referer": "", // 这里设置无效
- },
- method: "GET",
- referrer: "", // 将referer置空
- // referrerPolicy: 'no-referrer',
- }).then(response => response.blob());
- }
-
- fetchImage(imgSrc).then(blob => {
- handleBlob(blob);
- });
通过将配置参数redirect
置位空,可以看见本次请求已经不带referer了。