In Java 101, we're taught:
A
String
is immutable.
Yes. Good. Thanks.
Then we get to Java 102 (or perhaps Java 201), and we discover:
A
String
isn't really immutable: you can change it using reflection.
Ah. Fine. Either quite cute or immeasurably perverse, depending on your perspective.
These things, thus far, have been discussed ad infinitum on Stack Overflow and elsewhere. I am taking this much for granted in framing this question.
What I'm interested to ask is this:
Once we discover that a
String
isn't really immutable, what are the implications for how we should write our code?
Let me make that more specific in two respects.
Is it possible for a malicious class to use this trick to break the security of the JVM, and get up to tricks that it shouldn't? Are there places in the JDK, for instance, where a String
is returned from a method, and it's safe only on the assumption that it can't be changed?
Here's a promising start:
String prop = "java.version";
// retrieve a System property as a String
String s = System.getProperty(prop);
System.out.println(s);
// now mess with it
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[])field.get(s);
value[0] = 'x';
//turns out we've changed not just the String we were
//given but the underlying property too!
System.out.println(System.getProperty(prop));
What this does is to retrieve a system property from the JVM, which comes back as a String
, and then alter the String
; the consequence is that the underlying property is changed too. Can this be used to wreak havoc?
I'm not certain. It's worth noting that you have to have the right permissions to perform reflection. Is the security game already up by that point? It allows me here to get round not having permissions to change security properties, but is that to be expected, or is it a problem?
Are there ways of extending this to do something much worse?
We're always told that it's a good idea to clone arrays and other objects before passing them to methods that might modify them, unless we want them to be modified. It's just good coding practice. It's your own silly fault if you give someone the only copy of your array and it comes back messed with.
But it looks as though the same argument ought to apply to a String
! I have never, ever heard anyone say that we ought to clone a String
before passing it to a method someone else has written. But how is this any different, if a String
is as mutable as an array?
If defensive cloning is all about not really knowing what a method might do to the things we pass it, and if what we're passing it is mutable, then we ought to clone it. If the code might be malicious, then it might alter a String
; so whenever it's important that the String
stay unchanged, we should make sure we send a copy rather than the real thing.
It won't wash to say that untrusted code ought to be run under a security manager if we don't trust it not to do bad things to the String
. If that were true for a String
, it would be true for an array; but no one ever says that cloning of arrays and other objects is only to be done in cases where you're also locking the code down with a security manager.
An array might get modified just by sloppy coding; in other words, it might get modified unintentionally. But no one alters a String
unintentionally: it can be done only with sneaky tricks that mean the programmer was trying to break immutability.
That makes the two cases different. We really shouldn't be passing an array, even a clone of it, to code that we don't trust on any level. You don't download a library from somewhere dodgy and then think you're OK because you cloned your array. You clone defensively because you think the programmer might be making mistakes or making different assumptions from you about what's allowable with the data you're sending. If you're worried that the code might be modifying String
s behind your back, you really shouldn't be running the code at all. All you achieve by cloning a String
is performance overhead.
How should we think about this? Can JVM sandboxing be broken using these tricks? And should we code defensively in the light of the mutability of a String
, or is this all a red herring?
Why Is String Immutable in Java? The key benefits of keeping this class as immutable are caching, security, synchronization, and performance.
Since String is immutable, its value can't be changed otherwise any hacker could change the referenced value to cause security issues in the application. Since String is immutable, it is safe for multithreading. A single String instance can be shared across different threads.
There is a security setting that can be enabled for your Java program. According to the Javadocs for setAccessible
,
First, if there is a security manager, its checkPermission method is called with a ReflectPermission("suppressAccessChecks") permission.
A SecurityException is raised if flag is true but accessibility of this object may not be changed (for example, if this element object is a Constructor object for the class Class).
A SecurityException is raised if this object is a Constructor object for the class java.lang.Class, and flag is true.
So, with a SecurityManager
that doesn't allow this check, you can prevent any code from successfully calling setAccessible
, preserving the immutability of String
s.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With