http://www.developer.com/

Back to article

Java Language Security: Controlling Access to a Class


December 8, 2006

This series, The Object-Oriented Thought Process, is intended for someone just learning an object-oriented language and who wants to understand the basic concepts before jumping into the code, or someone who wants to understand the infrastructure behind an object-oriented language he or she is already using. These concepts are part of the foundation that any programmer will need to make the paradigm shift from procedural programming to object-oriented programming.

Click here to start at the beginning of the series.

In keeping with the code examples used in the previous articles, Java will be the language used to implement the concepts in code. One of the reasons that I like to use Java is because you can download the Java compiler for personal use at the Sun Microsystems Web site http://java.sun.com/. You can download the standard edition, J2SE 5.0, at http://java.sun.com/j2se/1.5.0/download.jsp to compile and execute these applications. I often reference the Java J2SE 5.0 API documentation and I recommend that you explore the Java API further. Code listings are provided for all examples in this article as well as figures and output (when appropriate). See the first article in this series for detailed descriptions for compiling and running all the code examples.

This column covers the topic of Java Class access security. Here, you explore a very basic aspect of security that a Java developer would be concerned about at the design level.

Security in Reuse

Classes are designed, implemented, and deployed with reuse in mind. Although there are situations when a class may be created for a very specific use, perhaps even a one-time use, the basic object-oriented approach is to design a class with reuse as a primary goal.

It is an interesting academic exercise to debate the various ways that a class can be used, and reused. However, there are really only two distinct ways that a class can utilize a previously written class: by extending it (inheritance) or by referencing it (composition).

Note: For this discussion, you will consider a reference to be either a reference to an object passed into the class as a parameter or an object created in the class itself. These concepts will be explored in more detail later in this article.

Thus, you can say that, in general, a class can be reused in two different ways, composition (has-a) and inheritance (is-a).

For example, composition can be used when various Car classes incorporate an Engine class. Consider the real-life scenario where two different car models use the same type of engine—in these cases, the cars are a composite (has-a) of the engine and other reusable parts.

class Engine {


}

class Car01 {

   Engine engine = new Engine();

}

class Car02 {

   Engine engine = new Engine();

}

Listing 1: Composition Reuse

In the inheritance model, you can say that an Employee is-a type of Person. You can create a Person class that has a name attribute because you can assume that all people have a name. Thus, if the Employee inherits from Person, the Employee will inherit the name attribute.

The beauty of the inheritance approach is that you then can create a class called Vendor (assuming a vendor is a Person) and have the Vendor class inherit from Person as well. Thus, the Employee class and the Vendor class can both utilize, and reuse, the code contained in the Person class.

class Person {

   String name;

}

class Employee extends Person {

}

class Vendor extends Person {

}

Listing 2: Inheritance Reuse

Uncontrolled access in reuse

While the concept of reuse is a very powerful design mechanism, it does present some security issues – there is the potential of providing unlimited access to the class and the attributes and methods of that class.

Let me illustrate this concept by creating a class called CheckingAccount that includes two attributes, accountNumber and accountBalance. To provide uncontrolled access to these attributes, you declare them as public—you will soon see why this is not a good design practice.

Note: You should immediately balk at declaring these attributes as public. By dong so, you are breaking one of the most important object-oriented rules—all attributes must be declared private with access to them controlled via methods.
class CheckingAccount {

   public String accountNumber;
   public double accountBalance = 1000000.00;

}

Listing 3: Uncontrolled Access

The problem with this approach can be seen immediately by creating another class called AccountList that uses composition to use the CheckingAccount class. Because the attributes were declared as public, the author of the class can actually inspect the contents of the attributes, for example, the accountBalance.

class AccountList {
   public static void main(String args[]) {
      CheckingAccount myList = new CheckingAccount();
      System.out.println("Balance = " + myList.accountBalance);
   }
}

Listing 4: Inspecting Unprotected Attributes

When this class is executed as an application, you obtain the results as seen in Figure 1.

Figure 1: Obtaining Access to an Unprotected Attribute

Complete control of access in reuse

Although using the public access modifier for attributes breaks the encapsulation rule, you can go to the other extreme by making all of the attributes private.

class CheckingAccount {

   private String accountNumber;
   private double accountBalance = 1000000.00;

}

Listing 5: Controlled Access

Now, when you attempt to execute the same application (see Listing 4), access to the accountBalance is prohibited—in fact, you can't even compile the code.

Figure 2: Compiler Controlling Access to an Protected Attribute

By adhering to the rules of data hiding, you can prohibit other classes from incorporating your class and directly accessing attributes. However, this level of data protection does pose a significant limitation. For example, assume that you want to use inheritance in your design. Create a class called PlatinumChecking that inherits the CheckingAccount class. In this case, a Platinum account contains all of the advantages of a regular checking account and provides some added benefits to select customers.

class CheckingAccount {
   private String accountNumber;
   private double accountBalance = 1000000.00;
}
// PlatinumChecking
public class PlatinumChecking extends CheckingAccount {

   public void PlatinumChecking() {
      System.out.println("Balance = " + accountBalance);
   }

}
class AccountList {
   public static void main(String args[]) {
      PlatinumChecking myList = new PlatinumChecking();
      System.out.println("Balance = " + myList.accountBalance);
   }
}

Listing 6: Controlled Access Using Inheritance

Now, when you attempt to execute the AccountList application yet again (Listing 6), access to the accountBalance is still prohibited—the private access designation prohibits even a subclass from inspecting it.



Click here for a larger image.

Figure 3: Compiler Controlling Access to a Protected Attribute

