Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protect Final Variables from reflection

I want to design a class to demonstrate immutability, incrementally.

Following is a simple class

public class ImmutableWithoutMutator {
    private final String firstName;
    private final String lastName;
    private final int age;

    public ImmutableWithoutMutator(final String firstName, final String lastName, final int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    @Override
    public String toString() {
        return String.format(
                "ImmutableWithoutMutator [age=%s, firstName=%s, lastName=%s]",
                age, firstName, lastName);
    }
}

Now I can still breach in using reflection, by using the following code.

import java.lang.reflect.Field;

public class BreakImmutableUsingReflection {
    public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        ImmutableWithoutMutator immMutator = new ImmutableWithoutMutator(
                "firstName", "lastName", 2400);
        System.out.println(immMutator);

        // now lets try changing value using reflection
        Field f = ImmutableWithoutMutator.class.getDeclaredField("age");
        f.setAccessible(true);
        f.set(immMutator, 2000);

        System.out.println(immMutator);
    }
}

My question is, I have not modified the Modifiers for the fields using reflection API. Then how the code is still able to mutate final fields?

like image 346
Vivek Avatar asked Apr 12 '13 05:04

Vivek


2 Answers

This is expected behaviour - it's just something you shouldn't do.

From the documentation for Field.set:

If the underlying field is final, the method throws an IllegalAccessException unless setAccessible(true) has succeeded for this Field object and the field is non-static. Setting a final field in this way is meaningful only during deserialization or reconstruction of instances of classes with blank final fields, before they are made available for access by other parts of a program. Use in any other context may have unpredictable effects, including cases in which other parts of a program continue to use the original value of this field.

Now if you then want to argue this breaches all kinds of security, you need to ask yourself why you've got code you don't trust to be sane running with sufficient permission to call field.setAccessible(true). Basically, protection is given by the security manager - but if you're running without a security manager which stops you from doing this, you can shoot yourself in the foot.

like image 81
Jon Skeet Avatar answered Oct 06 '22 04:10

Jon Skeet


You can protect from setAccessible using the SecurityManager. You can either specify it on the command line or create it dynamically, like they do it here.

But even if you protect from reflection your code would still be not protected against bytecode modification libraries like ASM.

So you shouldn't rely on your fields being final.

like image 26
Oleg Mikheev Avatar answered Oct 06 '22 04:10

Oleg Mikheev