See for yourself – run a scan on your code right now

Introduction

CORS: Four simple letters that carry immense weight in web security. As the digital landscape expands, the bridges that allow our applications to communicate become crucial. CORS is that bridge. The unsung hero ensures seamless interactions, but a slight misstep can lead to vulnerabilities. Let’s journey to understand CORS better and ensure our web apps are fortified against potential threats.

What is CORS?

CORS stands for Cross-Origin Resource Sharing. In essence, web browsers use the security protocol to control how web pages from one origin can request and interact with other resources. Think of it as the customs and border patrol of the web world, meticulously checking the credentials of each request, ensuring only the legitimate ones get through.

Common CORS Misconfigurations

Overly Permissive Origins

It might seem convenient to use a wildcard (*) to allow any origin to access your resources. It’s the equivalent of saying, “Come one, come all!” But just as an open-door policy at a venue can lead to unexpected guests, this approach in the digital realm can invite unwanted security risks. By indiscriminately allowing all origins, you can expose your server to malicious requests and data breaches.

// This is a bad idea!
app.use(cors({
  origin: ‘*’
}));

In this code snippet, the origin: ‘*’ setting in the CORS middleware for an Express.js application allows any website to interact with your server. While this may seem like a quick fix for CORS errors during development, it’s a security risk in a production environment.

Countermeasures:

  • Specify the exact origins that should be allowed to access your resources. This way, you’re explicitly stating who can interact with your server.
  • Regularly audit and prune your list of trusted origins.
  • Implement dynamic origin matching based on a list of trusted domains.
  • Use middleware to validate origins against a database of approved domains.
// Much better
app.use(cors({
  origin: ‘<https://your-trusted-origin.com>’
}));

Here, we replace the wildcard with the precise origin that we want to allow (https://your-trusted-origin.com). This ensures that only requests from this origin will be allowed to access your server’s resources.

Ignoring Non-Simple Requests

Some HTTP methods and headers require a preflight request—a preliminary request that checks whether the actual request is safe to send. Ignoring these preflight requests can expose your application to risks.

// Not handling preflight could be risky
app.options(‘*’, cors()); // enabling CORS preflight for all routes

In this code snippet, the app.options(‘*’, cors()); line enables CORS preflight requests for all routes but doesn’t specify which methods or headers are allowed. This is risky because it doesn’t restrict potentially unsafe HTTP methods or headers.

Countermeasures:

  • Handle Preflight Requests: Properly configure CORS middleware to validate preflight requests.
  • Update Whitelist Regularly: Periodically review and refresh your list of trusted origins.
  • Rate Limit by Origin: Beyond IP-based rate limiting, requests based on their originating domain are also limited.
  • Time-Based Access: Allow sensitive cross-origin requests only during specific time windows.
  • Set Up Anomaly Alerts: Monitor cross-origin requests and alert on unexpected spikes or origins.
// Safe preflight handling
app.options(‘*’, cors({
  methods: [‘GET’, ‘POST’], // allowed methods
  allowedHeaders: [‘Content-Type’, ‘Authorization’]
}));

Here, we specify that only GET and POST methods are allowed and limit the headers to Content-Type and Authorization. This adds an extra layer of security by explicitly stating what’s permitted.

Best Practices for CORS Configuration

Use HTTPS

If you’re not using HTTPS, start now. It’s not just about encrypting data; it’s about establishing trust and authenticity. In a world where data interception and man-in-the-middle attacks are prevalent, HTTPS ensures that the CORS policy and data are securely transmitted, keeping eavesdroppers and potential manipulators at bay.

Limit Exposed Headers

Only expose the headers that are necessary for the application to function. Each header is like a window into your application’s operations. While some windows are essential for transparency and functionality, others can be potential entry points for prying eyes. Limiting exposed headers allows you to draw the curtains on sensitive information.

// Limit exposed headers
app.use(cors({
  exposedHeaders: [‘Authorization’]
}));

In this snippet, we’re limiting the headers exposed to the client to just the Authorization header. This minimizes the risk of leaking sensitive information.

Keep It Short and Sweet

The less complex your CORS settings, the less room for errors. It’s akin to a chef using just the right ingredients in a dish too many, and the flavors might clash, leading to an undesirable outcome. Keep your configuration as minimal as possible, ensuring it’s palatable and effective for your application’s needs.

Advanced Techniques for CORS Security

Logging and Monitoring

Monitoring CORS requests is like having CCTV cameras at crucial points in a building. It helps you spot any unusual patterns or potential intruders. By keeping a vigilant eye on the interactions with your server, you’re not just passively observing but actively safeguarding your digital premises.

// Logging CORS requests
app.use((req, res, next) => {
  console.log(`CORS request from origin: ${req.header(‘Origin’)}`);
  next();
});

This middleware logs the origin of each incoming request, helping you keep track of who is interacting with your server.

Rate Limiting

Imagine a scenario where there’s a sudden rush at a store, and the staff is overwhelmed. That’s what it’s like for a server without rate limiting when faced with a barrage of requests. Implementing rate limiting is like having a controlled entry system, ensuring your server can cater to each request without being overwhelmed or compromised.

 

// Implement rate limiting
const rateLimit = require(‘express-rate-limit’);

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

app.use(limiter);

Here, we use the express-rate-limit package to limit each IP address to 100 requests every 15 minutes. This helps in mitigating brute-force attacks.

Conclusion

With the right balance and attention to detail, you can configure CORS to make your web application functional and secure. So roll up those sleeves and give your web app the secure, efficient CORS settings it deserves.  Are you looking to fortify your web app further? Book a demo with our Qwiet.ai today and embark on a journey towards unparalleled web security.

About ShiftLeft

ShiftLeft empowers developers and AppSec teams to dramatically reduce risk by quickly finding and fixing the vulnerabilities most likely to reach their applications and ignoring reported vulnerabilities that pose little risk. Industry-leading accuracy allows developers to focus on security fixes that matter and improve code velocity while enabling AppSec engineers to shift security left.

A unified code security platform, ShiftLeft CORE scans for attack context across custom code, APIs, OSS, containers, internal microservices, and first-party business logic by combining results of the company’s and Intelligent Software Composition Analysis (SCA). Using its unique graph database that combines code attributes and analyzes actual attack paths based on real application architecture, ShiftLeft then provides detailed guidance on risk remediation within existing development workflows and tooling. Teams that use ShiftLeft ship more secure code, faster. Backed by SYN Ventures, Bain Capital Ventures, Blackstone, Mayfield, Thomvest Ventures, and SineWave Ventures, ShiftLeft is based in Santa Clara, California. For information, visit: www.shiftleft.io.

Share

See for yourself – run a scan on your code right now