HIGH api key exposurespring bootmutual tls

Api Key Exposure in Spring Boot with Mutual Tls

Api Key Exposure in Spring Boot with Mutual Tls — how this specific combination creates or exposes the vulnerability

Mutual Transport Layer Security (mTLS) in Spring Boot requires both the client and the server to present valid X.509 certificates during the TLS handshake. While mTLS strongly authenticates the client, it does not automatically protect sensitive values that travel inside HTTP requests. An API key exposed in headers, query parameters, or request bodies can still be visible to parties who successfully complete the TLS handshake, such as trusted internal services, compromised clients, or intermediaries in a chain of trust.

In Spring Boot, developers commonly configure mTLS via server.ssl.key-store and server.ssl.trust-store properties, enabling request client certificate validation with setNeedClientAuth(true). This ensures only clients with a trusted certificate can establish a connection, but it does not strip or hide credentials from the application protocol layer. As a result, an API key passed in an Authorization: ApiKey header, a custom header like X-API-Key, or a query parameter can be extracted from the request by application code, by logging mechanisms, or by observability tools that are part of the trusted environment.

Spring Boot applications that also consume OpenAPI specifications can inadvertently document or expose API key locations in generated docs. If an OpenAPI spec describes an api_key security scheme in a securitySchemes object and the implementation uses that key from an unencrypted source (e.g., a request header without additional transport protection beyond mTLS), scanning and runtime analysis can detect this pattern. The scanner performs unauthenticated checks against the live endpoint and can observe whether API key values are returned in responses, reflected in error messages, or logged in server-side traces, even when mTLS is in place.

Another risk arises when mTLS is used for client authentication but the application treats the authenticated principal as implicitly trustworthy. For example, a Spring Security configuration that only verifies the certificate subject distinguished name (DN) may allow any client with a valid certificate to call sensitive endpoints that return data protected by an API key. This can lead to vertical or horizontal privilege escalation if the API key is embedded in server responses or used as a bearer token for downstream services. The scanner’s checks include BOLA/IDOR and Property Authorization tests that can surface cases where an authenticated client can access another client’s data via an API key–protected endpoint.

SSRF and Unsafe Consumption findings are also relevant. An API key may be forwarded to external services from within the Spring Boot app, and if the destination URL is supplied by an attacker, the key can be exfiltrated through SSRF callbacks or logged in external logs. The scanner’s SSRF and Unsafe Consumption checks examine whether outbound calls incorporate secrets from request-controlled inputs and whether responses contain credentials, regardless of the presence of mTLS.

Mutual Tls-Specific Remediation in Spring Boot — concrete code fixes

To reduce the risk of API key exposure while using mTLS in Spring Boot, combine strict transport configuration with application-level handling of secrets. The following patterns illustrate how to enforce mTLS and avoid leaking API keys in request handling.

Enforce mTLS in application.properties or application.yml

server.port=8443
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=changeit
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=tomcat
server.ssl.trust-store=classpath:truststore.p12
server.ssl.trust-store-password=changeit
server.ssl.trust-store-type=PKCS12
server.ssl.client-auth=need

Setting server.ssl.client-auth=need ensures that the server requests and validates client certificates. Requests without a valid client cert will fail the TLS handshake before reaching Spring Security.

Restrict allowed client principals with Spring Security

@Configuration
@EnableWebSecurity
public class MtlsSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .x509(x509 -> x509
                .subjectPrincipalRegex("CN=(.*?)(?:,|$)")
                .userDetailsService(username ->
                    User.withUsername(username)
                        .password("ignored")
                        .roles("CLIENT")
                        .build()
                )
            );
        return http.build();
    }
}

This configuration uses an X.509 filter that extracts the common name (CN) from the client certificate and maps it to a Spring Security authentication. By anchoring the regex to CN=...<end>, you avoid parsing ambiguities that could allow spoofed identities.

Avoid logging or echoing API keys in responses

@RestController
@RequestMapping("/api/v1/resource")
public class ResourceController {

    private final ResourceService service;

    public ResourceController(ResourceService service) {
        this.service = service;
    }

    @GetMapping
    public ResponseEntity getResource(
            @RequestHeader("X-Request-ID") String requestId) {
        // Do NOT log the API key; log only a redacted identifier
        String safeId = requestId != null ? requestId : "unknown";
        return ResponseEntity.ok(service.fetchResource(safeId));
    }
}

Ensure that API keys are never included in logs, error messages, or serialization output. Use request-scoped identifiers for tracing instead of raw keys.

Validate and sanitize inputs before forwarding

@Service
public class ProxyService {

    private final WebClient webClient;

    public ProxyService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://downstream.example.com").build();
    }

    public Mono forwardWithKey(String clientProvidedPath, String apiKey) {
        if (!Paths.get(clientProvidedPath).normalize().equals(Paths.get(clientProvidedPath))) {
            throw new IllegalArgumentException("Invalid path");
        }
        // Use apiKey from a secure source, not from client input
        return webClient.get()
                .uri("/" + clientProvidedPath)
                .header("Authorization", "ApiKey " + apiKey)
                .retrieve()
                .bodyToMono(String.class);
    }
}

Never construct outbound URLs or headers directly from client-controlled input. Validate paths, use allowlists, and keep API keys in server-side configuration or a secrets manager rather than deriving them from requests.

Frequently Asked Questions

Does mTLS alone prevent API key leakage in Spring Boot?
No. Mutual TLS authenticates the client at the transport layer but does not hide API keys that travel in headers, query parameters, or bodies. Keys can still be exposed through logging, error messages, insecure code, or improper handling of server-to-server calls.
How can I detect API key exposure in my Spring Boot endpoints using middleBrick?
Submit your endpoint URL to middleBrick; the scanner runs checks such as BOLA/IDOR, Property Authorization, and SSRF/Unsafe Consumption. It compares findings against your OpenAPI spec and reports whether API key values are reflected in responses or reachable via unauthenticated probes, even when mTLS is configured.