Securing Public APIs at Scale
Introduction
Public APIs operate under constant abuse, variable traffic, and strict latency budgets. Security at scale requires layered controls at the edge, consistent identity enforcement, and deep observability across every request path.
Threat Model for Public APIs
Public endpoints face a predictable set of threats.
- Credential stuffing and token replay.
- Excessive scraping and bot traffic.
- Injection attempts and schema abuse.
- Distributed denial of service attacks.
Edge Security Controls
Security starts at the perimeter before traffic reaches application code.
- Deploy a managed WAF with tuned rule sets.
- Apply bot detection based on behavioral signals.
- Enforce TLS termination with modern cipher suites.
- Use global rate limits for unauthenticated traffic.
Gateway Responsibilities
An API gateway should enforce consistent policies across all services.
- Authentication and JWT verification.
- Scope-based authorization decisions.
- Quotas, throttling, and burst control.
- Request normalization and schema validation.
Java Spring Boot Example: Scope-Based Access
The following example converts JWT scopes into authorities and restricts endpoints by scope.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.util.Collection;
import java.util.stream.Collectors;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class ApiSecurityConfig {
@Bean
public SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/v1/billing/**").hasAuthority("SCOPE_billing.read")
.requestMatchers("/v1/admin/**").hasAuthority("SCOPE_admin")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(
jwt -> jwt.jwtAuthenticationConverter(jwtConverter())));
return http.build();
}
private Converter<Jwt, ? extends AbstractAuthenticationToken> jwtConverter() {
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
// Extract "scope" claim and map each scope to a SCOPE_ prefixed authority
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
String scope = jwt.getClaimAsString("scope");
if (scope == null || scope.isBlank()) {
return List.of();
}
return Arrays.stream(scope.split(" "))
.map(s -> new SimpleGrantedAuthority("SCOPE_" + s))
.collect(Collectors.toList());
});
return converter;
}
}
Observability and Incident Response
- Log every authorization decision with correlation IDs.
- Track token issuer, audience, and scope usage metrics.
- Alert on sudden spikes in 401 and 403 responses.
- Maintain a rapid credential rotation playbook.
Conclusion
Scaling security for public APIs is less about a single control and more about consistently applying layered policies across the edge, gateway, and service tiers.
This post is licensed under CC BY 4.0 by the author.