防盗链的必要性,我这里就不再赘述了,这是网站设计的最基本要求。而在nginx中,一般比较容易实现的防盗链手段就是通过ungx_http_referer_module模块(查看官方文档) 检查访问请求的referer信息是否有效来实现防盗链功能。
  所谓referer检查,举个例子来说,在正常情况下当用户在浏览http://example.com/abc.html时点击一个链接去到http://example.com/123.mp3文件时,浏览器在发出请求123.mp3 资源时还会附带当刻浏览器所处的页面地址(即http://example.com/abc.html),所以当你的网站程序接收到下载 jacky.mp3 资源请求的时候,先判断http的referer字段的值,如果是从 自己的域名(example.com)过来的,则可以认为是合法的连接请求,否则就返回一个错误的提示信息。

  这种方法通常用于图片、mp3这种容易被人用html“嵌入”到其他网站的资源,使用这种方法可以防止你的图片直接出现在别人的网页里(或者防止mp3直接被其他网站嵌入到flash播放器里),不过访客使用下载工具还是可以轻松下载,因为现在的下载工具一般会自动用你的域名构造一个引用地址,所以如果想再进一步防范的话,可以使用一个对应表限制每个资源的引用地址,例如将 123.mp3 的引用地址限制为http://example.com/abc.htmlid=123456,这样下载工具就不太可能构造一个“正确”的引用地址了。

referer

  要过滤掉盗链访问的referer信息,首先要明确知道,正常访问的referer有哪些。一般来说,正常的referer信息有以下四种:

  • none:请求报文首部没有referer首部,比如用户直接在浏览器输入域名访问web网站,就没有referer信息。
  • blocked:请求报文有referer首部,但无有效值,比如为空。
  • 本站链接:referer首部中包含本站域名。
  • 搜索引擎跳转:referer中为 * .baidu. * 、 * .google. * 、及其他搜索引擎(如360、必应)(具体图片或mp3媒体文件,不希望被搜索引擎引用,可单独设置,主页等html页面建议允许搜索引擎跳转)
      所以根据官方文档,我们只需制定合适的匹配规则,将正常的访问放过,对那些“非正常的”盗链访问,返回403错误代码,即可实现防盗链。
    valid_referers none blocked server_names
                   *.example.com example.* www.example.org/galleries/
                   ~\.google\.;
    
    if ($invalid_referer) {
        return 403;
    }

    过滤规则设置

      打开nginx配置文件,找到想要定义的location下,加入下面设置
          location  /blog/ {
              root /apps/nginx/html/;    #定义路径
              valid_referers none blocked server_names *.example.com  ~\.google\. ~\.baidu\.;  
              #设置允许访问的匹配规则,匹配规则可以写在一行,也可以分行写。
              if ($invalid_referer) {     #设置条件判断,不符合上述规则的,返回403状态码
                  return 403;
              }
          }
          location ^~ /mp3/ {
              alias /apps/nginx/html/blog/mp3/;    #定义路径,也可用root
              valid_referers none blocked server_names *.example.com ;   
              if ($invalid_referer) {     #设置条件判断,不符合上述规则的,返回403状态码
                  return 403;
              }
          }
      也可在全局配置server中做设置,不过还是建议每个location单独设置,因为对于图片和或者音频视频文件本身,还是不希望直接被搜索引擎所引用,造成网站资源的无意义的消耗。

    跳转设置

      对于盗链者,也可以予以反击,允许他们请求我们的资源,不过,只给他们我们指定的资源,例如百度使用的防盗链图:

  在这里插入图片描述
  配置上只需将return 403;改为rewrite ^/ http://www.example/images/return.jpg; 例如:

        location  ~ return\.jpg$ {
            root /apps/nginx/html/blog/images/
        }
        location  ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
            access_log off;
            root /apps/nginx/html/blog/images/; 
            valid_referers none blocked server_names *.example.com  ~\.google\. ~\.baidu\.; 
            if ($invalid_referer) { 
                rewrite ^/ http://www.example/images/return.jpg;
                            }
        }

  PS:return.jpg要设置规则优先匹配到,这个图片不能被防盗链,不然会无限重定向,导致显示不正常。

规则细节

  设置匹配规则时,根据官方文档,只有none、blocked、server_names、arbitrary string和regular expression五种规则。

Parameters can be as follows:

none
  the “Referer” field is missing in the request header;
blocked
  the “Referer” field is present in the request header, but its value has been deleted by a firewall or proxy server; such values are strings that do not start with “http://” or “https://”;
server_names
  the “Referer” request header field contains one of the server names;
arbitrary string
  defines a server name and an optional URI prefix. A server name can have an “* ” at the beginning or end. During the checking, the server’s port in the “Referer” field is ignored;
regular expression
  the first symbol should be a “~”. It should be noted that an expression will be matched against the text starting after the “http://” or “https://”.

  这就要求我们在设置匹配规则的时候,要按照这个五种方式来,none、blocked直接写上就可以了,没有什么可说的,我们重点理解下剩下三种。

server_names

  server names字面上理解很容易,就是匹配的域名。注意:这里的域名,指本服务器上所有监听的域名。而且这是一个包含的关系,只要referer头部信息中包含有本服务器的监听的任意域名,即可通过匹配。

arbitrary string

  翻译过来是任意字符串,其实就是任意可以匹配到到字符串,这里支持通配符。大致有2种写法:

  • 直接写域名
    例如可以写*.example.com,也可写为www.example.*,可问题是为什么就偏偏不支持 * .example. * 呢。这我也很费解,不过确实不支持,有兴趣的朋友可以去试一下,也希望能有大佬告知这其中的原理是什么。
    [root@CentOS8 ~]#/apps/nginx/sbin/nginx -t
    nginx: [emerg] invalid hostname or wildcard "*.example.*" in /apps/nginx/conf/nginx.conf:95
    nginx: configuration file /apps/nginx/conf/nginx.conf test failed
  • 定义匹配域名加路径
      例如:www.example.com/blog;
      而博主试验过很很多次,如果写成例如www.example.com/*,在www.example.com/blog/页面下去引用页面下的/apps/nginx/html/mp3/123.mp3文件时就会报403错误,而写域名加确切地址如www.example.com/blog时才可以访问。仔细查阅了官方文档,才知道,有个很关键的细节就是,这个通配符的位置,只能在域名里。可以再看一下官方文档,

      defines a server name and an optional URI prefix. A server name can have an “*” at the beginning or end. During the checking, the server’s port in the “Referer” field is ignored;

  我们可以得知,只可以在域名的开头和结尾用 * 的通配符,而不是URI中,这也就是为什么我发现www.examlpe.com/*无法匹配通过的原因。
  跟server_names一样,只要包含自定义字符串就可以,例如匹配规则写成www.example.com/mp3/,在www.example.com/mp3/页面下就可以引用的/apps/nginx/html/mp3/123.mp3文件了,在www.example.com/mp3/abc/efg/页面下是同样可以跳转访问/apps/nginx/html/mp3/123.mp3文件的。

regular expression

  被指定的正则表达式模式匹配到的字符串,要使用 ~ 开头,例如:~.*.google.com。这要严格按照正则表达式匹配到的referer写,否则就会无法访问。

总结

  设置匹配规则时,必须符合其中的某一种,而不能想当然的把几种规则混合起来使用,想要放行的链接,一定要考虑好,到底确切适用于哪一种规则,才不会出现“误伤“、“漏网”的情况。


一个低调的男人