Api Key Exposure in Spring Boot with Oauth2
Api Key Exposure in Spring Boot with Oauth2 — how this specific combination creates or exposes the vulnerability
When a Spring Boot application uses OAuth2 for authorization while also managing API keys for service-to-service or third‑party integrations, misconfiguration can expose those keys through the same endpoints that OAuth2 protects. Common patterns that lead to exposure include placing API keys in request headers, query parameters, or environment properties that are inadvertently logged, returned in error responses, or reflected in OpenAPI documentation. Because OAuth2 scopes and roles are enforced by Spring Security filters, a developer may assume that once an endpoint is secured with OAuth2, no further leakage is possible. However, if the API key is transmitted in a header such as x-api-key and the endpoint also returns detailed error messages or debug information, the key can be exposed to unauthorized clients or logged in application telemetry.
Spring Boot applications often integrate with external services using OAuth2 client credentials flow. In this flow, the application obtains an access token and uses it to call downstream APIs, sometimes adding an additional API key for authentication. If the code that retrieves and forwards the API key does not properly separate concerns, the key can be included in logs, URL paths, or HTTP response bodies. For example, returning the raw Authorization header or a custom x-api-key header in error responses can disclose the key to unauthenticated or insufficiently scoped clients. Similarly, if the OpenAPI spec generated by SpringDoc includes examples with hard‑coded keys, consumers of the documentation might inadvertently expose secrets through client code generation or shared repositories.
The risk is amplified when OAuth2 resource servers are used alongside legacy API‑key validation logic. If a developer configures Spring Security to permit all requests to certain paths for OAuth2 introspection while still requiring API keys at the same layer, the effective protection level may be weaker than intended. An attacker who discovers an endpoint that echoes headers or configuration can combine OAuth2 token acquisition techniques with header smuggling to retrieve API keys. Moreover, if the application exposes a health or info endpoint that dumps environment properties, any property containing an API key becomes reachable to authenticated users with the right OAuth2 scope, even if the scope should not grant such access.
These issues map to common weaknesses such as CWE‑532 (Insertion of Sensitive Information into Log File) and CWE‑200 (Exposure of Sensitive Information to an Unauthorized Actor). In the context of OWASP API Top 10, this behavior aligns with Broken Object Level Authorization (BOLA) and Security Misconfiguration. Because OAuth2 scopes are often misunderstood as a complete access control solution, developers may overlook the need to sanitize outputs and strictly separate credentials from access tokens. middleBrick scans detect such exposure by analyzing OpenAPI specs and runtime responses, identifying places where API keys appear in examples, error payloads, or documentation, even when OAuth2 protections are in place.
Oauth2-Specific Remediation in Spring Boot — concrete code fixes
To prevent API key exposure in Spring Boot applications using OAuth2, apply defense‑in‑depth measures that separate credential handling from authorization logic. First, ensure that API keys are never transmitted in HTTP headers that might be echoed back in responses or logs. Instead, store keys in a secure vault or environment variables and inject them only into the runtime context that needs them. Configure Spring Security to enforce scopes and roles strictly, and avoid broad permit‑all rules for OAuth2 resources.
Use a dedicated filter to strip or mask sensitive headers before responses leave the application. For example, you can implement a OncePerRequestFilter that removes or replaces the x-api-key header from error responses and logs. Below is a concise Spring Boot configuration that combines OAuth2 resource server support with header sanitization:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
http.addFilterAfter(new HeaderSanitizationFilter(), BasicAuthenticationFilter.class);
return http.build();
}
private static class HeaderSanitizationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} finally {
// Remove sensitive headers from responses to avoid accidental exposure
response.setHeader("X-API-Key", "");
response.setHeader("Authorization", "");
}
}
}
}
When consuming downstream APIs with client credentials flow, keep the API key outside of the OAuth2 token request and avoid merging it into the same HTTP context used for token storage. Use separate RestTemplate or WebClient configurations with explicit headers, and ensure that sensitive values are cleared after use:
@Service
public class ExternalServiceClient {
private final String apiKey;
public ExternalServiceClient(@Value("${external.api.key}") String apiKey) {
this.apiKey = apiKey;
}
public String callExternalApi(String resourceId) {
return WebClient.create()
.post()
.uri("https://api.external.com/resource/{id}", resourceId)
.headers(headers -> headers.setBasicAuth("app", apiKey))
.retrieve()
.bodyToMono(String.class)
.block();
}
}
Validate incoming requests to ensure that API keys are not reflected in responses or error details. Configure Spring MVC to suppress default error body details for OAuth2 secured endpoints and use a custom error handler that omits credential information:
handleApiException(Exception ex, WebRequest request) {
// Return generic messages without echoing headers or keys
String body = "An error occurred";
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Finally, audit your OpenAPI generation (e.g., SpringDoc) to ensure that no examples contain hard‑coded API keys. Use placeholders and external configuration to keep generated documentation safe while still providing useful examples for consumers.