首页 > java > 使用java过滤非法盗链动态生成的图片

使用java过滤非法盗链动态生成的图片

我们知道验证码其实是由一个servlet或其他脚本动态生成的图片,生成图片是比较消耗服务器资源的,在不安全的互联网世界中我们还是需要简单防御一下的DDOS攻击,不过在这之前我先介绍一下如何发动一次DDOS攻击。

这里拿网易邮箱的验证码注册为例,他们并没有没有对referer作判断估计是对自己强大的服务器集群信心十足!

如果某个热门论坛允许引用外部图片(下面这张图片就是引用网易的验证码),Look:

(图一,网易邮箱注册页面的验证码地址,虽然做的比较细腻,但是没有我之前写的那个验证码好看)

攻击代码如下,创建一个img标签设置src属性即可:

<img src="http://reg.email.163.com/unireg/call.do?cmd=register.verifyCode&t=1462454878305"/>

仔细分析网易给出的Response Headers可以看出我们并没有设置缓存,后面的时间参数可以省略了。

也就是说如果该热门帖子拥有数十万PV的浏览量,那么产生的网络流量达到400MB+消耗。

如果是某个黑客通过XSS取得了引用外站图片的权限,可以想象一下创建一个定时器无限更新img标签的src属性……,网易公司土豪啊!不过我没尝试过。


好了,废话到此为止,下面简单介绍一下验证码防盗链的思路:

思路一,使用web容器(如nginx)自带的防盗链设置:

location /test/ { 
    valid_referers none blocked server_names ~\.google\. ~\.baidu\.;
    if ($invalid_referer) {
        return 403;
   } 
}

个人感觉杀鸡用牛刀,不是很推荐

思路二,写一个java方法对referer进行简单过滤:

package com.zhengshuiguang.blog;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class Demo {

    public static void main(String[] args) {
        String referer = "http://www.phprm.com/";
        ArrayList<String> list = new ArrayList<String>();
        list.add("*.phprm.com");
        list.add("127.0.0.1");
        list.add("");
        
        System.out.println(filterReferer(referer, list));
        
    }
    
    /**
     * 检测referer是否能通过constraints的host约束
     * @param referer 待检测的url
     * @param constraints host约束list集合
     * @return boolean
     */
    public static boolean filterReferer(String referer, List<String> constraints) {
        // 不约束
        if(constraints.size() == 0) {
            return true;
        }
        // referer为空时查看是否允许空referer
        if(referer.isEmpty()) {
            return constraints.contains("");
        }else{
            // referer不为空开始解析
            URL url;
            try {
                url = new URL(referer);
            } catch (MalformedURLException e) {
                return constraints.contains("");
            }
            // 提取出referer的host值
            String localHost = url.getHost();
            // 遍历白名单主机地址
            for(String hostRule:constraints) {
                // 判断规则是否以*.开头
                if(hostRule.startsWith("*.")) {
                    // 去掉*.
                    hostRule = hostRule.substring(2, hostRule.length());
                    // 判断localHost的首部部分是否满足规则
                    boolean flag = localHost.indexOf(hostRule) == localHost.length() - hostRule.length();
                    if(flag) {
                        return true;
                    }
                }else{
                    // 否则采用完全匹配模式
                    boolean flag = hostRule.equals(localHost);
                    if(flag) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
}

原理都一样,都是通过referer进行过滤,用法规则是这样的:

规则1:完全匹配,list元素127.0.0.1与referer中的host完全匹配,只允许http://127.0.0.1/xxx之类。

规则2:模糊匹配:*.phprm.com与referer中的host=phprm.com或xxx.phprm.com都可以进行匹配。

规则3:空匹配:""与referer=""可以匹配,如果referer为非法url也可以与""进行匹配。

在servlet中referer值可以通过request进行获取,这里就不写代码,只提供带过滤功能的验证码源码下载:

CheckCodeFilter.zip


referer过滤验证

如果验证码的地址为:http://localhost:8080/test/CheckCodeFilter

可以新建一个html测试文件分别放在http://localhost:8080/test.html和http://127.0.0.1:8080/test.html下:

<img src="http://localhost:8080/test/CheckCodeFilter"/>

刷新发现localhost下的test.html验证码不显示,只有127.0.0.1下的test.html显示验证码。

如果希望localhost也可以匹配,可以向list中添加localhost记录即可!


本文地址:http://blog.zhengshuiguang.com/java/refererFilter.html

转载随意,但请附上文章地址:-)

标签:防盗链 验证码

相关文章

评论已关闭