Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions RATE_LIMITING_CORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Rate Limiting and CORS Configuration

## Overview
This implementation provides comprehensive rate limiting and CORS configuration for the API Gateway to prevent brute-force attacks and ensure proper cross-origin resource sharing.

## Features Implemented

### 1. Rate Limiting
- **General API**: 50 requests/minute (production), 100 requests/minute (development)
- **Authentication**: 5 attempts per 15 minutes
- **Strict Operations**: 10 requests per minute
- **Progressive Slowdown**: Delays after 20 requests in 15 minutes

### 2. CORS Configuration
- **Preflight Support**: Handles OPTIONS requests correctly
- **Origin Validation**: Allows specific frontend domains
- **Credentials**: Supports cookies and authorization headers
- **Development Mode**: Allows localhost origins in development

### 3. Security Headers
- **Helmet**: Comprehensive security headers
- **CSP**: Content Security Policy
- **HSTS**: HTTP Strict Transport Security
- **XSS Protection**: Cross-site scripting protection

## API Endpoints

### Rate Limiting Test
```bash
# Test rate limiting (limited to 10 requests/minute)
GET /api/test-security/rate-limit
```

### CORS Test
```bash
# Test CORS configuration
GET /api/test-security/cors

# Test CORS preflight
OPTIONS /api/test-security/cors
```

### Security Headers Test
```bash
# Test security headers
GET /api/test-security/security-headers
```

## Configuration

### Environment Variables
```bash
NODE_ENV=production # Enables stricter rate limits
JWT_SECRET=your-secret-key
```

### Rate Limits
- **General**: 50/min (prod), 100/min (dev)
- **Auth**: 5 attempts per 15 minutes
- **Strict**: 10 requests per minute
- **Speed Limit**: 20 requests, then 500ms delay

### CORS Origins
- `http://localhost:3000` (React)
- `http://localhost:5173` (Vite)
- `http://localhost:8080` (Vue)
- Production domains (to be added)

## Testing

### 1. Rate Limiting Test
```bash
# Make multiple requests to test rate limiting
for i in {1..15}; do
curl -w "%{http_code}\n" http://localhost:5000/api/test-security/rate-limit
done
```

### 2. CORS Test
```bash
# Test from browser console
fetch('http://localhost:5000/api/test-security/cors', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(data => console.log(data));
```

### 3. Preflight Test
```bash
# Test CORS preflight
curl -X OPTIONS \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type" \
http://localhost:5000/api/test-security/cors
```

## Error Responses

### Rate Limit Exceeded
```json
{
"success": false,
"message": "Too many requests from this IP, please try again later.",
"retryAfter": "1 minute"
}
```

### CORS Error
```json
{
"success": false,
"message": "CORS policy violation: Origin not allowed",
"origin": "http://disallowed-origin.com"
}
```

## Security Features

1. **Brute Force Protection**: Stricter limits on auth endpoints
2. **Progressive Delays**: Slows down repeated requests
3. **IP-based Limiting**: Tracks requests per IP address
4. **Origin Validation**: Only allows trusted domains
5. **Security Headers**: Comprehensive security headers via Helmet

## Production Considerations

1. **Redis Store**: Consider using Redis for distributed rate limiting
2. **Whitelist**: Add trusted IPs for higher limits
3. **Monitoring**: Implement rate limit monitoring
4. **Custom Messages**: Customize error messages per endpoint
5. **Bypass Options**: Add bypass mechanisms for admin users

## Files Created
- `src/middleware/rateLimiter.middleware.js` - Rate limiting configuration
- `src/middleware/cors.middleware.js` - CORS configuration
- `src/config/app.config.js` - Environment configuration
- `src/routes/test-security.routes.js` - Test endpoints
35 changes: 26 additions & 9 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import express from 'express';
import cors from 'cors';
import passport from './config/passport.config.js';
import productRoutes from './routes/product.routes.js';
import cartRoutes from './routes/cart.routes.js';
import collectionRoutes from './routes/collection.routes.js';
import wishlistRoutes from './routes/wishlist.routes.js';
import testRoutes from './routes/test.routes.js';
import testSecurityRoutes from './routes/test-security.routes.js';
import authRoutes from './routes/auth.routes.js';
import errorHandler from './middleware/error-handler.middleware.js';
import notFound from './middleware/notFound.middleware.js'
import cookieParser from 'cookie-parser';

import notFound from './middleware/notFound.middleware.js';
import { corsMiddleware, securityHeaders, corsErrorHandler } from './middleware/cors.middleware.js';
import { generalRateLimit, authRateLimit, speedLimiter, strictRateLimit } from './middleware/rateLimiter.middleware.js';
const app = express();

app.use(cors());
app.use(express.json());
app.use(cookieParser());
// Security middleware (must be first)
app.use(securityHeaders);

// CORS middleware
app.use(corsMiddleware);

// Body parsing middleware
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));

