Broken Authentication in Spring Boot with Hmac Signatures
Broken Authentication in Spring Boot with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Broken Authentication occurs when authentication controls are implemented incorrectly, allowing attackers to compromise credentials, tokens, or session states. In Spring Boot applications that use Hmac Signatures for request authentication, the vulnerability typically arises from improper key management, signature validation logic, or handling of replay and timing attacks.
Hmac Signatures rely on a shared secret to generate and verify a cryptographic hash (e.g., HMAC-SHA256) over request components like HTTP method, URI, headers, and body. If the server-side verification does not use a constant-time comparison, an attacker can exploit timing differences to learn about the correct signature. Additionally, if the server accepts unsigned or weakly signed requests due to misconfigured filters or missing validation, an attacker can forge requests by guessing or leaking the secret.
Spring Boot applications often integrate Hmac Signatures via filters or interceptors that process an Authorization header such as HMAC-SHA256 keyId:signature. Common pitfalls include:
- Using a predictable or hardcoded secret key, or storing it in source code or configuration files without encryption.
- Failing to include a timestamp or nonce, which enables replay attacks where a captured signed request is resent by an attacker.
- Not normalizing the request payload or headers before signing, leading to different signatures for semantically equivalent requests and opening ambiguity in validation logic.
- Improper error handling that reveals whether a signature was malformed, a key was missing, or a timestamp was invalid, thereby leaking information useful for enumeration.
An attacker who obtains or guesses the shared secret can sign arbitrary requests and bypass authentication. Conversely, weak signature verification can allow unsigned or tampered requests to be accepted. These issues map directly to OWASP API Top 10 categories such as Broken Object Level Authorization (BOLA) and Security Misconfiguration, and can be critical when combined with other weaknesses like IDOR.
middleBrick scans unauthenticated attack surfaces and includes checks for Authentication and BOLA/IDOR, highlighting risks specific to Hmac-based flows such as missing replay protection or weak key handling. The scan also cross-references OpenAPI definitions to ensure declared security schemes align with runtime expectations.
Hmac Signatures-Specific Remediation in Spring Boot — concrete code fixes
To remediate Broken Authentication when using Hmac Signatures in Spring Boot, implement robust key management, strict signature validation with constant-time comparison, replay protection, and secure error handling. Below are concrete, working examples.
1. Secure Hmac Signature Generation and Verification
Use a strong, randomly generated secret stored securely (e.g., via environment variables or a secrets manager). Implement a filter that computes the HMAC over a canonical representation of the request and validates it using a constant-time comparison.
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class HmacUtil {
private static final String HMAC_ALGORITHM = "HmacSHA256";
public static String calculateHmac(String data, String secret) throws Exception {
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM);
mac.init(keySpec);
byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(rawHmac);
}
public static boolean safeEquals(String a, String b) {
if (a == null || b == null || a.length() != b.length()) {
return false;
}
int result = 0;
for (int i = 0; i < a.length(); i++) {
result |= a.charAt(i) ^ b.charAt(i);
}
return result == 0;
}
}
2. Spring Boot Filter for Request Authentication
Implement a OncePerRequestFilter that reconstructs the signed string, retrieves the key based on a key identifier, and validates the signature using safeEquals. Include a timestamp and reject requests older than a short window (e.g., 5 minutes) to prevent replay.
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
@Component
public class HmacAuthenticationFilter extends OncePerRequestFilter {
private final long replayWindowMs = TimeUnit.MINUTES.toMillis(5);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("HMAC-SHA256 ")) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing or invalid Authorization header");
return;
}
String[] parts = authHeader.substring("HMAC-SHA256 ".length()).split(":", 2);
if (parts.length != 2) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid Authorization format");
return;
}
String keyId = parts[0];
String receivedSignature = parts[1];
String secret = KeyStore.getSecretForKey(keyId); // implement secure key retrieval
if (secret == null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unknown key identifier");
return;
}
String timestampHeader = request.getHeader("X-Request-Timestamp");
if (timestampHeader == null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing timestamp");
return;
}
long requestTime;
try {
requestTime = Long.parseLong(timestampHeader);
} catch (NumberFormatException e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid timestamp");
return;
}
long now = System.currentTimeMillis();
if (Math.abs(now - requestTime) > replayWindowMs) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Request expired, possible replay");
return;
}
String method = request.getMethod();
String path = request.getRequestURI();
String body = request.getReader().lines().reduce("", String::concat);
String dataToSign = String.join("
", method, path, timestampHeader, body);
try {
String expectedSignature = HmacUtil.calculateHmac(dataToSign, secret);
if (!HmacUtil.safeEquals(expectedSignature, receivedSignature)) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid signature");
return;
}
} catch (Exception e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Signature computation error");
return;
}
filterChain.doFilter(request, response);
}
}
3. Secure Key Storage and Rotation
Do not hardcode secrets. Use environment variables or a secrets manager and rotate keys periodically. For example, configure keys in application properties with placeholders resolved at runtime, and implement a KeyStore that supports multiple keys keyed by keyId.
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class KeyStore {
private static final Map<String, String> keys = new ConcurrentHashMap<>();
static {
// In production, load from secure source (e.g., environment, vault)
keys.put("keyId2024", System.getenv("HMAC_SECRET_KEY"));
}
public static String getSecretForKey(String keyId) {
return keys.get(keyId);
}
}
4. Complementary Practices
- Ensure TLS is enforced to protect secrets in transit.
- Log authentication failures with minimal detail to avoid information leakage.
- Validate and normalize inputs before signing to avoid ambiguity.
- Use short timestamp windows and consider one-time nonces if higher replay protection is required.
These measures reduce the risk of Broken Authentication when using Hmac Signatures and align the implementation with secure design principles. middleBrick can verify that your API’s authentication scheme correctly handles signatures, replay protection, and error conditions, surfacing findings with remediation guidance.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |