绕过防盗链加载图片

之前写 blog,一直是用的 Gitee 的图床。原因是不想自己去购买 CDN 服务器,同时也想减少自己服务器的压力。突然有一天,网站中使用 gitee 的图片崩溃了,返回我们一个 gitee 的小 logo 图片。奇怪的是,单独访问这个图片 url,又能正常访问。起初以为是服务器异常,过两天自动就好了,但是过了好几天,仍然是这样,感觉事情不太对劲。然后查了一下,果然,很多人也遇见了这个问题。

为什么会这样?

由于许多人发现了国内使用 gitee 图床访问速度快,使用起来也方便,所以都在自己的项目中,使用 gitee 图床作为自己的 CDN 服务器。少一点人使用没啥问题,忍了。但是大家开始滥用它,gitee 也遭不住啊。所以加入了 防盗链 功能。

什么是防盗链?

简单理解就是防止 OSS 资源被别人盗用。目前 阿里云腾讯云 都支持配置 黑名单白名单 来开启使用。

实现原理?

HTTP 协议规范定义了在请求中加入 Referrer 字段,用于表示请求来源。该字段由浏览器在发起 HTTP 请求时指定。比如我们访问一篇文章,这篇文章中有一些图片,查看 Request Headers

绕过防盗链加载图片

可以看到这个里面有 Referrer,它表示这个图片是从哪个网站的哪个页面请求的。

所以通过对 HTTP 请求头中的 Referrer 判断可以知道这条请求是正常访问的还是来自其他网站。

如何绕过?

前面简单说了一下 什么是防盗链 以及它的 实现原理。那对于上面提到的那种,在网站中不能使用,但是直接访问 url 又可以使用的这种方式我们可以怎么绕过并继续使用它吗?

查看 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Referrer-Policy 可以发现 Referrer 有下面几类值:

  • no-referrer:整个 Referer 首部会被移除。访问来源信息不随着请求一起发送。
  • no-referrer-when-downgrade(默认值):在没有指定任何策略的情况下用户代理的默认行为。在同等安全级别的情况下,引用页面的地址会被发送 (HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP)。
  • origin:在任何情况下,仅发送文件的源作为引用地址。
  • origin-when-cross-origin:对于同源的请求,会发送完整的 URL 作为引用地址,但是对于非同源请求仅发送文件的源。
  • same-origin:对于同源的请求会发送引用地址,但是对于非同源请求则不发送引用地址信息。
  • strict-origin:在同等安全级别的情况下,发送文件的源作为引用地址 (HTTPS->HTTPS),但是在降级的情况下不会发送 (HTTPS->HTTP)。
  • strict-origin-when-cross-origin:对于同源的请求,会发送完整的 URL 作为引用地址;在同等安全级别的情况下,发送文件的源作为引用地址 (HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。
  • unsafe-url:无论是同源请求还是非同源请求,都发送完整的 URL(移除参数信息之后)作为引用地址。

我们只要将 Referrer 的值设置成 no-referrer 或者 same-origin 就可以了。

那具体怎么做呢?

方法一

设置 meta 标签的 content 值。

<meta name="referrer" content="no-referrer" />

这样设置后,图片果然能够正常访问了。

但是:这样设置后,发现 百度统计 无效了。原因是百度统计也需要根据 referrer 值来判断用户是从哪里进入的,访问了哪些页面。

怎么办?

方法二

设置图片的 referrerpolicy。

<img src="https://....jpg" alt="图片" referrerpolicy="no-referrer" />

不错,单个图片可以这样设置。但是对于一些文章中使用了图片呢?

如果文章的内容是 富文本。那在渲染前,我们可以通过正则 str.replace(/<img/g, '<img referrerpolicy="no-referrer"') 等代码方式先将 富文本 中的 <img ... /> 中先添加一下 referrerpolicy="no-referrer" 属性后再渲染。

如果文章的内容是 “一颗树”。在代码中通过第三方库来渲染的,或者其它不方便在渲染前拿到文章内容的情况,比如 nuxt-content,这时我们应该怎么办呢?方法也简单,查询出所有的 img 标签,给它们加上 referrerpolicy="no-referrer" 属性。

const imgs = document.querySelectorAll('img');
imgs.forEach(img => {
  img.setAttribute('referrerpolicy''no-referrer');
  img.setAttribute('src', `${img.src}`);
})

这样写后,页面中访问的还是裂开的图片,因为图片没有再请求,怎么办?

给图片的 url 加上一个随机参数,利用浏览器的特性,图片 url 发生改变,就会重新请求图片资源的方式。

const imgs = document.querySelectorAll('img');
imgs.forEach(img => {
  img.setAttribute('referrerpolicy''no-referrer');
  img.setAttribute('src', `${img.src}?imgRandom=${Date.now()}`);
})

但这存在一个问题,url 每次都不一样,没办法触发浏览器缓存功能。每次都要请求一次,特别耗资源。

优化

updateImgProps() {
    let imgRandom = localStorage.getItem('imgRandom');
    if (!imgRandom) {
      imgRandom = Date.now();
      localStorage.setItem('imgRandom', imgRandom);
    }
    const imgs = document.querySelectorAll('img');
    imgs.forEach(img => {
      img.setAttribute('referrerpolicy''no-referrer');
      img.setAttribute('src', `${img.src}?imgRandom=${imgRandom}`);
    })
}

localStorage 中存一个随机生成的变量,有的话直接读取并使用,没有的话就生成一个并保存在 localStorage 中,以便下次使用。这样既改变了图片的 url 以触发浏览器重新请求 url,同时如果之前访问过,浏览器中有缓存,浏览器会使用缓存中的图片资源,减少 HTTP 请求。

总结

通过上面的学习研究,更加了解了 Referrer 的工作方式以及它的其它值及作用。

以上方法只是为了探索解决方法,并不推荐使用。最好还是使用自己的 CDN 服务器资源,别浪费公共服务器带给大家的便利。又或者万一哪天防盗链机制变了,设置白名单才能访问,那不管怎么设置都是无用的。因此造成的一些损失,也只能自己承担。



原文始发于微信公众号(前端学习总结):绕过防盗链加载图片

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83074.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!