【转载】t-io im小站攻防实录
t-io im小站攻防实录
http://t-io.org:9292/newim/index.html是作者用tio写的一个web im小站,目前代码还在开发阶段,问题也比较多,暂未开源(t-io刚开源时的时候问题也不少,尤其是一些调试代码,眼尖的网友很容易看出来),废话少说,说一下本小站的一些攻防故事吧。
-
跨站脚本攻击
这个比较简单,直接用对html进行转义就好,譬如:org.apache.commons.lang3.StringEscapeUtils.escapeHtml4("<script>alert(1)</script>") -
言语攻击
譬如在聊天中输入"fXXk, XV, 国家LING人名字"等,策略是敏感词过滤
先找到词库,作者用的部分词库在https://git.oschina.net/tywo45/t-io/tree/master/docs/dict,然后用https://www.oschina.net/p/hutool的com.xiaoleilu.hutool.dfa.WordTree来处理就好,具体用法请去hutool的官网。 -
DDos攻击
这个是作者没想到的,其实DDos攻击的性质还可以分成几个情况:- 单纯的技术玩耍(就是好玩,也不怀恶意,在快要攻击死对方后马上休手)
- 单纯的技术破坏(攻击死对方后,自己在家偷着乐)
- 恶意的技术破坏(譬如攻击后向受害者索要财物、或是攻击后散布谣言说随便刷新一下就把小站给弄死了)
- 记录所有访问交互动作,这个是相对容易做到的,也没什么技术含量,就不细说了,这一步主要是为了留下证据,万一构成刑事纠分,可能会有用。
- 利用RateLimiter实现访问频率控制,不过作者在此基础上作了改造,下面可能会花点篇幅来介绍这个
- 对RateLimiter进一步封装,也没什么好说的,直接看代码吧,最核心的代码是Guava提供的RateLimiter
package org.tio.monitor; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tio.core.utils.SystemTimer; import com.google.common.util.concurrent.RateLimiter; /** * @author tanyaowu * 2017年5月23日 下午1:09:55 */ public class RateLimiterWrap { private static Logger log = LoggerFactory.getLogger(RateLimiterWrap.class); /** * 频率控制 */ private RateLimiter rateLimiter = null;//RateLimiter.create(3); /** * 本阶段已经收到多少次警告 */ private AtomicInteger warnCount = new AtomicInteger(); /** * 总共已经收到多少次警告 */ private AtomicInteger allWarnCount = new AtomicInteger(); /** * 本阶段最多警告多次数 */ private int maxWarnCount = 20; /** * 一共最多警告多次数 */ private int maxAllWarnCount = maxWarnCount * 10; /** * 上一次警告时间 */ private long lastWarnTime = SystemTimer.currentTimeMillis(); /** * 警告清零时间间隔,即如果有这么长时间没有收到警告,则把前面的警告次数清零 */ private int warnClearInterval = 1000 * 60 * 60 * 2; /** * * @param permitsPerSecond QPS * @param warnClearInterval 清理本阶段警告的时间间隔,参考值1000 * 60 * 60 * 2,单位为ms * @param maxWarnCount 本阶段最多警告多次数,参考值10 * @param maxAllWarnCount 一共最多警告多次数 * @author: tanyaowu */ public RateLimiterWrap(int permitsPerSecond, int warnClearInterval, int maxWarnCount, int maxAllWarnCount) { this.rateLimiter = RateLimiter.create(permitsPerSecond); this.warnClearInterval = warnClearInterval; this.maxWarnCount = maxWarnCount; this.maxAllWarnCount = maxAllWarnCount; } /** * * @return * 0位置:根据QPS获取执行锁, false: 没拿到锁<br> * 1位置:根据警告次数获取执行锁, false: 没拿到锁<br> * @author: tanyaowu */ public boolean[] tryAcquire() { boolean ret = rateLimiter.tryAcquire(); if (!ret) { synchronized (this) { long nowTime = SystemTimer.currentTimeMillis(); if ((nowTime - lastWarnTime) > warnClearInterval) { warnCount.set(0); } lastWarnTime = SystemTimer.currentTimeMillis(); int wc = warnCount.incrementAndGet(); int awc = allWarnCount.incrementAndGet(); if (wc > maxWarnCount || awc > maxAllWarnCount) { return new boolean[]{false, false}; } return new boolean[]{false, true}; } } else { return new boolean[]{true, true}; } } ///其它非核心代码 } - 在消息处理入口处,用上前面这个东西即可
ImSessionContext imSessionContext = channelContext.getSessionContext(); RateLimiterWrap rateLimiterWrap = imSessionContext.getRequestRateLimiter(); boolean[] ss = rateLimiterWrap.tryAcquire(); String group = "g"; if (ss[0] == false && ss[1] == false) { log.error("{} 访问过频繁,本次命令:{}, 将拉黑其IP", channelContext.toString(), command); // String imgsrc = UserService.nextImg(); String text = "<span style='font-size:16px;'>对不起大家,由于我发消息太频繁,已经被服务器拉黑了,大家珍重</span>"; ChatRespBody.Builder builder = ChatRespBody.newBuilder(); builder.setType(ChatType.CHAT_TYPE_PUBLIC); builder.setText(text); builder.setFromClient(org.tio.examples.im.service.UserService.sysClient); builder.setGroup(group); builder.setTime(SystemTimer.currentTimeMillis()); ChatRespBody chatRespBody = builder.build(); ImPacket respPacket1 = new ImPacket(Command.COMMAND_CHAT_RESP, chatRespBody.toByteArray()); Aio.sendToGroup(groupContext, group, respPacket1); Aio.IpBlacklist.add(groupContext, channelContext.getClientNode().getIp()); return null; } else if (ss[0] == false && ss[1] == true) { log.error("{} 访问过频繁,本次命令:{},将警告一次", channelContext.toString(), command); Client client = imSessionContext.getClient(); String nick = client.getUser().getNick(); String region = client.getRegion(); String ip = client.getIp(); int warnCount = rateLimiterWrap.getWarnCount().get(); int maxWarnCount = rateLimiterWrap.getMaxWarnCount(); int xx = maxWarnCount - warnCount; String formatedUserAgent = ImUtils.formatUserAgent(channelContext); String text = "<div class='tio-danger'>第" + warnCount + "次警告【" + nick + "】【" + region + "】【" + ip + "】【" + formatedUserAgent + "】,还剩" + xx + "次警告机会" + "</div>"; // text += "<div style='font-size:14px;color:#ff0033'><a href='http://t-io.org:9292/ecosphere.html?v=4514545454' target='_blank'>如果被拉黑请联系作者, 欢迎对t-io生态圈进行投资建设,谢谢!</a></div>"; ChatRespBody.Builder builder = ChatRespBody.newBuilder(); builder.setType(ChatType.CHAT_TYPE_PUBLIC); builder.setText(text); builder.setFromClient(org.tio.examples.im.service.UserService.sysClient); builder.setGroup(group); builder.setTime(SystemTimer.currentTimeMillis()); ChatRespBody chatRespBody = builder.build(); ImPacket respPacket1 = new ImPacket(Command.COMMAND_CHAT_RESP, chatRespBody.toByteArray()); Aio.sendToGroup(groupContext, group, respPacket1); return null; } }
- 对RateLimiter进一步封装,也没什么好说的,直接看代码吧,最核心的代码是Guava提供的RateLimiter
-
其它方面的攻击
作者也在慢慢积累各种经验,也希望大家是友好的攻防演练,而不要变成恶意攻击。
© 著作权归作者所有
浙公网安备 33010602011771号