In a Java Virtual Machine (JVM), "Two classes are the same class (and consequently the same type) if they are loaded by the same class loader and they have the same fully qualified name" [JVMSpec 1999]. Two classes with the same name but different package names are distinct, as are two classes with the same fully qualified name loaded by different class loaders.

It could be necessary to check whether a given object has a specific class type or whether two objects have the same class type associated with them, for example, when implementing the equals() method. If the comparison is performed incorrectly, the code could assume that the two objects are of the same class when they are not. As a result, class names must not be compared.

Depending on the function that the insecure code performs, it could be vulnerable to a mix-and-match attack. An attacker could supply a malicious class with the same fully qualified name as the target class. If access to a protected resource is granted based on the comparison of class names alone, the unprivileged class could gain unwarranted access to the resource.

Conversely, the assumption that two classes deriving from the same codebase are the same is error prone. Although this assumption is commonly observed to be true in desktop applications, it is typically not the case with J2EE servlet containers. The containers can use different class loader instances to deploy and recall applications at runtime without having to restart the JVM. In such situations, two objects whose classes come from the same codebase could appear to the JVM to be two different classes. Also note that the equals() method might not return true when comparing objects originating from the same codebase.

Noncompliant Code Example

This noncompliant code example compares the name of the class of object auth to the string "com.application.auth.DefaultAuthenticationHandler" and branches on the result of the comparison:

 // Determine whether object auth has required/expected class object
 if (auth.getClass().getName().equals(
      "com.application.auth.DefaultAuthenticationHandler")) {
   // ...
}

Comparing fully qualified class names is insufficient because distinct class loaders can load differing classes with identical fully qualified names into a single JVM.

Compliant Solution

This compliant solution compares the class object auth to the class object for the canonical default authentication handler:

 // Determine whether object auth has required/expected class name
 if (auth.getClass() == com.application.auth.DefaultAuthenticationHandler.class) {
   // ...
}

The right-hand side of the comparison directly names the class of the canonical authentication handler. In the event that the canonical authentication handler had not yet been loaded, the Java runtime manages the process of loading the class. Finally, the comparison is correctly performed on the two class objects.

Noncompliant Code Example

This noncompliant code example compares the names of the class objects of x and y using the equals() method. Again, it is possible that x and y are distinct classes with the same name if they come from different class loaders.

// Determine whether objects x and y have the same class name
if (x.getClass().getName().equals(y.getClass().getName())) {
  // Objects have the same class
}

Compliant Solution

This compliant solution correctly compares the two objects' classes:

// Determine whether objects x and y have the same class
if (x.getClass() == y.getClass()) {
  // Objects have the same class
}

Risk Assessment

Comparing classes solely using their names can allow a malicious class to bypass security checks and gain access to protected resources.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

OBJ09-J

High

Unlikely

Low

P9

L2

Automated Detection

ToolVersionCheckerDescription
The Checker Framework

2.1.3

Signature String CheckerEnsure that the string representation of a type is properly used for example in Class.forName (see Chapter 13)
Parasoft Jtest
2023.1
CERT.OBJ09.CMPDo not compare Class objects by name
PVS-Studio

7.30

V6054
SonarQube

9.9

S1872Classes should not be compared by name

Related Guidelines

MITRE CWE

CWE-486, Comparison of Classes by Name

Bibliography

[Christudas 2005]

Internals of Java Class Loading

[JVMSpec 1999]

§2.8.1, Class Names

[McGraw 1998]

"Twelve Rules for Developing More Secure Java Code"

[Wheeler 2003]

Java Secure Programming for Linux and UNIX HOWTO



5 Comments

  1. My only quibble with this is the Risk Assessment section. I suspect the severity is high, because a malicious class that can pass off as the proper class can execute arbitrary code. I'm not sure what the liklihood will be. Guessing from the rule's statements, it may be 'improbable' since it would only occur on J2EE servers, but I'm not sure how easy an exploit would be in that environment.

  2. I'm curious as to why the compliant solution uses such complicated syntax which is equivalent to the much simpler:

    if (auth.getClass() == com.application.auth.DefaultAuthenticationHandler.class)

    is the solution assuming you have to check using a class name string?

    1. Don't know, but even if so seems best to have your version as a first CS b/c its a much simpler example.

    2. I can't say why it started out that way, as it predates my involvement with the project. That said, I failed to spot the simpler approach in the run-up to publication.  Thanks for pointing it out.

      I've added the simpler CS, and modified the original to note that the simpler version is preferred.

      1. I went the next step and removed the non-preferred solution.  We don't need to list every solution; this one just cluttered the rule.