Length Extension Attacks Anti-Pattern
Severity: High
Summary
Hash length extension attacks exploit Merkle-Damgård construction vulnerabilities in MD5, SHA-1, and SHA-256. Attackers knowing hash(secret + message) and secret length can compute hash(secret + message + padding + attacker_data) without knowing the secret. This enables appending data to signed messages with valid signatures, completely breaking message integrity and authentication.
The Anti-Pattern
Never use vulnerable hash functions (MD5, SHA-1, SHA-256) in hash(secret + message) construction for MACs. Use HMAC instead.
BAD Code Example
# VULNERABLE: Using hash(secret + message) for message signature
import hashlib
SECRET_KEY = b"my_super_secret_key_16b" # 16 bytes
def get_signed_url(message):
# Signature created by prepending secret to message and hashing
# Vulnerable to length extension
signature = hashlib.sha256(SECRET_KEY + message.encode()).hexdigest()
return f"/api/action?{message}&signature={signature}"
def verify_request(message, signature):
expected_signature = hashlib.sha256(SECRET_KEY + message.encode()).hexdigest()
return signature == expected_signature
# 1. Legitimate URL generated:
# Message: "user=alice&action=view"
# URL: /api/action?user=alice&action=view&signature=...
# 2. Attacker intercepts URL. Knows signature and message.
# Doesn't know SECRET_KEY but can guess length (16 bytes)
# 3. Using `hashpump`, attacker computes new valid signature for extended message
# Original: "user=alice&action=view"
# Extended: "user=alice&action=view" + padding + "&action=delete&target=bob"
# Tool generates new signature and message with padding
# 4. Server receives forged request, recomputes hash of `SECRET_KEY + extended_message`,
# finds it matches attacker's signature. Delete action processed
GOOD Code Example
# SECURE: Use HMAC (Hash-based Message Authentication Code)
import hmac
import hashlib
SECRET_KEY = b"my_super_secret_key_16b"
def get_signed_url_secure(message):
# HMAC designed to prevent length extension attacks
# Two-step hashing: hash(key XOR opad, hash(key XOR ipad, message))
signature = hmac.new(SECRET_KEY, message.encode(), hashlib.sha256).hexdigest()
return f"/api/action?{message}&signature={signature}"
def verify_request_secure(message, signature):
expected_signature = hmac.new(SECRET_KEY, message.encode(), hashlib.sha256).hexdigest()
# Use hmac.compare_digest for constant-time comparison, prevents timing attacks
return hmac.compare_digest(signature, expected_signature)
# Attacker cannot extend HMAC-signed message without secret key
# Inner hash `hash(key XOR ipad, message)` prevents continuing hash chain
Language-Specific Examples
JavaScript/Node.js:
// VULNERABLE: hash(secret + message) construction
const crypto = require('crypto');
const SECRET = 'my_secret_key';
function signMessage(message) {
// Vulnerable to length extension!
const signature = crypto.createHash('sha256')
.update(SECRET + message)
.digest('hex');
return signature;
}
// SECURE: Use HMAC
const crypto = require('crypto');
const SECRET = 'my_secret_key';
function signMessageSecure(message) {
const signature = crypto.createHmac('sha256', SECRET)
.update(message)
.digest('hex');
return signature;
}
// Verify with constant-time comparison
function verifySignature(message, signature) {
const expected = signMessageSecure(message);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Java:
// VULNERABLE: Manual hash(secret + message)
import java.security.MessageDigest;
public class InsecureSigning {
private static final String SECRET = "my_secret_key";
public static String signMessage(String message) throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
// Vulnerable to length extension!
String combined = SECRET + message;
byte[] hash = digest.digest(combined.getBytes());
return bytesToHex(hash);
}
}
// SECURE: Use HMAC
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
public class SecureSigning {
private static final String SECRET = "my_secret_key";
public static String signMessage(String message) throws Exception {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(
SECRET.getBytes(), "HmacSHA256");
hmac.init(secretKey);
byte[] hash = hmac.doFinal(message.getBytes());
return bytesToHex(hash);
}
// Constant-time comparison
public static boolean verifySignature(String message, String signature)
throws Exception {
String expected = signMessage(message);
return MessageDigest.isEqual(
signature.getBytes(),
expected.getBytes()
);
}
}
Detection
- Find hash(secret + message) patterns: Grep for concatenation before hashing:
rg 'hashlib\.(md5|sha1|sha256)\(.*\+' --type pyrg 'crypto\.createHash.*update.*\+' --type jsrg 'MessageDigest.*update.*\+' --type java- Look for
hash(key + data)orhash(data + key)patterns
- Identify vulnerable hash functions for MACs: Search for signing without HMAC:
rg 'hashlib\.(md5|sha1|sha256)' --type py | rg -v 'hmac'rg 'crypto\.createHash\(' --type js | rg -v 'createHmac'rg 'MessageDigest\.getInstance.*MD5|SHA-1|SHA-256' --type java | rg -v 'Mac\.getInstance'
- Audit signature generation: Find custom MAC implementations:
rg 'signature.*=.*hash|mac.*=.*hash' -i- Check API signatures, token generation, cookie signing
- Use static analysis: Run tools to detect weak crypto:
- Semgrep:
python.lang.security.audit.hashlib-weak-hash - Bandit:
B303(MD5/SHA1 usage)
- Semgrep:
Prevention
- Use HMAC: Always use HMAC for message authentication codes. Industry standard, immune to length extension attacks, available in standard libraries
- Choose secure hash functions: Use HMAC with SHA-256 or SHA-3
- Never roll your own crypto: Avoid custom schemes like
hash(message + secret)orhash(secret + message + secret). Use HMAC - Alternative if HMAC unavailable: Use SHA-3 or BLAKE2 (not vulnerable to length extension). HMAC still preferred
Related Security Patterns & Anti-Patterns
- Weak Encryption Anti-Pattern: Part of broader cryptographic failures category
- Timing Attacks Anti-Pattern: Use constant-time comparison for signature verification to prevent timing leaks