1. 前言
log4j 出现了重大漏洞,黑客可以通过ldap 的注入漏洞而篡改后台代码,因而全球的技术部门几乎在同一时间进行着log4j 的升级。我司一个老产品用的是log4j 1.2.15 的版本,虽然并不受这次漏洞的影响但领导还是要求升级到最新的log4j 2.1.17 - 产品架构采用
jersey 作为web框架,再部署到tomcat 作为容器启动 - 直接替换jar,移除log4j 1.2.15版本的jar
log4j-1.2.15.jar ,增加新版本jar:
log4j-1.2-api-2.17.1.jar ,
log4j-api-2.17.1.jar ,
log4j-core-2.17.1.jar 没错,log4j 2.x做了架构上的解耦因此有3个jar,而原来的1.x版本只有一个jar。这次的漏洞出现在core包里面,所以1.x本身不受这次漏洞的影响。 - 做完替换工作,尝试启动失败,抛出异常
java.lang.ArrayIndexOutOfBoundsException
com.sun.jersey.api.core.ScanningResourceConfig localhost-startStop-1 SEVERE: catch scan exception: (java.lang.ArrayIndexOutOfBoundsException: 52264)
java.lang.ArrayIndexOutOfBoundsException: 52264
at org.objectweb.asm.ClassReader.readClass(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at com.sun.jersey.spi.scanning.AnnotationScannerListener.onProcess(AnnotationScannerListener.java:133)
at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94)
at com.sun.jersey.core.util.Closing.f(Closing.java:71)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:92)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:79)
at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:35)
at com.sun.jersey.api.core.servlet.WebAppResourceConfig.init(WebAppResourceConfig.java:102)
at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:89)
at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:74)
at com.sun.jersey.spi.container.servlet.WebComponent.getWebAppResourceConfig(WebComponent.java:672)
at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:414)
at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:581)
at com.sun.jersey.spi.container.servlet.WebServletConfig.getDefaultResourceConfig(WebServletConfig.java:87)
at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:703)
at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:678)
at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)
at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1231)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1144)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1031)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4997)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5289)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1101)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1813)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
2. 解决
- 本着有问题先google的态度去搜索了一番。Stack Overflow上有人给出的答案是因为:
log4j-core-2.17.1.jar\META-INF\versions\9 下的内容不能兼容导致,可以直接从jar内删除这部分代码。 - 按照此方法尝试后依然后异常抛出,于是想自己看下源码解决吧。
- 通过异常栈信息可以定位到抛异常的代码类
WebAppResourcesScanner 。
at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94)
at com.sun.jersey.core.util.Closing.f(Closing.java:71)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:92)
at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:79)
private void scan(String root, final ScannerListener cfl) {
Set<String> resourcePaths = this.sc.getResourcePaths(root);
if (resourcePaths != null) {
Iterator i$ = resourcePaths.iterator();
while(i$.hasNext()) {
final String resourcePath = (String)i$.next();
if (resourcePath.endsWith("/")) {
this.scan(resourcePath, cfl);
} else if (resourcePath.endsWith(".jar")) {
try {
(new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure() {
public void f(InputStream in) throws IOException {
JarFileScanner.scan(in, "", cfl);
}
});
} catch (IOException var9) {
throw new ScannerException("IO error scanning jar " + resourcePath, var9);
}
} else if (cfl.onAccept(resourcePath)) {
try {
(new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure() {
public void f(InputStream in) throws IOException {
cfl.onProcess(resourcePath, in);
}
});
} catch (IOException var7) {
throw new ScannerException("IO error scanning resource " + resourcePath, var7);
} catch (Exception var8) {
this.LOGGER.log(Level.WARNING, "Failed to scan path: " + resourcePath, var8);
}
}
}
}
}
- 可以大概看到这部分的代码逻辑就是扫面传入的路径中所有的jar文件读取需要的配置,结合类名和代码基本可以判断出是为了加载web source的一些资源信息,而抛出异常的代码应该是在这部分
JarFileScanner.scan(in, "", cfl) 。
try {
(new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure() {
public void f(InputStream in) throws IOException {
JarFileScanner.scan(in, "", cfl);
}
});
} catch (IOException var9) {
throw new ScannerException("IO error scanning jar " + resourcePath, var9);
}
- 可以看到这里只
catch (IOException var9) ,并没有catch到我们遇到的java.lang.ArrayIndexOutOfBoundsException ,这应该就是不兼容导致程序无法正常运行的直接原因。 - 于是我追加了异常捕获来处理异常。
try {
(new Closing(this.sc.getResourceAsStream(resourcePath))).f(new Closure() {
public void f(InputStream in) throws IOException {
JarFileScanner.scan(in, "", cfl);
}
});
} catch (IOException var9) {
throw new ScannerException("IO error scanning jar " + resourcePath, var9);
} catch (Exception var10) {
this.LOGGER.log(Level.WARNING, "Failed to scan path: " + resourcePath, var10);
}
- 修改完后编译出
class 文件替换到jersey-servlet-1.10.jar ,将新的jersey更新到项目lib下。 - 重新部署运行,成功启动,并打印如下log.
com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner localhost-startStop-1 WARNING: Failed to scan path: /WEB-INF/lib/log4j-api-2.17.1.jar (java.lang.ArrayIndexOutOfBoundsException)\n java.lang.ArrayIndexOutOfBoundsException
com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner localhost-startStop-1 WARNING: Failed to scan path: /WEB-INF/lib/log4j-core-2.17.1.jar (java.lang.ArrayIndexOutOfBoundsException)\n java.lang.ArrayIndexOutOfBoundsException
com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner localhost-startStop-1 WARNING: Failed to scan path: /WEB-INF/lib/log4j-1.2-api-2.17.1.jar (java.lang.ArrayIndexOutOfBoundsException: 14385)\n java.lang.ArrayIndexOutOfBoundsException: 14385\n at org.objectweb.asm.ClassReader.<init>(Unknown Source)\n at org.objectweb.asm.ClassReader.<init>(Unknown Source)\n at org.objectweb.asm.ClassReader.<init>(Unknown Source)\n at com.sun.jersey.spi.scanning.AnnotationScannerListener.onProcess(AnnotationScannerListener.java:133)\n at com.sun.jersey.core.spi.scanning.JarFileScanner.scan(JarFileScanner.java:97)\n at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner$1.f(WebAppResourcesScanner.java:94)\n at com.sun.jersey.core.util.Closing.f(Closing.java:71)\n at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:53)\n at com.sun.jersey.spi.scanning.servlet.WebAppResourcesScanner.scan(WebAppResourcesScanner.java:37)\n at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:80)\n at com.sun.jersey.api.core.servlet.WebAppResourceConfig.init(WebAppResourceConfig.java:102)\n at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:89)\n at com.sun.jersey.api.core.servlet.WebAppResourceConfig.<init>(WebAppResourceConfig.java:74)\n at com.sun.jersey.spi.container.servlet.WebComponent.getWebAppResourceConfig(WebComponent.java:672)\n at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:414)\n at com.sun.jersey.spi.container.servlet.ServletContainer.getDefaultResourceConfig(ServletContainer.java:581)\n at com.sun.jersey.spi.container.servlet.WebServletConfig.getDefaultResourceConfig(WebServletConfig.java:87)\n at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:703)\n at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:678)\n at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)\n at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)\n at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)\n at javax.servlet.GenericServlet.init(GenericServlet.java:158)\n at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1231)\n at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1144)\n at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1031)\n at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4997)\n at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5289)\n at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)\n at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725)\n at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701)\n at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)\n at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1101)\n at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1813)\n at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)\n at java.util.concurrent.FutureTask.run(FutureTask.java:266)\n at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\n at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\n at java.lang.Thread.run(Thread.java:745)
- 这样的改动相当于在扫描web source的时候跳过了log4j的jar,这应该是没问题的,log4j里并不包含任何web项目的内容,只是一个打日志的工具包。
- 至此问题解决。
3. 后记
- 后来回想了一下,也许第一种google到的办法也许也可以解决。因为一共3个jar,而我只修改其中的一个,如果都处理的话也许是可以的,没有继续尝试。
- 修改的代码片段是用于扫描jar的,所以扫描路径肯定是可以配置的,我找到了对应的参数名,默认是扫描
/WEB-INF/lib/* 也就是所有jar,如果我通过参数去指定哪些jar需要被扫描也是可以的,但没这么做。这个项目不太了解,并不确定需要扫描哪些jar,如果我们所有的jar都加上会是很长的参数,而只为了跳过log4j实在是太麻烦了不易于维护。
|