使用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进行获取,这里就不写代码,只提供带过滤功能的验证码源码下载:
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
转载随意,但请附上文章地址:-)
评论已关闭