Avoid MessageDigest Field

ID

java.avoid_message_digest_field

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Java

Tags

reliability

Description

Reports java.security.MessageDigest declared as an instance or static field of a class.

Rationale

MessageDigest is stateful: every call to update(…​) mutates the digest’s internal buffer, and the final digest() resets the state. The class is explicitly documented as not thread-safe.

Storing one in a shared field means concurrent calls to update()/digest() on the same instance interleave their state, producing wrong hashes or, worse, intermittent exceptions under load. The bug is usually invisible during single-threaded testing and only appears in production.

// Bad — shared mutable state; racy under concurrency
public class Hasher {
    private final MessageDigest digest = MessageDigest.getInstance("SHA-256");

    public byte[] hash(byte[] data) {
        digest.update(data);
        return digest.digest();
    }
}

Remediation

Create a fresh MessageDigest per use, or hold it in a ThreadLocal:

// Good — local instance, no sharing across threads
public byte[] hash(byte[] data) throws NoSuchAlgorithmException {
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    return digest.digest(data);
}

// Good — one instance per thread
private static final ThreadLocal<MessageDigest> DIGEST =
    ThreadLocal.withInitial(() -> {
        try { return MessageDigest.getInstance("SHA-256"); }
        catch (NoSuchAlgorithmException e) { throw new IllegalStateException(e); }
    });

For very hot code paths a pool of digests (with explicit checkout/return) is an alternative.