JAVA安全-03JNDI
JAVA安全-03JNDI
JNDI是什么
JNDI(Java Naming and Directory Interface)
是Java提供的Java 命名和目录接口
。通过调用JNDI
的API
应用程序可以定位资源和其他程序对象。JNDI
并不只是包含了DataSource(JDBC 数据源)
,JNDI
可访问的现有的目录及服务有:JDBC
、LDAP
、RMI
、DNS
、NIS
、CORBA
。
JNDI
相关概念:
Naming Service 命名服务
命名服务将名称和对象进行关联,提供通过名称找到对象的操作。
例如:DNS系统将计算机名和IP地址进行关联。文件系统将文件名和文件句柄进行关联等等。
Name 名称
要在命名系统中查找对象,需要提供对象的名称。对象的名称是用来标识该对象的易于人理解的名称。
例如:文件系统用文件名来标识文件对象。DNS系统用机器名来表示IP地址。
Naming Convention 命名规范
一个命名系统中的所有名称必须遵循的语法规则称为命名规范。
例如:UNIX文件系统的命名规范要求文件名是一个相对于根目录的路径,路径中的每一部分以/分隔。如:/usr/bin。
Binding 绑定
一个名称和一个对象的关联称为一个绑定。
例如:文件系统中,文件名绑定到文件。DNS系统中,机器名绑定到IP地址。
Reference 引用
在一些命名服务系统中,系统并不是直接将对象存储在系统中,而是保持对象的引用。引用包含了如何访问实际对象的信息。
Address 地址
引用通常用一个或多个地址(通信端口)来表示。
Context 上下文
一个上下文是一系列名称和对象的绑定的集合。每个上下文都有与之关联的命名规范。一个上下文通常提供一个lookup操作来返回对象,也可能提供绑定,
解除绑定,列举绑定名等操作。一个上下文中的名称可以绑定到一个具有相同命名规范的上下文中,称之为子上下文(subcontext)。
例如:在文件系统中,/usr是一个Context,/usr/bin是usr的subcontext。
Naming System 命名系统
一个相同类型的Context的集合。一个命名系统向客户提供命名服务来执行命名相关的操作。如DNS系统,LDAP系统等。
Namespace 名称空间
一个命名系统的所有名称的集合。
例如:文件系统中的名称空间是组成该文件系统的所有文件和目录的名称。
Atomic Name/Compound Name/Composite Name
Atomic Name 原子名称,是一个简单的基本的名称。
Compound Name 混合名称,是由多个原子名称一起构成的名称。
Composite Name 复合名称,是跨越多个命名系统的名称。
例如:一个名称可能是uid=admin,ou=cms,dc=foobar,dc=com/user/bin/jndi,
其中,uid=admin,ou=cms,dc=foobar,dc=com/user/bin/jndi是一个复合名称,跨越了两个命名系统LDAP和文件系统,uid=admin,ou=cms,dc=foobar,dc=com和/user/bin/jndi是两个混合名称,
uid=admin、ou=cms、dc=foobar分别是原子名称。
目录服务的相关概念
Directory Service 目录服务
目录服务是命名服务的扩展,除了提供名称和对象的关联,还允许对象具有属性。目录服务中的对象称之为目录对象。
目录服务提供创建、添加、删除目录对象以及修改目录对象属性等操作。
Attribute 属性
一个目录对象可以包含属性。一个属性具有一个属性标识符和一系列属性值。
例如:一个打印机对象可以包含速度、分辨率等属性。分辨率的属性标识是resolution,属性值可能是300dpi,600dpi等等。
Search Filter 查找过滤器
目录服务除了通过名称查找对象的操作,通常还提供通过目录对象的属性来查找对象的操作。这种的查找一般通过规定的表达式来表示,称之为查找过滤器。
JNDI目录服务
访问JNDI
目录服务时会通过预先设置好环境变量访问对应的服务, 如果创建JNDI
上下文(Context
)时未指定环境变量
对象,JNDI
会自动搜索系统属性(System.getProperty())
、applet 参数
和应用程序资源文件(jndi.properties)
。
// 创建环境变量对象
Hashtable env = new Hashtable();
// 设置JNDI初始化工厂类名
env.put(Context.INITIAL_CONTEXT_FACTORY, "类名");
// 设置JNDI提供服务的URL地址
env.put(Context.PROVIDER_URL, "url");
// 创建JNDI目录服务对象
DirContext context = new InitialDirContext(env);
/*
Context.INITIAL_CONTEXT_FACTORY(初始上下文工厂的环境属性名称)指的是JNDI服务处理的具体类名称,如:DNS服务可以使用com.sun.jndi.dns.DnsContextFactory类来处理,JNDI上下文工厂类必须实现javax.naming.spi.InitialContextFactory接口,
通过重写getInitialContext方法来创建服务。
*/
javax.naming.spi.InitialContextFactory:
package javax.naming.spi;
public interface InitialContextFactory {
public Context getInitialContext(Hashtable<?,?> environment) throws NamingException;
}
JNDI目录服务
本篇不详细分析具体的流程,仅概括性记录。
//JNDI-DNS
JNDI支持访问DNS服务,注册环境变量时设置JNDI服务处理的工厂类为com.sun.jndi.dns.DnsContextFactory即可。
//JNDI-RMI
RMI的服务处理工厂类是:com.sun.jndi.rmi.registry.RegistryContextFactory,在调用远程的RMI方法之前需要先启动RMI服务:com.anbai.sec.rmi.RMIServerTest,启动完成后就可以使用JNDI连接并调用了。
//JNDI-LDAP
LDAP的服务处理工厂类是:com.sun.jndi.ldap.LdapCtxFactory,连接LDAP之前需要配置好远程的LDAP服务。
//JNDI-DataSource
JNDI连接数据源比较特殊,Java目前不提供内置的实现方法,提供数据源服务的多是Servlet容器,没有深入。安全方面主要还是利用RMI、LDAP。
注:java本身提供的JNDI服务,我构造请求利用这些服务加载我们的恶意代码,实现攻击。
协议转换
如果JNDI
在lookup
时没有指定初始化工厂名称,会自动根据协议类型动态查找内置的工厂类然后创建处理对应的服务请求。
JNDI
默认支持自动转换的协议有:
协议名称 | 协议URL | Context类 |
---|---|---|
DNS协议 | dns:// |
com.sun.jndi.url.dns.dnsURLContext |
RMI协议 | rmi:// |
com.sun.jndi.url.rmi.rmiURLContext |
LDAP协议 | ldap:// |
com.sun.jndi.url.ldap.ldapURLContext |
LDAP协议 | ldaps:// |
com.sun.jndi.url.ldaps.ldapsURLContextFactory |
IIOP对象请求代理协议 | iiop:// |
com.sun.jndi.url.iiop.iiopURLContext |
IIOP对象请求代理协议 | iiopname:// |
com.sun.jndi.url.iiopname.iiopnameURLContextFactory |
IIOP对象请求代理协议 | corbaname:// |
com.sun.jndi.url.corbaname.corbanameURLContextFactory |
RMI示例代码片段:
// 创建JNDI目录服务上下文
InitialContext context = new InitialContext();
// 查找JNDI目录服务绑定的对象
Object obj = context.lookup("rmi://127.0.0.1:9527/test");
通过lookup
会自动使用rmiURLContext
处理RMI
请求。
安全限制
RMI安全限制
在RMI
服务中引用远程对象(加载恶意类)将受本地Java环境限制即本地的java.rmi.server.useCodebaseOnly
配置必须为false(允许加载远程对象)
,如果该值为true
则禁止引用远程对象。
除此之外被引用的ObjectFactory
对象还将受到com.sun.jndi.rmi.object.trustURLCodebase
配置限制,如果该值为false(不信任远程引用对象)
一样无法调用远程的引用对象。
JDK 5 U45,JDK 6 U45,JDK 7u21,JDK 8u121
开始java.rmi.server.useCodebaseOnly
默认配置已经改为了true
。JDK 6u132, JDK 7u122, JDK 8u113
开始com.sun.jndi.rmi.object.trustURLCodebase
默认值已改为了false
。
LDAP安全限制
LDAP
在JDK 11.0.1、8u191、7u201、6u211
后也将默认的com.sun.jndi.ldap.object.trustURLCodebase
设置为了false
。
如果java版本高,本地测试远程对象引用可以使用如下方式允许加载远程的引用对象:
System.setProperty("java.rmi.server.useCodebaseOnly", "false");
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
或者在启动Java
程序时候指定-D参数
:
-Djava.rmi.server.useCodebaseOnly=false -Dcom.sun.jndi.rmi.object.trustURLCodebase=true
高版本的java也是可以绕过的,本篇不做讨论。
参考文章
本文来自博客园,作者:九天揽月丶,转载请注明原文链接:https://www.cnblogs.com/-meditation-/articles/16257842.html