API Security Best Practices: Bescherm je REST APIs
Leer de essentiële security best practices voor het beveiligen van je REST APIs tegen moderne bedreigingen. Van authenticatie tot rate limiting.
Jean-Pierre Broeders
Freelance DevOps Engineer
API Security Best Practices: Bescherm je REST APIs
APIs vormen de ruggengraat van moderne applicaties, maar zijn ook een veelvoorkomend aanvalspunt. In deze guide behandel ik de belangrijkste security best practices die je moet implementeren om je APIs te beschermen.
1. Gebruik Altijd HTTPS/TLS
Dit is de absolute basis. Nooit een productie API over onversleuteld HTTP draaien.
# Nginx configuratie: forceer HTTPS
server {
listen 80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/ssl/certs/api.example.com.crt;
ssl_certificate_key /etc/ssl/private/api.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
}
2. Implementeer Sterke Authenticatie
JWT Tokens (Recommended)
JSON Web Tokens zijn ideaal voor stateless authenticatie:
// .NET Example: JWT Token Generatie
public string GenerateJwtToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration["JwtSecret"]);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Email, user.Email)
}),
Expires = DateTime.UtcNow.AddHours(2),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature
)
};
return tokenHandler.WriteToken(tokenHandler.CreateToken(tokenDescriptor));
}
API Keys voor Service-to-Service
Voor machine-to-machine communicatie:
// Node.js: API Key validatie middleware
const validateApiKey = async (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({ error: 'API key missing' });
}
// Hash en vergelijk
const hashedKey = crypto.createHash('sha256').update(apiKey).digest('hex');
const isValid = await db.apiKeys.findOne({ hash: hashedKey, active: true });
if (!isValid) {
return res.status(403).json({ error: 'Invalid API key' });
}
req.apiKeyId = isValid.id;
next();
};
3. Rate Limiting is Essentieel
Voorkom brute force attacks en API misbruik:
# Python/Flask: Simple rate limiter
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"]
)
@app.route("/api/sensitive")
@limiter.limit("5 per minute")
def sensitive_endpoint():
return {"data": "protected"}
Voor production: gebruik Redis-backed rate limiting:
// Node.js + Redis
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const limiter = rateLimit({
store: new RedisStore({
client: redisClient,
prefix: 'rl:'
}),
windowMs: 15 * 60 * 1000, // 15 minuten
max: 100, // max 100 requests per window
message: 'Too many requests, please try again later'
});
app.use('/api/', limiter);
4. Input Validatie & Sanitization
Nooit user input vertrouwen:
// TypeScript: Zod schema validatie
import { z } from 'zod';
const userSchema = z.object({
email: z.string().email(),
age: z.number().min(18).max(120),
username: z.string().min(3).max(20).regex(/^[a-zA-Z0-9_]+$/)
});
app.post('/api/users', async (req, res) => {
try {
const validated = userSchema.parse(req.body);
// Veilig om te gebruiken
await createUser(validated);
} catch (error) {
return res.status(400).json({ error: error.errors });
}
});
5. CORS Configuratie
Beperk welke origins je API kunnen aanroepen:
// .NET Core: CORS policy
builder.Services.AddCors(options =>
{
options.AddPolicy("ProductionPolicy", builder =>
{
builder
.WithOrigins("https://app.example.com", "https://www.example.com")
.AllowedMethods("GET", "POST", "PUT", "DELETE")
.AllowedHeaders("Content-Type", "Authorization")
.AllowCredentials();
});
});
app.UseCors("ProductionPolicy");
6. Logging & Monitoring
Log security events, maar nooit gevoelige data:
// Goede logging praktijk
logger.info('Login attempt', {
userId: user.id,
ip: req.ip,
success: true
// GEEN passwords, tokens, of PII!
});
// Alert bij verdachte activiteit
if (failedAttempts > 5) {
await alertSecurityTeam({
type: 'BRUTE_FORCE_DETECTED',
ip: req.ip,
endpoint: '/api/login'
});
}
7. SQL Injection Preventie
Gebruik altijd parameterized queries:
// ✅ GOED: Parameterized query
var user = await db.Users
.FromSqlInterpolated($"SELECT * FROM Users WHERE Email = {email}")
.FirstOrDefaultAsync();
// ❌ FOUT: String concatenation
var user = await db.Users
.FromSqlRaw($"SELECT * FROM Users WHERE Email = '{email}'")
.FirstOrDefaultAsync();
8. Versioning & Deprecation
Geef API clients tijd om te migreren:
GET /api/v1/users (deprecated, EOL 2026-06-01)
GET /api/v2/users (current)
Response headers:
res.setHeader('X-API-Version', 'v1');
res.setHeader('X-API-Deprecated', 'true');
res.setHeader('X-API-Sunset', '2026-06-01T00:00:00Z');
Checklist voor Production APIs
- [ ] HTTPS/TLS verplicht
- [ ] Authenticatie (JWT/API Keys)
- [ ] Rate limiting geïmplementeerd
- [ ] Input validatie op alle endpoints
- [ ] CORS correct geconfigureerd
- [ ] Security headers (HSTS, CSP, etc.)
- [ ] Logging & monitoring actief
- [ ] SQL injection preventie
- [ ] Secrets in environment variables
- [ ] API versioning strategie
Conclusie
API security is geen "nice to have" maar een absolute vereiste. Implementeer deze best practices vanaf dag één—het achteraf toevoegen is veel moeilijker en risicovoller.
Start met de basics (HTTPS, authenticatie, input validatie) en bouw van daaruit verder. Elk van deze maatregelen verkleint je attack surface aanzienlijk.
Pro tip: Gebruik tools zoals OWASP ZAP of Burp Suite om je eigen APIs te testen op kwetsbaarheden. Beter jij dan een aanvaller.
