Safeguard
Application Security•

CWE-581 Equals Without hashCode: Java Object Model Bug

If equal Java objects have different hash codes, hash collections misbehave. See how CWE-581 happens and how to fix it.

Safeguard Security Team
3 min read

CWE-581 is a Java rule violation: the class defines equals() or hashCode() but not both, so equal objects can have different hash codes.

Java expects an invariant. If a.equals(b) is true, then a.hashCode() == b.hashCode() must also be true. Hash based collections depend on it.

How it happens

A developer overrides equals() to compare by value, such as an account ID, and forgets hashCode(). The default hashCode() still uses object identity, so two equal objects hash to different buckets.

// Vulnerable: equals overridden, hashCode is not
public class Account {
    private final String id;
    public Account(String id) { this.id = id; }

    @Override
    public boolean equals(Object o) {
        return o instanceof Account a && a.id.equals(id);
    }
}

Set<Account> blocked = new HashSet<>();
blocked.add(new Account("u-42"));
blocked.contains(new Account("u-42"));   // usually false

The reverse also happens: hashCode() defined without equals() leaves identity comparison in place.

MITRE explains the effect: if the invariant is not upheld, it is likely to cause trouble when objects are stored in a collection, such as a key in a Hashtable or an element in a Map or Set.

What an attacker gains

MITRE lists integrity impact and other consequences. In security terms, think about what the collection is used for. If it is a block list, a set of revoked tokens, a cache of permission decisions or a de-duplication set, then lookups that wrongly return "not found" can let blocked items through or repeat protected actions.

How to find it

MITRE says automated static analysis can find some instances. Many analyzers flag a class that overrides one method but not the other. IDEs can also generate both methods together. A unit test that puts an object in a HashSet and looks up an equal copy catches it quickly.

How to fix it

MITRE's mitigation: both equals() and hashCode() should be defined.

// Fixed
@Override
public int hashCode() {
    return java.util.Objects.hash(id);
}

Use the same fields in both methods. For simple value classes, a Java record generates both consistently. Prefer immutable fields in keys, because changing a field after insertion can lose the object inside a hash collection.

The free in-browser scan at gold.safeguard.sh/scan lists the dependencies in your manifest. No login is needed, the file stays on your machine, and it handles up to 100 dependencies.

Quick check

What is the rule?

If two objects are equal, they must return the same hashCode.

Why does it matter for security?

Lookups in sets and maps can miss, which breaks block lists, revocation sets and caches.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.