HIGH cors wildcardspring boothmac signatures

Cors Wildcard in Spring Boot with Hmac Signatures

Cors Wildcard in Spring Boot with Hmac Signatures — how this specific combination creates or exposes the vulnerability

A CORS wildcard (allowedOrigins = "*") combined with Hmac Signatures in Spring Boot can unintentionally expose authenticated or integrity-checked endpoints to any origin. When Hmac Signatures are used, the client typically includes a signature header derived from a shared secret and request attributes (e.g., timestamp, nonce, payload). If the server responds with Access-Control-Allow-Origin: * to a preflight or actual request that carries credentials or sensitive headers, any website can make requests on behalf of or observe responses intended for authenticated clients.

In Spring Boot, CORS configuration is often set globally or per path. A common insecure pattern is:

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/api/**")
                    .allowedOrigins("*")
                    .allowedMethods("GET", "POST", "PUT", "DELETE")
                    .allowedHeaders("*")
                    .exposedHeaders("X-Request-ID");
        }
    };
}

With Hmac Signatures, the server validates a signature header (e.g., X-API-Signature) before processing the request. Even if the signature is valid, a wildcard CORS policy means any origin can trigger these validated requests. This becomes critical when the endpoint performs actions on behalf of the user (e.g., changing settings) or returns sensitive data. An attacker site can embed a form or script that issues signed requests using the victim’s credentials (cookies or tokens), leading to unauthorized operations or information leakage through the response if credentials are included via cookies or authorization headers.

The interaction with preflight requests also matters. For non-simple requests (e.g., custom headers like X-API-Signature), the browser sends an OPTIONS preflight. If the server responds with Access-Control-Allow-Origin: * and does not reflect the requesting origin, the browser may block the actual request in secure contexts, but misconfigurations can allow the request to proceed. More importantly, wildcard CORS with exposed sensitive headers undermines the boundary that Hmac Signatures aim to enforce: that only known origins can craft valid interactions.

To avoid this, origins should be explicitly listed or dynamically set based on an allowlist, rather than using a wildcard, especially when Hmac Signatures protect state-changing or sensitive endpoints. This ensures that only trusted origins can initiate requests that will be validated with the shared secret.

Hmac Signatures-Specific Remediation in Spring Boot — concrete code fixes

Remediation centers on two changes: tighten CORS to avoid wildcards and ensure Hmac Signature validation is applied consistently before any business logic. Below are concrete, secure configurations and a Hmac signature validation filter for Spring Boot.

Secure CORS Configuration

Replace the wildcard with an explicit origin or a controlled allowlist. If you need dynamic origins, compute the allowed list at runtime and set the response header explicitly; do not use allowedOrigins("*") when credentials or sensitive headers are involved.

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/api/**")
                    .allowedOrigins("https://app.yourcompany.com", "https://admin.yourcompany.com")
                    .allowedMethods("GET", "POST", "PUT", "DELETE")
                    .allowedHeaders("X-API-Signature", "Content-Type", "Authorization")
                    .exposedHeaders("X-Request-ID")
                    .allowCredentials(true);
        }
    };
}

Hmac Signature Validation Filter

Implement a filter that computes the expected Hmac signature and compares it securely (constant-time comparison) before the request proceeds. This example uses SHA-256 with a shared secret stored as an environment variable.

@Component
public class HmacSignatureFilter extends OncePerRequestFilter {

    private final String secret = System.getenv("API_HMAC_SECRET");

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String signatureHeader = request.getHeader("X-API-Signature");
        String timestamp = request.getHeader("X-API-Timestamp");
        String nonce = request.getHeader("X-API-Nonce");

        if (signatureHeader == null || timestamp == null || nonce == null) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing signature or headers");
            return;
        }

        // Prevent replay attacks: ensure timestamp is recent (e.g., within 5 minutes)
        long requestTime = Long.parseLong(timestamp);
        long now = System.currentTimeMillis();
        if (Math.abs(now - requestTime) > 5 * 60 * 1000) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Request expired");
            return;
        }

        // Build the data to sign: method + path + timestamp + nonce + body
        String method = request.getMethod();
        String path = request.getRequestURI();
        String body = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        String dataToSign = method + path + timestamp + nonce + body;

        String computedSignature = calculateHmac(dataToSign, secret);

        if (!constantTimeEquals(computedSignature, signatureHeader)) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid signature");
            return;
        }

        filterChain.doFilter(request, response);
    }

    private String calculateHmac(String data, String key) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            mac.init(keySpec);
            return Hex.encodeHexString(mac.doFinal(data.getBytes(StandardCharsets.UTF_8)));
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException("Hmac calculation failed", e);
        }
    }

    private boolean constantTimeEquals(String a, String b) {
        if (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;
    }
}

Register the filter in your security configuration to ensure it runs before authentication/authorization:

@Configuration
@WebFilter(urlPatterns = "/api/*")
public class FilterConfig {

    @Bean
    public FilterRegistrationBean hmacFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean<>();
        registration.setFilter(new HmacSignatureFilter());
        registration.setOrder(1);
        return registration;
    }
}

Additional guidance:

  • Use HTTPS to prevent interception of the signature, timestamp, and nonce.
  • Store the shared secret securely (e.g., via a secrets manager) and rotate periodically.
  • Include a nonce and short timestamp window to mitigate replay attacks.
  • Avoid logging the signature or secret in production logs.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Why is using allowedOrigins("*") unsafe when Hmac Signatures protect my endpoints?
A wildcard CORS policy allows any origin to make requests that pass Hmac validation if credentials or sensitive headers are accepted. This means an attacker site can issue signed requests using the victim’s cookies or tokens, bypassing origin-based restrictions and potentially leading to unauthorized actions or data exposure.
How should I handle dynamic origins with Hmac Signatures and CORS in Spring Boot?
Maintain an allowlist of trusted origins and set Access-Control-Allow-Origin explicitly per request based on the Origin header, rather than using a wildcard. Ensure allowCredentials is true and the exposed headers do not leak sensitive information to untrusted origins.