突发:宕机崩溃OOM
突发:宕机崩溃OOM
事故背景:我们的项目每年都进行一次三级等保认证,2024年*月*日认证公司对我们的项目进行渗透测试时,我们系统出现无法访问和使用的情况。出现问题后我们联系认证公司停止测试,系统依然没有恢复。
事故分析:
1. 出现问题后我第一时间检查了日志,发现了OOM
java.lang.OutOfMemoryError: Java heap space
at org.apache.tomcat.util.threads.TaskThreadFactory.newThread(TaskThreadFactory.java:42)
由于没有dump日志所以不好排查OOM的原因,我便加上了启动参数:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/heapdump-9001.hprof
然后联系对方公司再测试一遍
- OOM再次出现,我拿到了heapdump-9001.hprof,开始用MemoryAnalyzer 进行分析,首先查看大对象(Dominator Tree)
大对象在这儿看,点进去发现最大的是 数据库相关
最大的这个对象有1个多G,有200多万条数据,都在内存里
怎么可能有200多万呢,肯定是查错了,难道SQL注入导致把数据库里所有数据都查出来了?
再去数据库查下,果然是所有数据:
- 再尝试找找代码是哪儿运行的,
点开LeakSuspects
找到
好多都是框架代码,我们尝试在里面找到 认识的业务相关代码:
- 找到getFiles代码:
这里都是拼接的SQL,有SQL注入问题,应该就是这儿的问题了。
- 那么对方公司是怎么注入的呢,每个请求在Ngnix Acess日志里有记录,我尝试到Ngnix里查找日志,发现了
/request?view&cgFormField=-1'%20OR%202%2B663-663-1=0%2B0%2B0%2B1%20or%20'BApebRcZ'='
添加日志发现 该参数传入后,SQL变成了这样:
注意两个or 之间 变成了 1=1 永恒true,也就导致把所有数据都查出来了。
原因已经找到了,那么该如何解决呢,之前知道用预编译prepareStatement就行, 但这里的代码 不适合大动,查了下用StringEscapeUtils.escapeSql() 比较合适,改成这样
同样的参数再次测试
相当于整个字符串 是个value ,不会被恶意拆掉
事故复盘:
查看了该处代码提交记录,很久之前就是这么写的,往年渗透测试时并没有测出来。比起追究责任,更重要的是给我们提醒:
- 注意SQL注入问题,不要拼接原生SQL,可以用上面的注入字符串自测。
- 每个应用都应该加上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath,免得出现宕机无法复现,无法排查问题