Protecting fields from Reflection - The strange ca

2019-02-07 02:57发布

问题:

I am currently looking into java security and came across a strange phenomenon. The SecurityManager in java is stored in the field "security" in java.lang.System. Interestingly, the field seems to be protected against reflective access, which does make sense, but as far as I know this field is the only one which is. So here is the example:

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

outputs

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

Interestingly: the field declared as

private static volatile SecurityManager security = null;

is not in the list, and sure enough a call to

System.class.getDeclaredField("security"); 

yields a NoSuchFieldException. As I couldn't find anything about this online, and I am pretty sure this field used to be accessible via reflection (see also, for example, this blog post from 2010 which describes accessing this field) I was wondering a) was this implemented as a quick fix to prevent easily disabling the securitymanager via reflection and b) how this is implemented (or rather is there any chance of protecting other private fields from reflection as well).

回答1:

A colleague pointed out that the answer is not in the jvm but in the jdk, more precisely in the class sun.reflect.Reflection. There you'll find a static initializer that does the following

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[]>();
}

If we now look a bit closer at the getDeclaredFields method in java.lang.Class we'll find that the fields are filtered using a call to the Reflection class:

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

where filterFields is implemented as

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

So .. this solves the issue how the field is protected. I am however still curious as to why this was implemented.



回答2:

First, the way this was prevented from reflection was probably a dirty if in the JVM under the field getting mechanism:

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

(I am NOT meaning to imply that this is the actual code in the JVM!!)

This obviously is not accesible to most users of java, so the only other option is to install a security manager that disallows private field and method access. This is possible, but I'm not sure how.