• 防盗链(一)资料整理


    背景

    防盗链,其本质就是用户对于自己的资源设置的访问控制,控制“谁”可以在“什么时间”访问到“什么资源”。不做防盗链,用户的许多资源都为其他人做了嫁衣,也会给自己的服务器增加不必要的访问压力和带宽消耗。

    不同的用户,由于网站的性质不同(游戏/新闻/游戏),需求也是不尽相同的,所以需要在我们的portal系统中添加访问控制的功能,满足用户的需要。

    业务功能

    防盗链生效配置

    表示在什么情况下需要进行防盗链逻辑。比如我们需要对 xxx.com/image/ 下面的URL进行防盗链处理,而对 xxx.com/js/ 则不需要防盗链。

    是否进行防盗链,一般通过2种方式进行匹配:

    • URL匹配
    • 类型匹配

    URL匹配是指,符合某些前缀或者正则表达式的URL进行防盗链,见上例; 类型匹配,就是对某些特定类型的资源进行防盗链,例如jpg、mp3。

    防盗链规则配置

    IP黑白名单

    只允许或者阻止某些IP访问资源,一般都是IP黑名单的形式。

    用户可以在界面中输入单个IP、IP区间、或者IP通配符,例如:

    1.2.3.4
    192.168.199.1 ~ 192.168.199.100
    192.168.1.*
    

    个人理解是我们通过分析以往的访问日志,得出某些IP的访问可能有异常,之后将其加入黑名单。IP黑名单的限制范围既可以是全局范围(我们CDN服务提供商配置),也可以是域名范围(CDN服务使用方配置)。


    Referer黑白名单

    最常用的防盗链手段,通过对HTTP请求中的referer header进行判断,以决定用户是否可以访问该资源。一般都是referer白名单的形式。

    需要支持单个域名以及泛域名的形式,例如:

    ent.cankaoxiaoxi.com
    *.cankaoxiaoxi.com
    public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 获取请求是从哪里来的
            String referer = request.getHeader("referer");
            // 如果是直接输入的地址,或者不是从本网站访问的重定向到本网站的首页
            if (referer == null || !referer.startsWith("http://localhost")) {
                response.sendRedirect("/day06/index.jsp");
                // 然后return,不要输出后面的内容了
                return;
            }
            String date = "日记";
            response.getWriter().write(date);
        }
     
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    禁止空Referer
    禁止空referer,即不允许用户直接访问该资源,因为直接在浏览器的地址栏中输入一个资源的URL地址,请求是不会包含Referer字段的。

    个人认为使用场景并不多。

    UA白名单

    HTTP请求header中的User Agent字段,是一段浏览器或者设备标识自己的字符串。对于网站主来说,有时需要让一些资源只能在某些浏览器或者设备上才能访问。

    UA 防盗链通常用在手机 APP 或者一些可自定义 User Agent 的应用,比如播放器。设置UA白名单,也和其他情况类似,支持字符串通配符。

    Java通过浏览器请求头(User-Agent)获取 浏览器类型,操作系统类型,手机机型
    
    User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
    一些网站常常通过判断 UA 来给不同的操作系统、不同的浏览器发送不同的页面,因此可能造成某些页面无法在某个浏览器中正常显示,但通过伪装 UA 可以绕过检测。
    
    一:获得浏览器请求头中的User-Agent
    String ua = request.getHeader("User-Agent")
    
    二:获得浏览器类型,操作系统类型:(注意,UserAgent类在UserAgentUtils.jar中,自行下载)
    UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));  
           Browser browser = userAgent.getBrowser();  
           OperatingSystem os = userAgent.getOperatingSystem();
    
    三:获得手机类型:
    
    方案一:正则表达式
    
    通过观察规律,得出以下表达式:
    ;s?([^;]+?)s?(Build)?/
    
    Java代码:
    Pattern pattern = Pattern.compile(";\s?(\S*?\s?\S*?)\s?(Build)?/");  
                Matcher matcher = pattern.matcher(userAgent);  
                String model = null;  
                if (matcher.find()) {  
                    model = matcher.group(1).trim();  
                    log.debug("通过userAgent解析出机型:" + model);  
                }
    
    以下为部分UserAgent,供测试,可以直接在EditPlus里验证。
    通过验证,成功率95%以上。
    
    

    方案二:开源类库WURFL

    地址:https://wurfl.sourceforge.net/apis.php

    
    

    在线测试地址:https://tools.scientiamobile.com/

     
    Token加密串
    Token加密串,是通过资源URL、密钥、以及过期时间生成的一个加密字符串,然后外链必须要带上这个 Token 才能在规定的时间内访问到该资源。

    token一般会在请求参数或者cookie中,没有带 token 的外链,或者超过了有效期的token链接都会返回403,达到防盗链的目的。

    Token 加密串这种方案,一般用于文件下载的场景比较多,静态图片的场景则较少。

    盗链提示
    当盗链发生时,一般会在页面显示出一个固定的文本或者图片,用于提示用户该资源为盗链。

    在系统中,用户需要有界面能够上传自定义的盗链提示图,甚至是不同的HTTP返回码可以设置不同的盗链提示图。

    相关技术

    nginx conf

    nginx本身已经提供了丰富的功能,通过获取HTTP请求中的各个字段,判断请求是否合法,可以实现以上提到的URL匹配、类型匹配、IP黑白名单等功能。

    比如,可以获取client ip,来决定是否请求upstream,以此达到IP黑白名单的目的,样例:

    ## If IP is 1.2.3.4 send backend to apachereadwrite ##
    if ( $remote_addr ~* 1.2.3.4 ) {
        proxy_pass http://cdn_backend;
    }

    也可以判断server_name,来达到域名黑白名单的效果,样例:

    复制代码
    # conf/nginx.conf
    
    server {
        listen       80;
        include      /home/uaq/local/static_dynamic_proxy/nginx/conf/static_server_names.conf;
    
        #charset koi8-r;
    
        #access_log  logs/host.access.log  main;
    
        location ~ {
            proxy_pass              http://static_accelerate;
        }
    
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    复制代码
    复制代码
    # conf/static_server_names.conf;
    
    server_name
        a.com
        b.com
        ;
    复制代码

    nginx module

    ngx_http_referer_module,可以专门用于判断referer是否合法、或者是否是空的referer,样例:

    复制代码
    location ~* .(gif|jpg|png|bmp)$ {
        valid_referers none blocked *.mmtrix.com server_names ~.google. ~.baidu.;
        if ($invalid_referer) {
            return 403;
        }
    }
    复制代码

    ngx_http_secure_link_module,用于检测访问资源者的授权,以及资源的有效时间,可以用于Token加密串的防盗链,样例:

    复制代码
    server {
    
        listen       80;
        server_name  cdn.aaa.com;
        access_log  /data/logs/nginx/access.log  main;
    
        index index.html index.php index.html;
    
        location / {
            secure_link $arg_st,$arg_e;
            secure_link_md5 some_private_key$uri$arg_e;
    
            if ($secure_link = "") {
                return 403;
            }
    
            if ($secure_link = "0") {
                return 403;
            }
        }
    }
    复制代码

    nginx + lua

    虽然nginx的功能已经比较强大,但是不够灵活,而且在conf中有过多的配置,也会占用较多内存、影响nginx处理请求的时间。我们既然作为CDN服务提供商,除了全局的配置,还需要为每个用户的每个域名进行自定义的配置,因为将所有条件写入nginx conf是不现实的。另外,lua还提供更多强大功能,例如请求时访问redis、memcached,和其他实例共享状态等等功能。因此,nginx + lua可能是一个不错的解决方案。

    网上的cookie token加密串的样例:

    复制代码
    -- Some variable declarations.
    local cookie = ngx.var.cookie_MyToken
    local hmac = ""
    local timestamp = ""
    
    -- Check that the cookie exists.
    if cookie ~= nil and cookie:find(":") ~= nil then
        -- If there's a cookie, split off the HMAC signature
        -- and timestamp.
        local divider = cookie:find(":")
        hmac = cookie:sub(divider+1)
        timestamp = cookie:sub(0, divider-1)
    
        -- Verify that the signature is valid.
        if hmac_sha1("some very secret string", timestamp) == hmac and tonumber(timestamp) >= os.time() then
            return
        end
    end
    
    -- Internally rewrite the URL so that we serve
    -- /auth/ if there's no valid token.
    ngx.exec("/auth/")
    复制代码

    网上查找到的各种与nginx+lua相关的方案,基本上都还是单机版本的解决方案。考虑到我们分布式环境,以及业务复杂的情况,还需要用到分布式存储、缓存、任务分发等技术。

     

  • 相关阅读:
    51 张图助你彻底掌握 HTTP
    Nginx从原理到实战
    vu3.0 + ts + swiper6 的问题
    使用 react-router-dom v5 查询query 参数的方法
    visual studio 2015配置SVN
    SVN使用教程总结
    C#与SAP进行数据交互
    shell csv/txt文件对比
    persto array_join(array_agg(),',')
    shell 拼接html table 发送邮件
  • 原文地址:https://www.cnblogs.com/youqc/p/14428263.html
Copyright © 2020-2023  润新知