API Security Hardening
Rate limiting, CORS, input validation, security headers, API key management.
securityapibackend
# API Security Hardening
## Rate limiting
```ts
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 30, // 30 requests per window
standardHeaders: true,
keyGenerator: (req) => req.headers['x-real-ip'] || req.ip,
})
app.use('/api/', limiter)
// Stricter for auth endpoints
const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5 })
app.use('/api/auth/login', authLimiter)
```
## CORS
```ts
import cors from 'cors'
app.use(cors({
origin: ['https://yourdomain.com'], // never '*' with credentials
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
maxAge: 86400,
}))
```
## Input validation (Zod)
```ts
import { z } from 'zod'
const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(1).max(100).trim(),
age: z.number().int().min(0).max(150).optional(),
})
app.post('/api/users', (req, res) => {
const result = CreateUserSchema.safeParse(req.body)
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() })
}
// result.data is typed and validated
})
```
## Security headers
```ts
import helmet from 'helmet'
app.use(helmet()) // sets ~15 security headers
// Or manually in Next.js
const headers = [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-XSS-Protection', value: '0' }, // deprecated, CSP is better
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
]
```
## API key management
```ts
// Hash API keys in DB (treat like passwords)
const hashedKey = crypto.createHash('sha256').update(apiKey).digest('hex')
// Verify
const inputHash = crypto.createHash('sha256').update(req.headers['x-api-key']).digest('hex')
const keyRecord = await db.apiKey.findUnique({ where: { hash: inputHash } })
```
## Error responses
```ts
// NEVER leak internal details
// BAD: { error: 'ER_NO_SUCH_TABLE: Table users doesn\'t exist' }
// GOOD: { error: 'Internal server error', code: 'INTERNAL_ERROR' }
app.use((err, req, res, next) => {
console.error(err) // log internally
res.status(500).json({ error: 'Internal server error' })
})
```
API: /api/skills/api-security-hardening