Log4j2漏洞后的反思
我并不是专门搞安全的,这里的安全泛指网络安全,不自作孽的话,离线情况一般不存在安全问题,而我知道这件事还是因为学校对信管部的通知,不知为何我就想了解一下,真就这么离谱,一个日志都可以被骇入。
我先简单整理一下这个漏洞是怎么产生的,这个漏洞产生在使用了Log4j2日志的服务器,但影响版本有一定范围,有log4j2版本和JDK版本,依据攻击方式各不相同。其中常见的攻击方式是,针对log4j2提供的Lookups模块,该模块可以执行“${}”对应的符合一定格式的语句,一般对于信息输出,无关紧要,更致命的是它内部没有对参数分开处理的情况下还提供了Jndi的功能,一般情况下我们采用占位符输出,像这样Log.info("name:{}",username)
,为了输出能力更强,log4j2提供了可执行输出username="${java:os}"
这是lookups的java模块

真正危险的是下面这个功能

虽说官方说使用在配置文件里,但从底层实现可以发现,使用如上的Log.info也会执行相应代码,而且对于jndi此库是基于javaapi的相应库实现的,而这个jndi库很久以前就被曝存在jndi注入漏洞,主要存在jdk较低版本里,我们应该理解为什么很多公司都停留在jdk7和jdk8,因为更新有时会使维护变得困难。jndi注入,虽说与sql注入和js注入相似,但有本质的区别,java不是脚本语言,代码是需要编译的,与此齐名的是java反序列化漏洞,比如Fastjson就存在这个漏洞。在java的jndi里本质只是一个接口,用于定位网络上资源,可以是局域网也可以是广域网,与url类似,jndi底层协议中比较危险的是rmi和ldap,我们以前者为例,rmi用于远程执行代码,但是必须要服务端提供相应的继承于Remote的接口,但是从实现上来看,服务端需要使用类加载器来加载远程端的class代码,而类加载中会执行类中存在static代码块,就算没有服务端的接口函数来执行,也可以注入相应代码。这过程其实还是挺复杂的,但本质还是注入漏洞,只不过比一般脚本注入有更高的权限,而这种漏洞主要还是过于相信用户输入,还有就是没有完全将后端封闭在安全的空间里,只要后端在局域网里也没什么好怕的。当然还有局域网渗透,但门都没锁好又能怪谁。
当然安全漏洞牵扯的相关方还是挺多的,如log4j2方对于参数判断不过完善却还提供jndi模块,使用的开发者可能挺无辜,不知道一个日志库还有这种功能,但相信用户输入也是不对的,更何况这还是在后端,jdk一直在低版本也不太对,但jdk方提供远程执行功能本身也存在风险的。不过纠结谁对谁错是没有意义的,而是对我们今后的启示。
首先是库的开源问题,开源库有助于大家一起学习发现漏洞,但取决于谁发现漏洞了,实际上大部分人都倾向于使用而非研究源码,反而是安全人员或骇客比较乐于此。此时开源反而成了弊端,对于还需要反编译或读汇编的闭源库,开源反而更容易被研究,这时或许就能理解为什么公司乐于使用商业软件了,供应方不仅能提供指导服务还能背锅,从经济和资本家的角度考虑,这样的投资确实是值得的。
然后是库自身的问题,本身作为一个日志库,却提供超过日志库的功能,功能越复杂就越有可能出问题,但过于细分化也是有可能出问题的,如nodejs的leftpad事件,这虽然与安全无关,但也说明了使用轮子的弊端。但终究我们还是得用库的,主要在于对库的选择和了解,依赖当然是越少越好,有时还是得看看底层,在学习源码的同时,还有助于发现潜在风险。
最后是本身属于服务端应考虑的问题了,既然将自己暴露于公网,那一些常见的风险怎能不考虑呢,如用户输入可能产生的注入问题。不过像我这样的静态网页,也没什么有用的数据库的,还依赖于其它服务器的小网页,其实也没什么考虑的必要就是了。
还记得我之前对自己不喜欢用轮子的批评,现在看来有必要重新考虑了,并在两者间稍微做些权衡了。“一切网络风险都是自己引入的”这句话可一点也不假。