You have encountered the two extremes of security in terms of access members of a class. Using the public access modifier basically leaves the class member totally unprotected, whereas the private access modifier explicitly prohibits any other class from accessing the item directly—even a subclass.

There are other access modifiers besides public and private. In the rest of the article, you will explore the subtleties of using the various access modifiers and when to use them in your design.

Access level modifiers

Before you specifically look at other individual access modifiers, it is helpful to look at a table that is provided on the Sun web site. This table indicates the access levels permitted by each of the modifiers.

Access Levels
Modifier Class Package Subclass World
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N

Source: http://java.sun.com/docs/books/tutorial/java/javaOO/accesscontrol.html

Table 1: Class Access Levels

As you determined in your code examples, the table confirms that the public access modifier allows unlimited access to the class, the package, and any subclass as well any other class (in this table designation, the world). Likewise, the table indicates that the private modifier prohibits access to any outside classes.

Protected level access modifiers

To solve your problem with your inheritance example, you will have to use the protected access modifier to gain access to the accountBalance attribute.

class CheckingAccount {

   protected String accountNumber;
   protected double accountBalance = 1000000.00;

   public double getAccountBalance() {

      return (accountBalance);

   }

}

class PlatinumChecking extends CheckingAccount {

}

class AccountList {

   public static void main(String args[]) {

      PlatinumChecking myList = new PlatinumChecking();

      System.out.println("Balance = " + myList.getAccountBalance());

   }
}

Listing 7: Protected Access using Inheritance

It is important to note that to gain access to the protected attribute from the AccountList application, you had to provide the getAccountBalance() method in the CheckingAccount class. As can be seen in Table 1, the protected access modifier not only allows access to subclasses, but also to any class in the same package.

// CheckingAccount

package Account;

public class CheckingAccount {

   protected String accountNumber;
   protected double accountBalance = 1000000.00;

   public double getAccountBalance() {

      return (accountBalance);

   }

}

// PlatinumChecking
package Account;

public class PlatinumChecking extends CheckingAccount {

}

// AccountList
package Account;

public class AccountList {

   public static void main(String args[]) {

      PlatinumChecking myList = new PlatinumChecking();

      System.out.println("Balance = " + myList.accountBalance);

   }
}

Listing 8: Protected Access using Packages

The interesting issue with the code in Listing 8 is that the AccountList application has been placed in the Account package:

// AccountList
package Account;

Also, note that you are directly accessing the accountBalance attribute—which should not be allowed.

      System.out.println("Balance = " + myList.accountBalance);

When this code is compiled, it compiles cleanly, confirming that the protected access modifier allows all classes within the package full access.

Figure 4: Protected Access using Packages

Note: You instantiated a PlatinumChecking object in the AccountList application., and not a CheckingAccount object. This is important because using the getAccountBalance() method is what actually requires the protected access modifier.

Security and the default level access modifier

While the protected access modifier allows you to access attributes and methods within a package and a subclass, the default access modifier allows access to classes in the package. However, it restricts access to subclasses.

Subclass
Access Levels
Modifier Class Package World
no modifier (default) Y Y N N

Listing 9 and Figure 6 illustrates this behavior.

Note: Because classes in the same directory are considered to be in the same package, the PlatinumChecking class was moved out of the Account directory. You can see this in the command line in Figure 5.
// CheckingAccount
package Account;

public class CheckingAccount {

   String accountNumber;                  // no access modifier (default)
   double accountBalance = 1000000.00;    // no access modifier (default)

   public double getAccountBalance() {

      return (accountBalance);

   }

}

// PlatinumChecking
import Account.*;

public class PlatinumChecking extends CheckingAccount {

   public void PlatinumChecking() {

      accountBalance=0;    // subclass can't access
   }

}

// AccountList
import Account.*;

public class AccountList {

   public static void main(String args[]) {

      PlatinumChecking myList = new PlatinumChecking();

   }
}

Listing 9: Default Access using Inheritance & Packages



Click here for a larger image.

Figure 5: Default Access using Packages

Why would this be a security issue? By using the default access modifier, a subclass is prevented from altering an attribute in a parent class without going through the security constraints in that parent class. For example, rather than directly changing an attribute as follows:

      accountBalance=0;    // subclass can't access

The subclass would be required to call a method in the superclass to access/change the attribute.

Conclusion

Once of the great advantages that Java brought to the table when it was first introduced was that several levels of security were built directly into the language itself. In this article, you explore just one of those security concerns.

The concept of secure access levels is even more interesting when you consider that this type of security is enforced at the compilation level. In other words, if properly designed, security anomalies are caught prior to the creation of the bytecodes, so the application can never be executed. This not only makes the code more secure, it also assists the software development process itself.

There are several other compiler enforced security concepts to explore in future articles, as well as security issues enforced by the Virtual Machine.

References

About the Author

Matt Weisfeld is a faculty member at Cuyahoga Community College (Tri-C) in Cleveland, Ohio. Matt is a member of the Information Technology department, teaching programming languages such as C++, Java, C#, and .NET as well as various Web technologies. Prior to joining Tri-C, Matt spent 20 years in the information technology industry gaining experience in software development, project management, business development, corporate training, and part-time teaching. Matt holds an MS in computer science and an MBA in project management. Besides The Object-Oriented Thought Process, which is now in its second edition, Matt has published two other computer books, and more than a dozen articles in magazines and journals such as Dr. Dobb's Journal, The C/C++ Users Journal, Software Development Magazine, Java Report, and the international journal Project Management. Matt has presented at conferences throughout the United States and Canada.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date