The API Gateway as Security Perimeter
An API gateway acts as the single entry point for all client-to-service communication in a microservices architecture. It is uniquely positioned to enforce security policies centrally, reducing complexity in individual services and providing a consistent security layer.
Core Security Functions
Authentication and Authorization
The gateway validates tokens before requests reach backend services, offloading this responsibility from individual services.
# Kong Gateway authentication configuration
services:
- name: user-service
url: http://user-svc.internal:8080
routes:
- name: user-routes
paths:
- /api/users
plugins:
- name: jwt
config:
key_claim_name: iss
secret_is_base64: false
claims_to_verify:
- exp
- nbf
run_on_preflight: true
// Custom auth middleware in Express Gateway
const jwt = require('jsonwebtoken');
async function gatewayAuth(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '');
try {
const decoded = jwt.verify(token, PUBLIC_KEY, {
algorithms: ['RS256'],
issuer: 'https://auth.example.com'
});
// Inject validated claims into downstream request
req.headers['x-user-id'] = decoded.sub;
req.headers['x-user-roles'] = decoded.roles.join(',');
req.headers['x-authenticated'] = 'true';
// Strip the original token from downstream
delete req.headers.authorization;
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}
Rate Limiting and Throttling
The gateway enforces global and per-service rate limits:
plugins:
- name: rate-limiting
config:
second: null
minute: 1000
hour: 50000
policy: local
fault_tolerant: true
hide_client_headers: false
redis:
host: redis.internal
port: 6379
IP Allowlisting and Blocklisting
plugins:
- name: ip-restriction
config:
allow:
- 10.0.0.0/8
- 172.16.0.0/12
deny:
- 10.0.0.42 # Specific blocked IP
Request Validation
Schema Validation
Validate request bodies against OpenAPI schemas at the gateway:
plugins:
- name: request-validator
config:
body_schema:
type: object
required:
- email
- password
properties:
email:
type: string
format: email
maxLength: 255
password:
type: string
minLength: 8
maxLength: 128
parameter_schema:
- in: query
name: page
schema:
type: integer
minimum: 1
Request Size Limiting
# Nginx as API gateway
http {
client_body_buffer_size 128k;
client_max_body_size 10M;
client_body_timeout 30s;
server {
listen 443 ssl;
server_name api.example.com;
location /api/ {
# Limit request size per endpoint
location /api/upload {
client_max_body_size 100M;
}
location /api/query {
client_max_body_size 1K;
}
}
}
}
TLS Termination
Terminate TLS at the gateway to encrypt traffic between clients and the gateway, and optionally re-encrypt between gateway and services:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/ssl/certs/api.example.com.pem;
ssl_certificate_key /etc/ssl/private/api.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Backend communication (re-encrypt)
location /api/internal/ {
proxy_pass https://internal-services;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca.crt;
}
}
CORS Management
Centrally manage CORS policies:
plugins:
- name: cors
config:
origins:
- https://app.example.com
- https://admin.example.com
methods:
- GET
- POST
- PUT
- DELETE
- PATCH
headers:
- Authorization
- Content-Type
exposed_headers:
- X-RateLimit-Remaining
credentials: true
max_age: 3600
Threat Detection and WAF
ModSecurity with OWASP CRS
# Enable WAF on the gateway
location /api/ {
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/owasp-crs/crs-setup.conf;
# Custom rules
modsecurity_rules '
# Block SQL injection patterns
SecRule REQUEST_URI "@rx (?i:(?:union[\s]+select|select[\s]+from))" \
"id:1000,deny,status:403,msg:\'SQL Injection blocked\'"
# Block common XSS attempts
SecRule ARGS "@rx <script[^>]*>" \
"id:1001,deny,status:403,msg:\'XSS blocked\'"
# Rate limit login attempts
SecRule IP:LOGIN_ATTEMPT "@gt 5" \
"id:1002,deny,status:429,msg:\'Brute force blocked\'"
';
}
GraphQL Security at the Gateway
GraphQL APIs need specialized protections:
plugins:
- name: graphql-proxy-cache
- name: graphql-rate-limiting
config:
max_queries_per_minute: 100
max_depth: 5
max_complexity: 1000
Security Headers at the Gateway
Apply consistent security headers across all responses:
plugins:
- name: response-transformer
config:
add:
headers:
- "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload"
- "X-Content-Type-Options: nosniff"
- "X-Frame-Options: DENY"
- "Content-Security-Policy: default-src 'self'"
- "Referrer-Policy: strict-origin-when-cross-origin"
Audit Logging
Log all API requests centrally for security analysis:
plugins:
- name: file-log
config:
path: /var/log/api-gateway/access.log
custom_fields_by_lua:
client_ip: "ngx.var.remote_addr"
method: "ngx.var.request_method"
path: "ngx.var.request_uri"
status: "ngx.var.status"
latency: "ngx.var.upstream_response_time"
user_id: "ngx.var.http_x_user_id"
Summary
The API gateway is the ideal location to centralize security controls: authentication, rate limiting, request validation, IP restrictions, TLS termination, CORS management, and WAF rules. By enforcing these policies at the gateway, individual services remain simpler and can focus on business logic while maintaining a consistent security posture across your entire API surface.