Implementing CORS Securely
Implementing CORS Securely
Node.js/Express Implementation
// Basic CORS implementation
const cors = require('cors');
// Simple CORS for all origins (NOT recommended for production)
app.use(cors());
// Secure CORS configuration
const corsOptions = {
origin: function (origin, callback) {
const allowedOrigins = [
'https://app.example.com',
'https://dashboard.example.com',
'https://mobile.example.com'
];
// Allow requests with no origin (like mobile apps)
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
exposedHeaders: ['X-Total-Count', 'X-Page-Number'],
maxAge: 86400 // 24 hours
};
app.use(cors(corsOptions));
// Dynamic CORS based on environment
const dynamicCors = {
origin: (origin, callback) => {
// Different rules for different environments
if (process.env.NODE_ENV === 'development') {
// More permissive in development
const devOrigins = [
'http://localhost:3000',
'http://localhost:3001',
'http://127.0.0.1:3000'
];
callback(null, devOrigins.includes(origin));
} else {
// Strict in production
const productionOrigins = [
'https://app.example.com',
'https://www.example.com'
];
callback(null, productionOrigins.includes(origin));
}
},
credentials: true
};
// Route-specific CORS
app.get('/api/public', cors({ origin: '*' }), (req, res) => {
res.json({ message: 'Public API endpoint' });
});
app.get('/api/private', cors(corsOptions), (req, res) => {
res.json({ message: 'Private API endpoint' });
});
Manual CORS Implementation
// Understanding CORS by implementing it manually
function setCORSHeaders(req, res, next) {
const origin = req.headers.origin;
const allowedOrigins = new Set([
'https://app.example.com',
'https://trusted-partner.com'
]);
// Check if origin is allowed
if (allowedOrigins.has(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
// Handle preflight requests
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers',
'Content-Type, Authorization, X-Requested-With, X-CSRF-Token');
res.setHeader('Access-Control-Max-Age', '86400');
res.status(204).end();
return;
}
// Expose custom headers
res.setHeader('Access-Control-Expose-Headers',
'X-Total-Count, X-Page-Number, X-RateLimit-Remaining');
next();
}
app.use(setCORSHeaders);