Name: rmT116609 Date: 08/20/2003
A DESCRIPTION OF THE REQUEST :
I realise that this idea has been brought up (and shut down) a number of times. However, I think that this problem with the Java language is significant enough to warrant yet another attempt at getting it addressed.
The Java language's policy of returning references from methods by default is a great idea to reduce program overhead without complicating the issue with address operators. However, this benefit in efficiency and performance creates a potentially serious security risk.
To allow clients to read private or protected objects, a class (particularily if it's a JavaBean) will provide get methods intended to allow the client to read the encapsulated object by returning it to the client. In C++, this never posed a security risk because the method could declare the return type as const reference, meaning that the client couldn't modify a protected object.
I realise that Sun has removed this feature from Java to simplify writing methods, but, as the example will try to illustrate, this action has weakened encapsulation and security because the returned object can be manipulated almost as easily as if it was declared public.
I also realise that Sun wants to avoid C++ features from getting into Java unless if they are absolutely necessary. Therefore, being able to explicitly declare return types final may not be the best solution.
However, may I suggest that Sun implements a policy into Java whereby methods can only return immutable references to private, protected, and/or package objects by default. Put another way, non-final objects with private, protected, and/or package access would be mutable within its own class but immutable outside of it.
(If Sun is still concerned about the implications of creating this policy for private, protected, and package access objects, may I suggest that Sun starts by enforcing this policy for private access modifiers)
JUSTIFICATION :
First: a lot of people are pushing for it. They don't see it as a C++ feature but a security and performance issue. I am aware of a number of similar RFEs that request this feature but sun has closed. Here are a couple of forums that have tried to address the issue without much success:
http://forum.java.sun.com/thread.jsp?forum=31&thread=417927
http://forum.java.sun.com/thread.jsp?forum=31&thread=330663
http://forum.java.sun.com/thread.jsp?forum=54&thread=289105
http://forum.java.sun.com/thread.jsp?forum=31&thread=186158&start=30&range=15&hilite=false&q=
http://www.javaworld.com/javaworld/jw-11-1999/jw-11-letters.html
http://forum.java.sun.com/thread.jsp?forum=31&thread=280669
Second: this loophole in Java's encapsulation policy irrelevates the point of even having private or protected objects in classes, since object returns can bypass the protection. It is no use suggesting that programmers should avoid returning sensitive objects because there will invariably be situations in which a class has to allow a client class to read (but not write) an object's information.
Third: I believe that returning immutable references to private and protected objects makes logical sense for the same reason that Java doesn't allow a program to read an array outside of its bounds. It enforces the principle of least privilage by only allowing a client that wants to read the object just enough access to do the job. If that client wants writing access, it should use a set method because the set method can make sure that the data is safe and is formatted properly before assigning it to the object.
Fourth: inexperienced or lazy programmers may not be aware that returning references theoretically allows direct access to the original object, regardless of any Fort Knox provisions that the class or method tries to make on protecting the encapsulated data. Therefore, such users may not be aware of how potentially unsafe their libraries are before they distirbute them.
Fifth: most programmers probably won't have to change their software because they already design and use methods that return encapsulated objects in a read-only context.
Sixth: for the other programmers who design get methods intending to manipulate the original object, this new policy would not mean that they have to re-design their programs from scratch. In fact, it would probably reduce errors in their programs because there wouldn't be any ambiguity as to whether the client got and manipulated a copy or the original object. Where the client intends to manipulate the original, encapsulated object, the programmer would simply have to replace
Object someObject = someClass.getObject();
someObject.callMutatorMethod( int paramater ); //did that just modify the original or a copy?
with
Object someObject = someClass.getObject().clone(); //creates a mutable copy
someObject.callMutatorMethod( integerParamater ); //syntax error if clone() was not called
someClass.setObject( someObject );
which, I think, is better because the program is now crystal clear as to what the client is doing with the object and is safer because the setObject method can make sure that someObject is formatted correctly. The clone and extra set call may take some overhead, but a whole lot less than someClass having to return copies of all of its objects to make sure that they don't get accidentally or maliciously modified.
---------- BEGIN SOURCE ----------
/*BankBranch.java: a class that contains a private object as a data member and is designed to maintain information about a bank branch (hypothetically)*/
public class BankBranch {
private BankAccount aReallyImportantBankAccount;
/* assume that BankAccount.java exists and contains method setSocialInsuranceNumber() */
//insert constructor, mutators, and other methods here
public BankAccount getAReallyImportantBankAccount
{return aReallyImportantBankAccount; }
}
// end of BankBranch.java
/* BadMrFrosty.java: a malicious, or poorly designed, driver class that changes the bank account's Social Insurance number (SIN), when, in fact, only the BankBranch should be allowed to do this, which is why the BankBranch.java file made aReallyImportantBankAccount private */
public class BadMrFrosty {
public static void main( String[] args ) {
BankBranch centralBank = new BankBranch( "Central" );
/* assume that the constructor loads Central branch's information from file, which is the expected and fair behavour */
BankAccount account = centralBank.getAReallyImportantBankAccount();
account.setSocialInsuranceNumber( 666666666 );
}
}
//end BadMrFrosty.java
/* now BadMrFrosty would only mess up the BankAccount temporarily, unless if the accounts were also saved to a file, in which case aReallyImportantBankAccount would be corrupted */
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
The only known ways to get around this problem is to pass and return objects as clones, bury the private or protected object deep within a maze of wrapper classes and interfaces, or finalise all private or protected objects.
The problem with the former is that if a program passes a container (List, Vector, array) by cloning it, the clone() method will not copy the data members, but just reference them. This means that the client can still alter the data members, making cloning useless. Secondly, cloning a very large object just to protect the original private copy slows the program down significantly.
The problem with the latter solution, using wrappers and interfaces, is that it promotes ambiguity in software engineering. The extra code to try to 'throw off the scent' of the original private object complicates any attempts to use, maintain, and update the code. In addition, this solution throws the focus of the programmer's task from writing the code to building mazes.
The third solution, finalisation, simply doesn't work if the class needs to internally modify a private or protected object in runtime.
(Incident Review ID: 190085)
======================================================================