# ADR-016: API Security Hardening

**Date**: 2025-12-12

**Status**: Accepted

## Context

While authentication (`ADR-011`) is covered, the API lacks formal policies for protection against common web vulnerabilities and abuse. Security measures like rate limiting, secure HTTP headers, and Cross-Origin Resource Sharing (CORS) are not standardized.

## Decision

We will implement a multi-layered security approach for the API:

1. **`helmet`**: Use the `helmet` library to set crucial security headers (e.g., Content-Security-Policy, X-Content-Type-Options).
2. **Rate Limiting**: Apply rate limiting using a library like `express-rate-limit` on sensitive endpoints (e.g., `/login`, `/register`) to prevent brute-force attacks.
3. **CORS**: Establish a strict CORS policy to control which domains can access the API.

## Consequences

- **Positive**: Significantly improves the application's security posture against common web vulnerabilities like XSS, clickjacking, and brute-force attacks.
- **Negative**: Requires careful configuration of CORS and rate limits to avoid blocking legitimate traffic. Content-Security-Policy can be complex to configure correctly.

## Implementation Status

### What's Implemented

- ✅ **Helmet** - Security headers middleware with CSP, HSTS, and more
- ✅ **Rate Limiting** - Comprehensive implementation with 17+ specific limiters
- ✅ **Input Validation** - Zod-based request validation on all routes
- ✅ **File Upload Security** - MIME type validation, size limits, filename sanitization
- ✅ **Error Handling** - Production-safe error responses (no sensitive data leakage)
- ✅ **Request Timeout** - 5-minute timeout protection
- ✅ **Secure Cookies** - httpOnly and secure flags for authentication cookies

### Not Required

- ℹ️ **CORS** - Not needed (API and frontend are same-origin)

## Implementation Details

### Helmet Security Headers

Using **helmet v8.x** configured in `server.ts` as the first middleware after app initialization.

**Security Headers Applied**:

| Header | Configuration | Purpose |
| ------ | ------------- | ------- |
| Content-Security-Policy | Custom directives | Prevents XSS, code injection |
| Strict-Transport-Security | 1 year, includeSubDomains, preload | Forces HTTPS connections |
| X-Content-Type-Options | nosniff | Prevents MIME type sniffing |
| X-Frame-Options | DENY | Prevents clickjacking |
| X-XSS-Protection | 0 (disabled) | Deprecated, CSP preferred |
| Referrer-Policy | strict-origin-when-cross-origin | Controls referrer information |
| Cross-Origin-Resource-Policy | cross-origin | Allows external resource loading |

**Content Security Policy Directives**:

```typescript
contentSecurityPolicy: {
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "'unsafe-inline'"],   // React inline scripts
    styleSrc: ["'self'", "'unsafe-inline'"],    // Tailwind inline styles
    imgSrc: ["'self'", 'data:', 'blob:', 'https:'],  // External images
    fontSrc: ["'self'", 'https:', 'data:'],
    connectSrc: ["'self'", 'https:', 'wss:'],   // API + WebSocket
    frameSrc: ["'none'"],                        // No iframes
    objectSrc: ["'none'"],                       // No plugins
    upgradeInsecureRequests: [],                 // Production only
  },
}
```

**HSTS Configuration**:

- Max-age: 1 year (31536000 seconds)
- Includes subdomains
- Preload-ready for browser HSTS lists

### Rate Limiting

Using **express-rate-limit v8.2.1** with a centralized configuration in `src/config/rateLimiters.ts`.

**Standard Configuration**:

```typescript
const standardConfig = {
  standardHeaders: true,  // Sends RateLimit-* headers
  legacyHeaders: false,
  skip: shouldSkipRateLimit,  // Disabled in test environment
};
```

**Rate Limiters by Category**:

