JDK-4910043 : Provide constant returns for private and protected objects
  • Type: Enhancement
  • Component: specification
  • Sub-Component: language
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2003-08-20
  • Updated: 2007-02-20
  • Resolved: 2007-02-20
Related Reports
Relates :  
Relates :  
Description

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) 
======================================================================

Comments
EVALUATION Most programmers understand that leaking a reference to a private member can compromise an object, so employ defensive copying where necessary. The cure proposed here is worse than the illness: treating the return of a private/protected member specially will cause confusion, and treating it specially *by default* is out of the question for compatibility reasons. A general immutable type facility is more likely.
20-02-2007

EVALUATION The argument made here for "const" return types is a security argument. It should not be dismissed lightly, but I still believe it is misguided. Adding language features is not going to enhance security. As a rule, security flaws are properties of complex systems that have too many features. In any event, we are in no position to change the semantics of existing programs. I think there is much to be said for the style the RFE recommends, but I don't see value in the language enforcing this in any way. Let me also add: 1. Access control in Java is used to restrict access to members, not to objects. This misconception pervades this RFE nad I feel obliged to correct it. 2. Even immutable objects can pose security risks if they expose information that should be kept private. ###@###.### 2003-08-20 The C++ notion of "const" doesn't provide any security, as users can circumvent it with a cast. A similar mechanism in Java would be similarly useless for protection of data. The common idiom for addresing this need is a wrapper class. See, for example, Collections.unmodifiable*. In addition, beginning with 1.5, you can use interfaces to accomplish the same thing. Write an interface that represents the "readonly" object, and return that instead from methods that are to return a readonly view. You can use covariant returns if methods in the readonly view must return readonly views of other objects. Be aware, though, that this provides exactly as much "security" as C++ const: none, due to the possibility of the client using a cast. ###@###.### 2003-08-20
20-08-2003