103

The more I learned about the power of java.lang.reflect.AccessibleObject.setAccessible, the more astonished I am at what it can do. This is adapted from my answer to the question (Using reflection to change static final File.separatorChar for unit testing).

import java.lang.reflect.*;

public class EverythingIsTrue {
   static void setFinalStatic(Field field, Object newValue) throws Exception {
      field.setAccessible(true);

      Field modifiersField = Field.class.getDeclaredField("modifiers");
      modifiersField.setAccessible(true);
      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

      field.set(null, newValue);
   }
   public static void main(String args[]) throws Exception {      
      setFinalStatic(Boolean.class.getField("FALSE"), true);

      System.out.format("Everything is %s", false); // "Everything is true"
   }
}

You can do truly outrageous stuff:

public class UltimateAnswerToEverything {
   static Integer[] ultimateAnswer() {
      Integer[] ret = new Integer[256];
      java.util.Arrays.fill(ret, 42);
      return ret;
   }   
   public static void main(String args[]) throws Exception {
      EverythingIsTrue.setFinalStatic(
         Class.forName("java.lang.Integer$IntegerCache")
            .getDeclaredField("cache"),
         ultimateAnswer()
      );
      System.out.format("6 * 9 = %d", 6 * 9); // "6 * 9 = 42"
   }
}

Presumably the API designers realize how abusable setAccessible can be, but must have conceded that it has legitimate uses to provide it. So my questions are:

  • What are the truly legitimate uses for setAccessible?
    • Could Java has been designed as to NOT have this need in the first place?
    • What would the negative consequences (if any) of such design be?
  • Can you restrict setAccessible to legitimate uses only?
    • Is it only through SecurityManager?
      • How does it work? Whitelist/blacklist, granularity, etc?
      • Is it common to have to configure it in your applications?
    • Can I write my classes to be setAccessible-proof regardless of SecurityManager configuration?
      • Or am I at the mercy of whoever manages the configuration?

I guess one more important question is: DO I NEED TO WORRY ABOUT THIS???

None of my classes have any semblance of enforceable privacy what-so-ever. The singleton pattern (putting doubts about its merits aside) is now impossible to enforce. As my snippets above show, even some basic assumptions of how Java fundamental works is not even close to being guaranteed.

ARE THESE PROBLEMS NOT REAL???


Okay, I just confirmed: thanks to setAccessible, Java strings are NOT immutable.

import java.lang.reflect.*;

public class MutableStrings {
   static void mutate(String s) throws Exception {
      Field value = String.class.getDeclaredField("value");
      value.setAccessible(true);
      value.set(s, s.toUpperCase().toCharArray());
   }   
   public static void main(String args[]) throws Exception {
      final String s = "Hello world!";
      System.out.println(s); // "Hello world!"
      mutate(s);
      System.out.println(s); // "HELLO WORLD!"
   }
}

Am I the only one who thinks this is a HUGE concern?

polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
  • 30
    You should see what they can do in C++! (Oh, and probably C#.) – Tom Hawtin - tackline Mar 20 '10 at 04:20
  • 1
    In C and C++ they quite often do, but it's usually Undefined Behavior, so they rely on whatever particular compilers happen to do then. In C# unsafe blocks are fortunately rare. Besides, they're clearly marked, so you can easily disallow them at the source level. And, even better, there is a compiler option to disable them (it can be set at the command line, in the IDE or in the project's configuration file). There are also ways to ensure that given compiled code units don't contain unsafe blocks. – ByteEater Jun 25 '22 at 21:41
  • As I commented in another thread, actually, hard private state is important (especially on platforms with very different concerns, like embedded systems with limited resources which cannot afford process separation, or blockchains) and access modifiers (or closures which can do more or less the same) are a natural mechanism for it, so it's a shame they're removing SecurityManager. See e.g. https://agoric.com/blog/all/taxonomy-of-security-issues/. – ByteEater Jun 25 '22 at 21:42

3 Answers3

113

DO I NEED TO WORRY ABOUT THIS???

That depends entirely on what types of programs you're writing and for what kind of an architecture.

If you're distributing a software component called foo.jar to the people of the world, you're completely at their mercy anyway. They could modify the class definitions inside your .jar (through reverse engineering or direct bytecode manipulation). They could run your code in their own JVM, etc. In this case worrying will do you no good.

If you're writing a web-application that only interfaces with people and systems via HTTP and you control the application server, it's also not a concern. Sure the fellow coders at your company may create code that breaks your singleton pattern, but only if they really want to.

If your future job is writing code at Sun Microsystems/Oracle and you're tasked with writing code for the Java core or other trusted components, it's something you should be aware of. Worrying, however, will just make you lose your hair. In any case they'll probably make you read the Secure Coding Guidelines along with internal documentation.

If you're going to be writing Java applets, the security framework is something you should be aware of. You'll find that unsigned applets trying to call setAccessible will just result in a SecurityException.

setAccessible is not the only thing that goes around conventional integrity checks. There's a non-API, core Java class called sun.misc.Unsafe that can do pretty much anything at all it wants to, including accessing memory directly. Native code (JNI) can go around this kind of control as well.

In a sandboxed environment (for example Java Applets, JavaFX), each class has a set of permissions and access to Unsafe, setAccessible and defining native implementations are controlled by the SecurityManager.

"Java access modifiers are not intended to be a security mechanism."

That very much depends on where the Java code is being run. The core Java classes do use access modifiers as a security mechanism to enforce the sandbox.

What are the truly legitimate uses for setAccessible?

The Java core classes use it as an easy way to access stuff that has to remain private for security reasons. As an example, the Java Serialization framework uses it to invoke private object constructors when deserializing objects. Someone mentioned System.setErr, and it would be a good example, but curiously the System class methods setOut/setErr/setIn all use native code for setting the value of the final field.

Another obvious legitimate use are the frameworks (persistence, web frameworks, injection) that need to peek into the insides of objects.

Debuggers, in my opinion, don't fall into this category, as they normally don't run in the same JVM process, but instead the interface with the JVM using other means (JPDA).

Could Java has been designed as to NOT have this need in the first place?

That's a pretty deep question to answer well. I imagine yes, but you'd need to add some other mechanism(s) that might not be all that preferrable.

Can you restrict setAccessible to legitimate uses only?

The most straight-forward OOTB restriction you can apply is to have a SecurityManager and allow setAccessible only to code coming from certain sources. This is what Java already does - the standard Java classes that come from your JAVA_HOME are allowed to do setAccessible, while unsigned applet classes from foo.com aren't allowed to do setAccessible. As was said before, this permission is binary, in the sense that one either has it or not. There is no obvious way to allow setAccessible to modify certain fields/methods while disallowing others. Using the SecurityManager you could, however, disallow classes from referencing certain packages completely, with or without reflection.

Can I write my classes to be setAccessible-proof regardless of SecurityManager configuration? ... Or am I at the mercy of whoever manages the configuration?

You can't and you most certainly are.

Sami Koivu
  • 3,640
  • 3
  • 24
  • 23
  • "If you're distributing a software component called foo.jar to the people of the world, you're completely at their mercy anyway. They could modify the class definitions inside your .jar (through reverse engineering or direct bytecode manipulation). They could run your code in their own JVM, etc. In this case worrying will do you no good." Is this still the case? Any advises on making software tamper harder (Hopefully significantly)? It is hard to just throw java desktop applications out of the window because of this. –  Jan 21 '17 at 01:12
  • 1
    @naaz I would have to say nothing has changed. It's just that the architecture is working against you. Any piece of software running on my machine that I have full control over, I can alter. The strongest deterrent might be a legal one, but I don't imagine that will work for all scenarios. I think Java binaries are among the easiest to reverse, but folks with experience will tamper anything. Obfuscation helps by making it difficult to make big changes, but anything sort of boolean (like a copyright check) still is trivial to bypass. You could try putting key parts on a server instead. – Sami Koivu Jan 26 '17 at 03:31
  • How about denuvo? –  Jan 27 '17 at 14:27
  • What I believe you can do is to write your own SecurityManager and allow only it or its subclass to be active when your class is loaded. The ClassLoader must also be required to cooperate, otherwise you don't know if under another SecurityManager something was done to your class after loading but before any opportunity (the earliest of which is the static block) for its code to run. – ByteEater Jun 25 '22 at 21:30
16
  • What are the truly legitimate uses for setAccessible?

Unit testing, internals of the JVM (e.g. implementing System.setError(...)) and so on.

  • Could Java has been designed as to NOT have this need in the first place?
  • What would the negative consequences (if any) of such design be?

Lots of things would be unimplementable. For example, various Java persistence, serialization and dependency injections are reliant on reflection. And pretty much anything that relies on the JavaBeans conventions at runtime.

  • Can you restrict setAccessible to legitimate uses only?
  • Is it only through SecurityManager?

Yes.

I'm not saying that doing this through the SecurityManager and blacklists / whitelists is a good idea. I'm saying that it is (AFAIK) the only way to do this.

Also, note that Java 17 marks SecurityManager as deprecated; for removal. See JEP 411. That is a reason that this another bad idea.

  • How does it work? Whitelist/blacklist, granularity, etc?

It depends on the permission, but I believe that the permission to use setAccessible is binary. If you want granularity, you need to either use a different class loader with a different security manager for the classes that you want to restrict. I guess you could implement a custom security manager that implements finer grained logic.

  • Is it common to have to configure it in your applications?

No.

  • Can I write my classes to be setAccessible-proof regardless of SecurityManager configuration?
  • Or am I at the mercy of whoever manages the configuration?

No you cannot, and yes you are.

The other alternative is to "enforce" this via source-code analysis tools; e.g. custom pmd or findbugs rules. Or selective code review of code identified by (say) grep setAccessible ....

In response to the followup

None of my classes have any semblance of enforceable privacy what-so-ever. The singleton pattern (putting doubts about its merits aside) is now impossible to enforce.

If that worries you, then I suppose you need to worry. But really you should not be trying to force other programmers to respect your design decisions. If people are stupid enough to use reflection to gratuitously create multiple instances of your singletons (for example), they can live with the consequences.

On the other hand, if you mean "privacy" to encompass the meaning of protecting sensitive information from disclosure, you are barking up the wrong tree. The way to protect sensitive data in a Java application is not to allow untrusted code into the security sandbox that deals with sensitive data. Java access modifiers are not intended to be a security mechanism.

<String example> - Am I the only one who thinks this is a HUGE concern?

Probably not the only one :-). But IMO, this is not a concern. It is accepted fact that untrusted code should be executed in a sandbox. If you have trusted code / a trusted programmer doing things like this, then your problems are worse than unexpectedly mutable Strings. (Think logic bombs, exfiltration of data via covert channels, etcetera)

There are ways to deal with (or mitigate) the problem of a "bad actor" in your development or operations team. But they are costly and restrictive ... and overkill for most use-cases.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 1
    Also useful for things such as persistence implementations. Reflections isn't really useful for debuggers (although it was originally intended to be). You can't usefully change (subclass) the `SecurityManager` for this, because all you get is the permission check - all you can really do is look at the acc and perhaps walk the stack check the current thread). `grep java.lang.reflect.` – Tom Hawtin - tackline Mar 20 '10 at 04:56
  • @Stephen C: +1 already, but I'd also appreciate your input on one more question I just added. Thanks in advance. – polygenelubricants Mar 20 '10 at 04:57
  • Note that grep is a terrible idea. There are so many ways to dynamically execute code in Java that you'll almost certainly miss one. Blacklisting code in general is a recipe for failure. – Antimony Jul 21 '13 at 23:03
  • 1
    "Java access modifiers are not intended to be a security mechanism." -- actually, yes they are. Java is designed to allow trusted and untrusted code to coexist in the same virtual machine -- this was part of its core requirements from the very beginning. But only when the system is running with a locked-down SecurityManager. The ability to sandbox code depends entirely on enforcement of access specifiers. – Jules Jul 22 '16 at 23:03
  • @Jules - Most Java experts would disagree with that; e.g. http://stackoverflow.com/questions/9201603/are-private-members-really-more-secure-in-java – Stephen C Jul 23 '16 at 01:42
  • The question there is subtly different. In a typical application, which is the scenario the OP was apparently talking about, then access specifiers are not a security issue. This doesn't change the fact that Java was designed, from the ground up, to support sandboxing and that access specifiers are a critical part of that sandboxing mechanism, which therefore means that the specific statement you made, i.e. that they "are not intended to be a security mechanism", is untrue. In most cases they aren't used as one, but the system is designed to allow them to be one when that is required. – Jules Jul 23 '16 at 02:02
  • Check your history. In fact, Java / Oak was designed before that. The security sandboxing was an afterthought in Java 1.0 when Sun management saw an opportunity to get a foothold in the newly emerging "world wide web" market. If you read the old Oak specification, security is not mentioned. Indeed only the current JLS makes only passing reference to "security". It is not a core property of the Java >>language< – Stephen C Jul 23 '16 at 02:29
  • However the JLS does state that >>type security<< is a design goal, and that that (boadly) means that you should not be able to subvert JVM runtime typing from Java code. But type security and more general security are different things. Sandboxing is infrastructure that "leverages" properties of the Java language, but it is a too much of a stretch to say that that is why Oak / Java visibility rules were included in the first place. – Stephen C Jul 23 '16 at 02:35
  • Also, read this: http://gcc.uni-paderborn.de/www/WI/WI2/wi2_lit.nsf/64ae864837b22662c12573e70058bbb4/abf8d70f07c12eb3c1256de900638899/$FILE/Java%20Technology%20-%20An%20early%20history.pdf – Stephen C Jul 23 '16 at 02:41