| Category | Limiter | Window | Max Requests |
| -------- | ------- | ------ | ------------ |
| **Authentication** | loginLimiter | 15 min | 5 |
| | registerLimiter | 1 hour | 5 |
| | forgotPasswordLimiter | 15 min | 5 |
| | resetPasswordLimiter | 15 min | 10 |
| | refreshTokenLimiter | 15 min | 20 |
| | logoutLimiter | 15 min | 10 |
| **Public/User Read** | publicReadLimiter | 15 min | 100 |
| | userReadLimiter | 15 min | 100 |
| | userUpdateLimiter | 15 min | 100 |
| **Sensitive Operations** | userSensitiveUpdateLimiter | 1 hour | 5 |
| | adminTriggerLimiter | 15 min | 30 |
| **AI/Costly** | aiGenerationLimiter | 15 min | 20 |
| | geocodeLimiter | 1 hour | 100 |
| | priceHistoryLimiter | 15 min | 50 |
| **Uploads** | adminUploadLimiter | 15 min | 20 |
| | aiUploadLimiter | 15 min | 10 |
| | batchLimiter | 15 min | 50 |
| **Tracking** | trackingLimiter | 15 min | 200 |
| | reactionToggleLimiter | 15 min | 150 |

**Test Environment Handling**:

Rate limiting is automatically disabled in test environment via `shouldSkipRateLimit` utility (`src/utils/rateLimit.ts`). Tests can opt-in to rate limiting by setting the `x-test-rate-limit-enable: true` header.

### Input Validation

**Zod Schema Validation** (`src/middleware/validation.middleware.ts`):

- Type-safe parsing and coercion for params, query, and body
- Applied to all API routes via `validateRequest()` middleware
- Returns structured validation errors with field-level details

**Filename Sanitization** (`src/utils/stringUtils.ts`):

```typescript
// Removes dangerous characters from uploaded filenames
sanitizeFilename(filename: string): string
```

### File Upload Security

**Multer Configuration** (`src/middleware/multer.middleware.ts`):

- MIME type validation via `imageFileFilter` (only image/* allowed)
- File size limits (2MB for logos, configurable per upload type)
- Unique filenames using timestamps + random suffixes
- User-scoped storage paths

### Error Handling

**Production-Safe Responses** (`src/middleware/errorHandler.ts`):

- Production mode: Returns generic error message with tracking ID
- Development mode: Returns detailed error information
- Sensitive error details are logged but never exposed to clients

### Request Security

**Timeout Protection** (`server.ts`):

- 5-minute request timeout via `connect-timeout` middleware
- Prevents resource exhaustion from long-running requests

**Secure Cookies**:

```typescript
// Cookie configuration for auth tokens
{
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'strict',
  maxAge: 7 * 24 * 60 * 60 * 1000  // 7 days for refresh token
}
```

### Request Logging

Per-request structured logging (ADR-004):

- Request ID tracking
- User ID and IP address logging
- Failed request details (4xx+) logged with headers and body
- Unhandled errors assigned unique error IDs

## Key Files

- `server.ts` - Helmet middleware configuration (security headers)
- `src/config/rateLimiters.ts` - Rate limiter definitions (17+ limiters)
- `src/utils/rateLimit.ts` - Rate limit skip logic for testing
- `src/middleware/validation.middleware.ts` - Zod-based request validation
- `src/middleware/errorHandler.ts` - Production-safe error handling
- `src/middleware/multer.middleware.ts` - Secure file upload configuration
- `src/utils/stringUtils.ts` - Filename sanitization

## Future Enhancements

1. **Configure CORS** (if needed for cross-origin access):

   ```bash
   npm install cors @types/cors
   ```

   Add to `server.ts`:

   ```typescript
   import cors from 'cors';
   app.use(cors({
     origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
     credentials: true,
   }));
   ```

2. **Redis-backed rate limiting**: For distributed deployments, use `rate-limit-redis` store

3. **CSP Nonce**: Generate per-request nonces for stricter script-src policy

4. **Report-Only CSP**: Add `Content-Security-Policy-Report-Only` header for testing policy changes