// Rate limiting middleware
app.use(generalRateLimit);
app.use(speedLimiter);

// Initialize Passport
app.use(passport.initialize());
Expand All @@ -24,16 +34,23 @@ app.get('/',(req,res)=>{
res.send("Welcome to Homepage");
})

// Routes
// Routes with specific rate limiting
app.use('/api/products', productRoutes);
app.use('/api/cart', cartRoutes);
app.use('/api/collections', collectionRoutes);
app.use('/api/wishlist', wishlistRoutes);
app.use('/api/test', testRoutes);
app.use('/auth', authRoutes);
app.use('/api/test-security', testSecurityRoutes);

// Auth routes with stricter rate limiting
app.use('/auth', authRateLimit, authRoutes);

// CORS error handler
app.use(corsErrorHandler);

// Middleware for not found 404
app.use(notFound);

// Global error handler (should be last)
app.use(errorHandler);

Expand Down
91 changes: 91 additions & 0 deletions src/config/app.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Environment configuration for rate limiting and CORS
*/

export const config = {
// Rate limiting configuration
rateLimit: {
// General API rate limits
general: {
windowMs: 60 * 1000, // 1 minute
max: process.env.NODE_ENV === 'production' ? 50 : 100, // 50 in prod, 100 in dev
},
// Authentication rate limits (stricter)
auth: {
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts per 15 minutes
},
// Strict rate limits for sensitive operations
strict: {
windowMs: 60 * 1000, // 1 minute
max: 10, // 10 requests per minute
},
// Speed limiter configuration
speedLimit: {
windowMs: 15 * 60 * 1000, // 15 minutes
delayAfter: 20, // Allow 20 requests per 15 minutes at full speed
delayMs: 500, // Add 500ms delay per request after delayAfter
maxDelayMs: 20000, // Maximum delay of 20 seconds
},
},

// CORS configuration
cors: {
// Allowed origins
allowedOrigins: [
'http://localhost:3000', // React dev server
'http://localhost:3001', // Alternative React port
'http://localhost:5173', // Vite dev server
'http://localhost:8080', // Vue dev server
'http://127.0.0.1:3000', // Localhost alternative
'http://127.0.0.1:5173', // Vite localhost alternative
// Add production domains here
// 'https://yourdomain.com',
// 'https://www.yourdomain.com',
],
// Additional CORS settings
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
allowedHeaders: [
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'Authorization',
'Cache-Control',
'Pragma',
],
exposedHeaders: [
'X-Total-Count',
'X-Page-Count',
'X-Current-Page',
],
},

// Security configuration
security: {
// Content Security Policy
csp: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
},
},
// HSTS configuration
hsts: {
maxAge: 31536000, // 1 year
includeSubDomains: true,
preload: true,
},
},

// Request size limits
requestLimits: {
json: '10mb',
urlencoded: '10mb',
},
};

export default config;
99 changes: 99 additions & 0 deletions src/middleware/cors.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import cors from 'cors';
import helmet from 'helmet';

/**
* CORS configuration for frontend integration
* Handles preflight requests and allows specific origins
*/

// Allowed origins for CORS
const allowedOrigins = [
'http://localhost:3000', // React dev server
'http://localhost:3001', // Alternative React port
'http://localhost:5173', // Vite dev server
'http://localhost:8080', // Vue dev server
'http://127.0.0.1:3000', // Localhost alternative
'http://127.0.0.1:5173', // Vite localhost alternative
// Add production domains here
// 'https://yourdomain.com',
// 'https://www.yourdomain.com',
];

// CORS options
const corsOptions = {
origin: (origin, callback) => {
// Allow requests with no origin (mobile apps, Postman, etc.)
if (!origin) return callback(null, true);

// Check if origin is in allowed list
if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
// In development, allow any localhost origin
if (process.env.NODE_ENV !== 'production' && origin.includes('localhost')) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
},
credentials: true, // Allow cookies and authorization headers
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
allowedHeaders: [
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'Authorization',
'Cache-Control',
'Pragma',
],
exposedHeaders: [
'X-Total-Count',
'X-Page-Count',
'X-Current-Page',
],
optionsSuccessStatus: 200, // Some legacy browsers choke on 204
preflightContinue: false, // Pass the CORS preflight response to the next handler
};

// Security headers configuration
export const securityHeaders = helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
},
},
crossOriginEmbedderPolicy: false, // Disable for API compatibility
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
});

// CORS middleware
export const corsMiddleware = cors(corsOptions);

// CORS error handler
export const corsErrorHandler = (err, req, res, next) => {
if (err.message === 'Not allowed by CORS') {
res.status(403).json({
success: false,
message: 'CORS policy violation: Origin not allowed',
origin: req.headers.origin,
});
} else {
next(err);
}
};

export default {
corsMiddleware,
securityHeaders,
corsErrorHandler,
corsOptions,
};
Loading