【问题标题】:Protecting fields from Reflection - The strange case of the System.security保护字段免受反射 - System.security 的奇怪案例
【发布时间】:2013-07-21 17:36:04
【问题描述】:

我目前正在研究 java 安全性并遇到了一个奇怪的现象。 java中的SecurityManager存储在java.lang.System中的“security”字段中。有趣的是,该字段似乎受到保护以防止反射访问,这确实是有道理的,但据我所知,该字段是唯一的。所以这里是例子:

for(Field f : System.class.getDeclaredFields())
    System.out.println(f);

输出

public static final java.io.InputStream java.lang.System.in
public static final java.io.PrintStream java.lang.System.out
public static final java.io.PrintStream java.lang.System.err
private static volatile java.io.Console java.lang.System.cons
private static java.util.Properties java.lang.System.props
private static java.lang.String java.lang.System.lineSeparator

有趣的是:声明为

的字段
private static volatile SecurityManager security = null;

不在列表中,果然调用了

System.class.getDeclaredField("security"); 

产生 NoSuchFieldException。因为我在网上找不到任何关于这个的信息,而且我很确定这个字段曾经可以通过反射访问(例如,请参见 2010 年的这个blog post,它描述了访问这个字段)我想知道 a)是这个实现为快速修复,以防止通过反射轻松禁用安全管理器,以及 b) 如何实现(或者更确切地说,是否有机会保护其他私有字段免受反射)。

【问题讨论】:

  • 如果我没记错的话,这个字段无法访问是因为引入了一个补丁来关闭 Java 小程序中的安全漏洞。不是 100% 确定。
  • 这确实很有趣,因为如果有反射,有几种不同的方法可以禁用安全管理器..

标签: java security reflection


【解决方案1】:

首先,防止这种反射的方式可能是在字段获取机制下的JVM中的一个脏if

if (strcmp(field, "security") == 0 && strcmp(class, "java.lang.System")) {
    return NULL;

(我并不是要暗示这是 JVM 中的实际代码!!)

这显然是大多数 java 用户无法访问的,因此唯一的其他选择是安装一个不允许私有字段和方法访问的安全管理器。这是可能的,但我不确定如何。

【讨论】:

  • 不授予 java.lang.reflect.ReflectPermission "suppressAccessChecks" 权限可以通过 SecurityManager 拒绝私有字段和方法访问。然而,这在 jvm 中是“固定的”似乎是一个非常粗暴的 hack。你知道任何文件吗?
  • 这似乎不是这样。至少在热点 vm 源中快速搜索“安全性”不会触发类似的事情。
  • @acerberus 热点虚拟机?那不是JVM,它是即时编译器。它不会在那里找到。我不知道有任何文件显示这一点,这只是我能想到的唯一方法。
【解决方案2】:

一位同事指出,答案不在jvm中,而是在jdk中,更准确地说是在sun.reflect.Reflection类中。在那里你会发现一个静态初始化器,它执行以下操作

static {
    Map<Class,String[]> map = new HashMap<Class,String[]>();
    map.put(Reflection.class,
        new String[] {"fieldFilterMap", "methodFilterMap"});
    map.put(System.class, new String[] {"security"});
    fieldFilterMap = map;

    methodFilterMap = new HashMap<Class,String[]>();
}

如果我们现在仔细看看 java.lang.Class 中的 getDeclaredFields 方法,我们会发现这些字段是使用对 Reflection 类的调用进行过滤的:

Reflection.filterFields(this, getDeclaredFields0(publicOnly));

filterFields 的实现方式

public static Field[] filterFields(Class containingClass,
                                   Field[] fields) {
    if (fieldFilterMap == null) {
        // Bootstrapping
        return fields;
    }
    return (Field[])filter(fields, fieldFilterMap.get(containingClass));
}

所以 .. 这解决了如何保护字段的问题。然而,我仍然很好奇为什么要实现这一点。

【讨论】:

  • 不错的发现,+1。您从哪里获得 sun.* 类的源代码?或者你只是在使用一个非常好的反编译器?
  • 您可以从openjdk.java.net获取代码作为下载包。在那里,您还可以找到有关如何签出 mercurial 存储库的教程。
猜你喜欢
  • 2010-10-18
  • 1970-01-01
  • 2011-09-12
  • 1970-01-01
  • 1970-01-01
  • 2012-01-22
  • 2012-01-11
  • 2012-10-13
相关资源
最近更新 更多