谈谈如何实现网站静态资源文件的防盗链

by LauCyun Jan 9,2018 19:32:59 20,361 views

近日,通过日志发现有很多其它网站偷偷使用我站的静态资源文件,放置在自己网站的网页中,通过这种方法盗取空间和流量。本文将介绍如何通过防盗链来避免这样情况的发生。

实验环境:

  • Web框架:Flask
  • 中间件:Nginx

为了实现防盗链,用了以下两种方法:

  • 通过判断Referer实现防盗链。
  • 通过判断Cookie实现防盗链。

在介绍第一种方法之前,先介绍一下什么是Referer?

1 Referer简介

简单来说,Referer是HTTP Header的一部分,当浏览器向网站Web服务器发送请求的时候,一般会带上Referer,告诉服务器此次请求是从哪个页面链接过来的。

举个栗子,假如一个域名为b.laucyun.com的网站,想盗链laucyun.com的图片链接https://laucyun.com/static/upload/cdn/jetbrains/jetbrains-license-server-activating.png

假设盗链网站b.laucyun.com的网页如下:

<!DOCTYPE html>
<html>
    <p>This is a test</p>
    <img src="//laucyun.com/static/upload/cdn/jetbrains/jetbrains-license-server-activating.png" />
</html>

用浏览器访问http://b.laucyun.com,网页里链接是laucyun.com的图片。由于从一个域名(b.laucyun.com)请求跳到了另一个域名(laucyun.com),浏览器就会在HTTP请求的Header中带上Referer,如下图所示:

可以看到浏览器在HTTP请求中的Referer为http://b.laucyun.com

如果浏览器直接输入地址,可以看到,请求中的Referer为空,如下图所示:

另外要注意的是,Referer是由浏览器自动为我们加上的,以下情况是不带Referer的

  • 直接输入网址或通过浏览器书签访问
  • 使用JavaScriptLocation.href或者是Location.replace()
  • HTTPS等加密协议

2 通过判断Referer实现防盗链

通过上面的例子可以看到,浏览器在请求静态资源的时候,如果发生页面跳转,浏览器会在请求中带入Referer,此时Referer的值为上一页面的URL,有的时候Referer也会为空。所以通过判断Referer即可实现防盗链。

这个方法是最早及最常见的方法。先判断http的referer字段的值,如果是从白名单内的域名过来的,则是合法的连接请求,否则就返回一个错误的提示信息。

以Flask框架和网站http://jetbrains.license.laucyun.com/(其图片是引用laucyun.com的资源)为例,在Flask框架中有一个方法before_request(),它的作用就是在每次请求之前运行的一个函数钩子,所以在laucyun.com中加上对referer的判断,代码如下:

@app.before_request
def before_request():
    try:
        url = request.url
        url = url[url.find('/', url.find('/', url.find('/') + 1) + 1):]
        # referer
        referer = request.headers.get("Referer")
        # prevent hotlinking
        if not (url in [
            "/static/upload/cdn/jetbrains/jetbrains-license-server-activating.png",
            "/static/upload/cdn/jetbrains/jetbrains-license-server-activated.png"
        ] and referer == "http://jetbrains.license.laucyun.com/"):
            raise Exception("forbidden")
    except Exception as e:
        logger.exception(e)
        if str(e) == "forbidden":
            abort(403)

来看一下其效果,如下图所示:


白名单之内的请求结果,成功


白名单之外的请求结果,失败(403错误)

由此可见,白名单之内即可请求成功;之外的请求失败,返回403。

3 通过Cookie实现防盗链

当访客请求网站上的一个资源时,先判断cookie里有没有正确的cookie,如果没有则返回错误提示信息。

以Flask框架和网站https://laucyun.com为例,在Flask框架中的before_request()钩子加上对cookie的判断,代码如下:

@app.before_request
def before_request():
    try:
        url = request.url
        url = url[url.find('/', url.find('/', url.find('/') + 1) + 1):]
        # cookie
        cookie = request.cookies
        # prevent hotlinking
        if url[:8] == "/static/":
            if not cookie:
                return True
            else:
                raise Exception("forbidden")
    except Exception as e:
        logger.exception(e)
        if str(e) == "forbidden":
            abort(403)

来看一下访问资源文件https://laucyun.com/static/public/plugins/jquery/jquery-1.12.4.min.js的效果,如下图所示:


带cookie的请求结果,成功


不带cookie的请求结果,失败(403错误)

由此可见,带cookie且cookie值有效的即可请求成功;否则请求失败,返回403。

4 参考

Tags