Skip to content

type(security): CORS Configuration Review for Production #103

@jonasyr

Description

@jonasyr

Severity: Low-Medium
Component: CORS Middleware Configuration
Status: Requires Review

Description

Current CORS configuration allows credentials with a specific origin (http://localhost:5173). This needs review for production deployment to ensure secure cross-origin policies.

Current Configuration Evidence

Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Credentials: true
Vary: Origin

Review Checklist

  • Verify ALLOWED_ORIGINS is properly configured from environment variables
  • Confirm development origins aren't allowed in production
  • Review if credentials: true is necessary (enables cookies/auth headers)
  • Check if wildcard origins are never used when credentials: true
  • Verify proper Vary: Origin header for caching
  • Consider if certain endpoints should have stricter CORS

Recommended Implementation

// apps/backend/src/middleware/cors.ts
import cors from 'cors';

const getAllowedOrigins = (): string[] => {
  const nodeEnv = process.env.NODE_ENV;
  const origins: string[] = [];
  
  // Development
  if (nodeEnv === 'development') {
    origins.push('http://localhost:5173');
    origins.push('http://localhost:3000');
  }
  
  // Production
  if (process.env.FRONTEND_URL) {
    origins.push(process.env.FRONTEND_URL);
  }
  
  // Additional allowed origins from env
  if (process.env.ALLOWED_ORIGINS) {
    origins.push(...process.env.ALLOWED_ORIGINS.split(','));
  }
  
  return origins.filter(Boolean);
};

const allowedOrigins = getAllowedOrigins();

export const corsOptions: cors.CorsOptions = {
  origin: (origin, callback) => {
    // Allow requests with no origin (like mobile apps or curl)
    if (!origin) return callback(null, true);
    
    if (allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      console.warn(`CORS blocked origin: ${origin}`);
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'X-Requested-With', 'X-Admin-Token'],
  exposedHeaders: ['X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-RateLimit-Reset'],
  maxAge: 86400 // 24 hours
};

export default cors(corsOptions);

Environment Configuration

# .env.production
NODE_ENV=production
FRONTEND_URL=https://gitray.example.com
# ALLOWED_ORIGINS=https://other-domain.com (if needed)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions