This is an issue when an object’s initialization code can throw an exception and the class is not protected against attacks through cleanup hooks, which can execute on partially-constructed objects. Protection mechanisms include preventing inheritance, providing empty cleanup implementations that cannot be overridden, or using construction patterns that prevent direct instantiation.

In Java, these cleanup hooks are called finalizers, and the protection mechanisms specifically refer to declaring the class as final, or using the factory pattern. Implementing a final empty finalize() method was also a known solution, but finalize() has been deprecated since Java 9 and is marked for removal.

Why is this an issue?

When a constructor throws an exception, you might expect the object construction to fail completely and no reference to the object to exist. However, finalization or cleanup mechanisms can be exploited to obtain a reference to a partially-constructed object.

Here’s how a Finalizer attack works:

  1. An attacker creates a malicious derived class that overrides the cleanup/finalization method (the method automatically called by the runtime when an object is being destroyed)
  2. The attacker attempts to instantiate this derived class
  3. If the parent constructor throws an exception during initialization, the object is not fully constructed
  4. Despite the exception, the memory management system will eventually call the cleanup/finalization method on the partially-constructed object
  5. The malicious cleanup method can store a reference to the object being finalized, effectively "resurrecting" the broken object
  6. The attacker now has access to an object that bypassed security checks or validation logic in the constructor

This vulnerability is particularly dangerous for security-sensitive classes where the constructor performs:

If an attacker can obtain a reference to a partially-constructed instance, they may be able to call methods on an object in an inconsistent state, potentially bypassing security controls.

What is the potential impact?

Attacks exploiting cleanup mechanisms for incompletely initialized objects can lead to serious security vulnerabilities:

How to fix it

The simplest solution is to declare the class as final. This prevents attackers from creating malicious subclasses that override the finalize() method.

However, some frameworks such as Spring or JPA/Hibernate require non-final classes. In such cases, use a factory method with a private constructor to ensure the object is fully validated before any reference is exposed. Since the constructor is private, no malicious subclass can be created, achieving the same protection as final.

This rule targets classes whose constructors can throw exceptions and that are not protected against finalizer attacks. If the class is internal to your application and never exposed to untrusted code, or if the constructor cannot actually throw, the risk is lower and the finding may be acceptable as a won’t-fix. A genuine false positive would occur when the constructor is guaranteed not to throw, making the attack path impossible.

Code examples

Noncompliant code example

public class SecuritySensitiveClass {
    private final String credentials;

    public SecuritySensitiveClass(String credentials) throws AuthenticationException {
        if (!isValid(credentials)) {
            throw new AuthenticationException("Invalid credentials"); // Noncompliant
        }
        this.credentials = credentials;
    }

    private boolean isValid(String credentials) {
        return credentials != null && credentials.length() > 10;
    }
}

Compliant solution

public final class SecuritySensitiveClass { // Compliant: class is final
    private final String credentials;

    public SecuritySensitiveClass(String credentials) throws AuthenticationException {
        if (!isValid(credentials)) {
            throw new AuthenticationException("Invalid credentials");
        }
        this.credentials = credentials;
    }

    private boolean isValid(String credentials) {
        return credentials != null && credentials.length() > 10;
    }
}

When the class cannot be final because a framework requires subclassing or proxying, use a factory method with a private constructor:

public class SecuritySensitiveClass { // Compliant: constructor is private, instances are created through factory method
    private final String credentials;

    private SecuritySensitiveClass(String credentials) {
        this.credentials = credentials;
    }

    public static SecuritySensitiveClass create(String credentials) throws AuthenticationException {
        if (!isValid(credentials)) {
            throw new AuthenticationException("Invalid credentials");
        }
        return new SecuritySensitiveClass(credentials);
    }

    private static boolean isValid(String credentials) {
        return credentials != null && credentials.length() > 10;
    }
}

Resources

Documentation

Standards