evasive 访问回避
一.简介
就是所谓的熔断机制,apache有个模块evasive2能够限制用户ip到访问量。但对于某个数据到,或者页面到不断刷新访问很难控制。因为它没有和你到业务逻辑配合起来。 本人在做投票软件到时候就碰到这个问题,刷票到人太多,如果不控制,服务器都会被刷死。(瓶颈在数据库的update有lock已经应答不过来来,有人说用删除在添加到方法,那是坑,因为高并发下,最后结果完全不正确), 为了解决这个问题,如果在程序里边判断。也只能真对某一个软件或者部分,很不通用。于是就产生了TXweb的evasive功能,它能控制每一个页面到get,post等的访问频率,并且通过简单到配置就能管理好TXWeb项目下的所有页面。 是针对apache下evasive2模块的细粒度补充。- evasive访问回避不是用在流量限制上,它一旦拦截,将不能访问所有数据,当然能可以配置一个提示页面。
- 采用缓存方式,能够很好的保证到效率。jspx构架下到缓存是很灵活的,可以扩展为分布式,也可以本机低配置运行。
- ip排除代理IP,防止ip池的狂刷。
- 本功能不能保证绝对不会被刷票,但可以保证不被刷死机。
- 如果有高安全需要可以配合ROC部分的加密传输,来提高安全性。
二.原理说明
TXWeb 本身支持pageInterceptors 页面拦截器,但这部分功能比较单独,为了方便配置和性能,所以单独做成了一个组件。 它位于pageInterceptors之上,如果被拦截action里边包括拦截器所有到东西都不会在执行。判断条件主要是判断IP, 有白名单和黑名单,白名单用户可以直接配置,黑名单由用户设置的条件判断。一旦满足规则将自动加入黑名单,屏蔽一段时间,时间长短根据配置。 如果要需要配置直接进入黑名单,只需要配置maxTimes为0和屏蔽时间就可以直接进入。三.配置说明
先配置jspx.properties文件里边的useEvasive=true,表示总开关,evasive_config=jspx.evasive.xml,jspx.evasive.xml为默认到配置文件,在开发中可以加入< include file="*.evasive.xml" />的方式嵌入各个软件的 不同控制配置。 jspx.properties####################################################### # # evasive 访问回避功能 # 限制高并发到提交或刷屏访问 ####################################################### #是否开启evasive功能,默认是开启的 useEvasive=true #url1回避拦截器配置 evasive_config=jspx.evasive.xmljspx.evasive.xml
<?xml version="1.0" encoding="UTF-8"?> <evasives > <!--白名单--> <whiteList>127.0.0.1;192.168.0.*</whiteList> <!--黑名单 默认返回名为 black --> <blackList>124.67.207.35;120.193.236.90;1.27.231.100;</blackList> <result name="black" type="html" status="503"><![CDATA[<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>提示说明</title> <style type="text/css">.question_wrapper { background-color: #fff; border: 2px solid #f8bb5b; margin: 10px; padding:10px; z-index: 10;} div, ul, li, h1, h2, h3, h4, form, input, p, button, dl, dt, dd, fieldset, textarea, label, del {margin: 0; padding: 0;}.question_text {color: #666;font-size: 14px; font-weight: normal; line-height: 21px;} .user_wrap a {color: #828282;}pre { font-family: "Microsoft YaHei","SimHei","SimSun",Arial,sans-serif; overflow-wrap: break-word; white-space: pre-wrap; word-break: break-all;} a { color: #666; text-decoration: none;}.clearfix::after, .cf::after { clear: both;display: table;}.ask_autho { color: #828282; line-height: 30px; position: relative;} .pj_btn a { color: #4083a9; display: block; float:right;margin-left:50px; padding: 0 6px; white-space: nowrap;}</style></head><body><div class="question_wrapper"> <h2>错误信息:<span style="color: #E0E0E0;">ERROR</span></h2><hr /><div class="question_text"><pre>你的ip存在异常行为,已被列入可疑名单</pre></div><div class="pj_btn cf"></div></div></body></html> ]]> </result> <!--不安全的URL关键字,配置里边空格有效--> <insecureUrlKeys><![CDATA[<;>; or ; and ;temp.;web-inf;windows;@;!;|;{;};~;^;*;:;\;%5c;%255c;#cmd]]></insecureUrlKeys> <!--不允许访问的后缀--> <blackSuffixList><![CDATA[jar;dll;bat;exe;sh;conf]]></blackSuffixList> <!--输入这个密码才能访问这个目录里边的文件,方便远程调试--> <passwordFolder> <value password="配置的查看密码">/logs/*</value> </passwordFolder> <!--不安全的参数字符串,配置里边空格有效;发现后直接提示错误--> <insecureQueryStringKeys><![CDATA[<;>; or ; and ;insert ;select ; where ; from ;delete ;drop ;exec ;execute ;xp_cmdshell ; create ;information_schema;group_concat; use ;window.location;";';!;#cmd]]></insecureQueryStringKeys> <!--IP 查询表黑名单 ipField:ip 字段名称 timesField:次数字段,出现次数; minTimes:最小次数,这个次数以上的才会放入黑名单; blackSize:一次最多放入黑名单个数,imprisonSecond:囚禁时间如为-1将永久屏蔽,,执行周期为有 imprisonSecond*1.5 例子: SELECT COUNT(ip) AS times,ip FROM `jbbs_vote_member` WHERE createDate>'${date.addMinutes(-30).string('yyyy-MM-dd HH:mm')}' GROUP BY ip HAVING times>100 ORDER BY times DESC LIMIT 0,10 <queryBlack name="weixinvote" ipField="ip" timesField="times" minTimes="200" blackSize="2" imprisonSecond="2000" result="voteBusy"><![CDATA[ SELECT COUNT(ip) AS times,ip FROM `jspx_vote_member` WHERE createDate>'${date.addMinutes(-30).string('yyyy-MM-dd HH:mm')}' GROUP BY ip HAVING times>200 ORDER BY times DESC LIMIT 0,10]]></queryBlack> <result name="voteBusy" type="html" status="503"><![CDATA[<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>提示说明</title> <style type="text/css">.question_wrapper { background-color: #fff; border: 2px solid #f8bb5b; margin: 10px; padding:10px; z-index: 10;} div, ul, li, h1, h2, h3, h4, form, input, p, button, dl, dt, dd, fieldset, textarea, label, del {margin: 0; padding: 0;}.question_text {color: #666;font-size: 14px; font-weight: normal; line-height: 21px;} .user_wrap a {color: #828282;}pre { font-family: "Microsoft YaHei","SimHei","SimSun",Arial,sans-serif; overflow-wrap: break-word; white-space: pre-wrap; word-break: break-all;} a { color: #666; text-decoration: none;}.clearfix::after, .cf::after { clear: both;display: table;}.ask_autho { color: #828282; line-height: 30px; position: relative;} .pj_btn a { color: #4083a9; display: block; float:right;margin-left:50px; padding: 0 6px; white-space: nowrap;}</style></head><body><div class="question_wrapper"> <h2>错误信息:<span style="color: #E0E0E0;">ERROR</span></h2><hr /><div class="question_text"><pre> Please try again later,不允许频繁刷票,访问存在异常行为,请${waitTime}后在试</pre></div><div class="pj_btn cf"></div></div></body></html> ]]> </result> --> <!-- 如果要直接屏蔽,调整频率和囚禁时间就可以 imprisonSecond 囚禁时间 600为 10分钟 result 中黑名单的时候默认是black优先 --> <!-- <evasive name="起个名字方便记" interval="50" maxTimes="10" method="POST" url="votePost" cacheName="validVotePostIp" imprisonSecond="600" logic="or"> <condition type="cookie" caption="只是判断是否存在这个参数">cookieVarName</condition> <condition type="session" caption="只是判断是否存在这个参数">sessionVarName</condition> <condition type="referer" caption="判断是否外链,防止CSRF攻击">允许外链的域名,多少使用|分割</condition> <condition type="sql" caption="使用默认DAO执行sql返回,sql 返回来判断是否成立">select id from table </condition> <condition type="ip" caption="这里是一个ip表达式例如:211.92.137.1-211.92.137.55;211.92.137.*">192.168.0.1</condition> <condition type="parameter" caption="只是判断是否存在这个参数">sign</condition> <result name="*" type="redirect">/error/busy.html</result> </evasive> <result name="black" type="redirect">/error/busy.html</result> --> <!--interval 检测周期 maxTimes 最大访问次数 imprisonSecond 囚禁时间,如果为-1将永久屏蔽--> <evasive name="allPage" interval="20" maxTimes="60" method="*" url="\S+[^.].${suffix}" cacheName="evasiveCache" imprisonSecond="20" logic="or" result="busy" /> <result name="busy" type="html" status="503"><![CDATA[<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>提示说明</title> <style type="text/css">.question_wrapper { background-color: #fff; border: 2px solid #f8bb5b; margin: 10px; padding:10px; z-index: 10;} div, ul, li, h1, h2, h3, h4, form, input, p, button, dl, dt, dd, fieldset, textarea, label, del {margin: 0; padding: 0;}.question_text {color: #666;font-size: 14px; font-weight: normal; line-height: 21px;} .user_wrap a {color: #828282;}pre { font-family: "Microsoft YaHei","SimHei","SimSun",Arial,sans-serif; overflow-wrap: break-word; white-space: pre-wrap; word-break: break-all;} a { color: #666; text-decoration: none;}.clearfix::after, .cf::after { clear: both;display: table;}.ask_autho { color: #828282; line-height: 30px; position: relative;} .pj_btn a { color: #4083a9; display: block; float:right;margin-left:50px; padding: 0 6px; white-space: nowrap;}</style></head><body><div class="question_wrapper"> <h2>错误信息:<span style="color: #E0E0E0;">ERROR</span></h2><hr /><div class="question_text"> <pre>The server is busy. Please try again later,服务器忙,请${waitTime}后在试</pre></div><div class="pj_btn cf"></div></div></body></html> ]]> </result> <include file="*.evasive.xml" /> </evasives>说明:
- include是为了方便多个应用到不同配置能够整合运行。
- result name="*",这里只是为了扩展,目前只会返回black,表示拦截,如果正常不拦截到就不会返回了。
- result提供了,type,提供了几种方式redirect,跳转到页面,html表示直接现实里边到内容,内容为html,xml,json,方便roc拦截后返回数据满足应用提示到要求,script脚本支持,内部变量有 waitSecond(拦截后等待到秒数),waitTime(需要等待的时间中文),request,response,ip。
- condition表示条件,这里可以配置多个,多个条件间到逻辑关系通过logic="or"或者 logic="and" 来判断,满足条件才判断累计计数,当计数达到maxTimes时就会被拦截。
- condition type支持到类型cookie,session,ip,sql,referer 在例子配置中能够看到说明。其中sql只返回一个数据就可以了。系统只判断这个数据是0,大于0,或者true,false来计数判断。sql方式必须调用默认的genericDAO来连接数据库。
- method和url可以使用通配符,表示所有,例如method="" url="*" 。
- cacheName这里配置的cache可以是已经配置在cache.sioc.xml文件里边的cache,可以同名。如果没有将会创建一个新的cache。
condition的type说明
- cookie:判断正文是否存在这个参数
- session:判断正文是否存在这个参数
- referer:是否为跳转打开,正文部分为允许通行的域名,多少使用|分割,能够防止CSRF攻击,action里边提高了防止重复提交token,能进一步提高防范CSRF的安全性。
- sql:使用默认DAO执行sql返回,例如select 1表示成立,select 0就不成立
- ip:这里是一个ip表达式例如:211.92.137.1-211.92.137.55;211.92.137.*
- parameter:只是判断是否存在这个参数
四.其它说明
com.jspx.txweb.evasive.EvasiveManager类对象可以得到相关配置信息,以及黑白名单。 默认提供了一个view来给开发者查看运行情况package com.jspx.txweb.view; import com.jspx.txweb.evasive.EvasiveIp; import com.jspx.txweb.evasive.EvasiveManager; import com.jspx.txweb.evasive.EvasiveRule; import com.jspx.txweb.support.ActionSupport; import java.util.Collection; import java.util.List; public class EvasiveView extends ActionSupport { private EvasiveManager evasiveManager = EvasiveManager.getInstance(); public List<String> getWhiteList() { return evasiveManager.getWhiteList(); } public Collection<EvasiveIp> getBlackIpList() { return evasiveManager.getBlackIpList(); } public Collection<EvasiveRule> getEvasiveRuleList() { return evasiveManager.getEvasiveRuleList(); } }最后给一个全局页面拦截的例子,可以拦截所有TXWeb页面防止频繁刷新访问的例子
<evasive name="allPage" interval="5" maxTimes="10" method="*" url="\S+[^.].${suffix}" cacheName="evasiveCache" imprisonSecond="30" logic="or"> <result name="*" type="html"> <![CDATA[ <!DOCTYPE html><html><head><meta http-equiv="Con-Type" Con="text/html; charset=utf-8"><meta name="viewport" content="width=device-width, initial-scale=1" /><title>错误提示</title> <style type="text/css">.question_wrapper { background-color: #fff; border: 2px solid #f8bb5b; margin: 10px; padding:10px; z-index: 10;} div, ul, li, h1, h2, h3, h4, form, input, p, button, dl, dt, dd, fieldset, textarea, label, del { margin: 0; padding: 0;}.question_text { color: #666; font-size: 14px; font-weight: normal; line-height: 21px;} .user_wrap a { color: #828282;}pre { font-family: "Microsoft YaHei","SimHei","SimSun",Arial,sans-serif; overflow-wrap: break-word; white-space: pre-wrap; word-break: break-all;} a { color: #666; text-decoration: none;}.clearfix::after, .cf::after { clear: both; content: ""; display: table;}.ask_autho { color: #828282; line-height: 30px; position: relative;} .pj_btn a { color: #4083a9; display: block; float:right; margin-left:50px; padding: 0 6px; white-space: nowrap;}</style></head><body><div class="question_wrapper"> <h2>错误信息:<span style="color: #E0E0E0;">ERROR</span></h2><hr /><div class="question_text"> <pre>The server is busy. Please try again later,服务器忙 请${waitTime}后在试</pre></div><div class="pj_btn cf"></div></div></body></html> ]]> </result> </evasive>