11

Reflection is indeed orthogonal to safety/security under this perspective.

How can we limit reflection?

Java has security manager and ClassLoader as foundations to its security model. In your case, I guess you need to look at java.lang.reflect.ReflectPermission.

But this does not completely solve the problem of reflection. The reflective capabilities that are available should be subject to a fine grained authorization scheme which is not the case now. E.g. to allow certain framework to use reflection (e.g. Hibernate), but no the rest of your code. Or to allow a program to reflect only in a read-only way, for debugging purpose.

One approach that may become mainstream in the future is the usage of so-called mirrors to separate reflective capabilities from classes. See Mirrors: Design Principles for Meta-level Facilities. There are however various other research that tackles this issue. But I agree that the problem is more severe for dynamic language than static languages.

Should we be worried of the superpower that reflection gives us? Yes and no.

Yes in the sense that the Java platform is supposed to be secured with Classloader and security manager. The ability to mess with reflection can be see as a breach.

No in the sense that most system are anyway not entirely secure. A lot of classes can frequently be subclassed and you could potentially already abuse the system with just that. Of course classes can be made final, or sealed so that they can not be subclassed in other jar. But only few classes are secured correctly (e.g. String) according to this.

See this answer about final class for a nice explanation. See also the blog from Sami Koivu for more java hacking around security.

The security model of Java can be seen as insufficient to some regard. Some languages such as NewSpeak take even more radical approach to modularity, where you have access only to what is explicitly given to you by dependency inversion (by default nothing).

It's also important to note that security is anyway relative. At the language level, you can for instance not prevent a module form consuming 100% of CPU or consuming all memory up to a OutOfMemoryException. Such concerns need to be addressed by other means. We will maybe see in the future Java extended with resource utilization quotas, but it's not for tomorrow :)

I could expand more on the subject, but I think I've made my point.

Community
  • 1
  • 1
ewernli
  • 38,045
  • 5
  • 92
  • 123