Credential Stuffing in Spring Boot with Api Keys

Credential Stuffing in Spring Boot with Api Keys — how this specific combination creates or exposes the vulnerability

Credential stuffing is an automated attack where previously breached username and password pairs are systematically submitted to authenticate to a service. When API keys are used as the primary or secondary credential in a Spring Boot application, the attack surface shifts but the risk remains. Unlike passwords, API keys are often long-lived, stored in configuration files or environment variables, and embedded in client-side code or CI/CD pipelines. If these keys are leaked or if the application does not enforce additional checks, attackers can replay stolen keys to gain access to accounts or elevate privileges.

In Spring Boot applications that rely on API keys for authentication (for example via a custom AuthenticationFilter that reads an X-API-Key header), credential stuffing can occur when an attacker collects valid API keys from data breaches, public repositories, or logs and then replays them against the authentication endpoint. Because API keys are typically static and do not expire unless explicitly rotated, compromised keys remain useful until detection. Spring Boot’s default security configuration does not inherently throttle or lock API key–based requests, which allows attackers to perform high-volume, low-and-slow login attempts without triggering immediate defenses.

The combination of Spring Boot’s flexible filter chain and the stateless nature of API key authentication can inadvertently enable credential stuffing if rate limiting, request validation, and monitoring are not explicitly configured. For instance, if an endpoint accepts an API key via header and maps it to a user context without verifying the origin of the request, an attacker can automate submissions using leaked keys across multiple accounts. This is especially risky when API keys are used for administrative or privileged operations, as the attack may go unnoticed due to the assumption that only service-to-service communication occurs.

Moreover, if OpenAPI specifications for the Spring Boot service expose endpoints that accept API keys without clearly indicating authentication requirements or threat models, scanners may fail to correlate runtime behavior with spec expectations. middleBrick’s OpenAPI/Swagger spec analysis (2.0, 3.0, 3.1) with full $ref resolution can cross-reference definitions with runtime findings to highlight inconsistencies, such as an endpoint that claims to require an API key but does not enforce it in practice.

Api Keys-Specific Remediation in Spring Boot — concrete code fixes

To mitigate credential stuffing risks when using API keys in Spring Boot, implement layered controls around authentication, validation, and monitoring. The following code examples demonstrate how to securely integrate API key validation with request throttling, header normalization, and explicit rejection of malformed keys.

Secure API Key Filter with Validation and Rate Limiting

Create a filter that checks the API key against a repository, enforces rate limits per key, and avoids leaking timing information. Use constant-time comparison to prevent timing attacks.

@Component
public class ApiKeyAuthenticationFilter extends OncePerRequestFilter {

    private final ApiKeyService apiKeyService;
    private final RateLimiter rateLimiter;

    public ApiKeyAuthenticationFilter(ApiKeyService apiKeyService, RateLimiter rateLimiter) {
        this.apiKeyService = apiKeyService;
        this.rateLimiter = rateLimiter;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String apiKey = request.getHeader("X-API-Key");
        if (apiKey == null || apiKey.isBlank()) {
            filterChain.doFilter(request, response);
            return;
        }

        // Constant-time validation to avoid timing leaks
        boolean isValid = apiKeyService.validateKey(apiKey);
        if (!isValid) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid API key");
            return;
        }

        // Apply rate limiting per API key
        if (!rateLimiter.tryAcquire(apiKey)) {
            response.sendError(HttpServletResponse.SC_TOO_MANY_REQUESTS, "Rate limit exceeded");
            return;
        }

        // Set authentication in SecurityContext if needed
        ApiKeyAuthenticationToken authentication = new ApiKeyAuthenticationToken(apiKey);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        filterChain.doFilter(request, response);
    }
}

API Key Service with Secure Storage and Rotation Support

Implement a service that retrieves keys from a secure source (e.g., encrypted database or vault) and supports key rotation. Avoid storing keys in plain text or in application properties without encryption.

@Service
public class ApiKeyService {

    private final ApiKeyRepository apiKeyRepository;
    private final KeyEncryptionService encryptionService;

    public ApiKeyService(ApiKeyRepository apiKeyRepository, KeyEncryptionService encryptionService) {
        this.apiKeyRepository = apiKeyRepository;
        this.encryptionService = encryptionService;
    }

    public boolean validateKey(String rawKey) {
        // Example: fetch encrypted key from storage and compare securely
        Optional stored = apiKeyRepository.findByEncryptedKey(encryptionService.encrypt(rawKey));
        return stored.map(storedKey -> {
            boolean isValid = MessageDigest.isEqual(
                encryptionService.decrypt(storedKey.getEncryptedKey()).getBytes(StandardCharsets.UTF_8),
                rawKey.getBytes(StandardCharsets.UTF_8)
            );
            if (isValid && storedKey.isRevoked()) {
                // Trigger alert for revoked key usage
                return false;
            }
            return isValid;
        }).orElse(false);
    }
}

Rate Limiting Configuration

Use a sliding window or token bucket algorithm to limit requests per API key. Spring Boot can integrate with libraries such as Bucket4j to enforce limits at the filter level.

@Bean
public RateLimiter rateLimiter() {
    return (apiKey) -> {
        Bucket bucket = Bucket4j.builder()
            .addLimit(Limit.builder()
                .capacity(100)
                .refillGreedy(10, Duration.ofMinutes(1))
                .build())
            .build();
        return bucket.tryConsume(1);
    };
}

OpenAPI Spec Alignment

Ensure your OpenAPI definitions accurately reflect the authentication requirements for each endpoint. Use security schemes and required headers so generated clients and scanners (including middleBrick’s OpenAPI/Swagger analysis with full $ref resolution) can correctly map the expected protections.