Immutability (@wikipedia)
Aside from being a software design pattern, immutability plays a big role in security, as well. At least in an environment such as the Java security sandbox. The Sun Secure Coding Guidelines section about input and ouput talks about making immutable copies of inputs and outputs.
Even if you've never put serious thought into this, the importance of immutability quickly becomes obvious.
Steve: Let's say my method takes a (java.util) List of Strings as a parameter, performs some security validation on the list and then performs some privileged action on the list.
001 public class PrivilegedClass {
002
003 public void processStrings(java.util.List<String> strings) {
004 for (String str : strings) {
005 if (!isOk(str)) {
006 throw new SecurityException();
007 }
008 }
009
010 for (String str : strings) {
011 doPrivilegedOperation(str);
012 }
013 }
014
015 private void doPrivilegedOperation(String str) {
016 // privileged stuff
017 // code omitted
018 }
019
020 private boolean isOk(String str) {
021 // security check for string
022 // code omitted
023 return false;
024 }
025
026 }
Bob: The problem is that java.util.List is in interface and can be implemented in a mutable way. Also virtually all the public List implementations included in Java are mutable, so one wouldn't even have to go through the trouble of creating a mutable version - one could just use java.util.ArrayList.
The problem with the mutability is that an ill intentioned caller of the method could pass a list which contains different values while the method does its validation, and then another set of harmful values for the actual processing after the validation has passed. This could be done via timing, or by crafting one's own List implementation for a more exact attack. If the method uses .iterator() to iterate through the list, the first call to .iterator() (used by validation) could be made to return one set of Strings and the 2nd call to .iterator() (used by the post-validation processing) could be made to return another set of Strings.
It's a well known, generic problem.
Let's look at some ways to protect your method and what could go wrong with them. Forgive the silliness of some of these examples, it's just to give the notion of how easy it is to get it wrong.
Steve: I could change the method signature to only accept a more specialized StevesImmutableList.
003 public void processStrings(StevesImmutableList strings) {
Steve: I'd then define StevesImmutableList as a class whose constructor receives a String array and stores it in a private String array field and only has a getter method which returns the array.
001 package version1;
002
003 public class StevesImmutableList {
004
005 private String[] strings;
006
007 public StevesImmutableList(String[] strings) {
008 this.strings = strings;
009 }
010
011 public String[] getStrings() {
012 return this.strings;
013 }
014
015 }
Bob: Arrays are always mutable. The size can't be changed, but the contents (given its size is greater than zero) are free game. And your method is returning a reference to its internal array.
Steve: What if I define my private internal array as final?
Bob: That doesn't help. It'll guarantee that your private field always points to the same array, but the array contents are free game.
Steve: Better have the method call .clone() on the array and return a copy of the array instead of the internal array.
001 package version2;
002
003 public class StevesImmutableList {
004
005 private String[] strings;
006
007 public StevesImmutableList(String[] strings) {
008 this.strings = strings;
009 }
010
011 public String[] getStrings() {
012 return this.strings.clone();
013 }
014
015 }
Bob: Your constructor is receiving an array and storing it as the internal array. A caller with bad intentions could create an array, keep a reference to it, pass it to your object and later on modify it.
Steve: Ok, let's call .clone() on the incoming array as well.
001 package version3;
002
003 public class StevesImmutableList {
004
005 private String[] strings;
006
007 public StevesImmutableList(String[] strings) {
008 this.strings = strings.clone();
009 }
010
011 public String[] getStrings() {
012 return this.strings.clone();
013 }
014
015 }
Steve: Let's also add an overloaded constructor that receives a List to improve the usability of this class.
001 package version4;
002
003 public class StevesImmutableList {
004
005 private String[] strings;
006
007 public StevesImmutableList(java.util.List<String> stringList) {
008 this.strings = stringList.toArray(new String[stringList.size()]);
009 }
010
011 public StevesImmutableList(String[] strings) {
012 this.strings = strings.clone();
013 }
014
015 public String[] getStrings() {
016 return this.strings.clone();
017 }
018
019 }
Bob: Now you're calling toArray() on a List object that you can't really trust. It could return an array and keep a reference to that array for itself for future modification.
Steve: Ok, let's do a manual conversion of the List to an array.
001 package version5;
002
003 public class StevesImmutableList {
004
005 private String[] strings;
006
007 public StevesImmutableList(java.util.List<String> stringList) {
008 this.strings = new String[stringList.size()];
009 for (int i = 0; i < stringList.size(); i++) {
010 this.strings[i] = stringList.get(i);
011 }
012 }
013
014 public StevesImmutableList(String[] strings) {
015 this.strings = strings.clone();
016 }
017
018 public String[] getStrings() {
019 return this.strings.clone();
020 }
021
022 }
Steve: It'd also be nice to be able to serialize this object for passing between JVMs and/or storing object state somewhere. So let's make the class implement Serializable.
001 package version6;
002
003 public final class StevesImmutableList implements java.io.Serializable {
004
005 private String[] strings;
006
007 public StevesImmutableList(java.util.List<String> stringList) {
008 this.strings = new String[stringList.size()];
009 for (int i = 0; i < stringList.size(); i++) {
010 this.strings[i] = stringList.get(i);
011 }
012 }
013
014 public StevesImmutableList(String[] strings) {
015 this.strings = strings.clone();
016 }
017
018 public String[] getStrings() {
019 return this.strings.clone();
020 }
021
022 }
Bob: Serial data can be constructed in such a way that when read, will contain a String array, followed by a StevesImmutableList containing the same String array in the private field, thus making the object mutable.
Steve: Let's make the private field transient and create a readObject method which reads the array from the stream and then clones it before assigning it to the field. Also create a compatible writeObject method.
001 package version7;
002
003 public final class StevesImmutableList implements java.io.Serializable {
004
005 private transient String[] strings;
006
007 public StevesImmutableList(java.util.List<String> stringList) {
008 this.strings = new String[stringList.size()];
009 for (int i = 0; i < stringList.size(); i++) {
010 this.strings[i] = stringList.get(i);
011 }
012 }
013
014 public StevesImmutableList(String[] strings) {
015 this.strings = strings.clone();
016 }
017
018 public String[] getStrings() {
019 return this.strings.clone();
020 }
021
022 private void readObject(java.io.ObjectInputStream s)
023 throws java.io.IOException, ClassNotFoundException {
024 String[] streamStrings = (String[]) s.readObject();
025 this.strings = streamStrings.clone();
026 }
027
028 private void writeObject(java.io.ObjectOutputStream s)
029 throws java.io.IOException {
030 s.writeObject(this.strings);
031 }
032 }
Now some examples from core Java classes
java.lang.reflection.Proxy
Proxy has a method (getProxyClass) which takes a ClassLoader and an array of Class objects, that are supposed to be interfaces, as parameters. It performs some validation on the Class array, and then it dynamically defines and returns a new Class for a dynamic Proxy class which implements all the interfaces of the Class array. If the validation fails, the method throws an Exception (and obviously doesn't return anything).
The getProxyClass uses the user-suplied Class array for the validation and later on for the construction of the Proxy class, so it could be called with an array which has one set of classes for the validation and another set of classes for the Proxy construction.
However, the validation here, as far as I can tell, is strictly for usability. Without the validation, if you gave the method an invalid set of parameters it would sometimes fail with some strange class verification exception. With the validation, the caller gets feedback which is more readily understandable.
java.lang.String
Strings are supposed to be immutable. A lot of things depend on the immutability of Strings. There is no defensive programming in the core classes against mutable Strings (which makes sense, because it'd take a lot of work).
The immutability of Strings hinges on the fact that one cannot access the char array in which the String contents are stored. Now, String does leak the internal array to any registered CharSet who wants it. However, registering a CharSet requires a CharSetProvider and that, in turn, requires the charsetProvider permission.
Unsigned applets don't have that permission and can't create their own registered CharSets. But it is something to keep in mind. If you're granting the charsetProvider permission, you're pretty much implicitly giving away full access.
2 comments:
Thanks for the nice blog post, keep up the good work!
Nice post. There is one more thing preventing applets from using CharSetProvider - that is bug 4619777 :P
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4619777
Apparently charset providers are loaded by 'ClassLoader.getSystemClassLoader()' even though the documentation states it should be a context class loader. Hence it is impossible to define a custom charset provider from an applet with or without the charsetProvider permission. This is also the case for many other services.
Post a Comment