CORS Policies and Security
What is CORS?
Cross-Origin Resource Sharing (CORS) is a security mechanism implemented by browsers to prevent a malicious website from making requests to your API using a user’s authenticated session (a form of CSRF).
By default, browsers follow the Same-Origin Policy, which means a script on domain-a.com cannot call an API on domain-b.com. CORS provides a way for domain-b.com to explicitly say: “I trust requests coming from domain-a.com.”
Core Terminology
- Origin: The combination of Scheme (HTTPS), Host (example.com), and Port (443).
- Preflight (OPTIONS): A preliminary request sent by the browser to check if the server allows the actual request (required for
PUT,DELETE, or custom headers).
Configuration in ASP.NET Core
In .NET, CORS is configured in the Dependency Injection container and applied via middleware.
1. Define Policies in Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
// Strict Policy for the main Frontend
options.AddPolicy("FrontendApp", policy =>
{
policy.WithOrigins("https://www.myapp.com")
.AllowAnyHeader()
.AllowAnyMethod();
});
// Read-only Policy for Partners
options.AddPolicy("PartnerSafe", policy =>
{
policy.WithOrigins("https://partner.io")
.WithMethods("GET")
.WithHeaders("X-API-KEY");
});
});
2. Apply Middleware
var app = builder.Build();
app.UseRouting();
// Must be between UseRouting and UseAuthorization
app.UseCors("FrontendApp");
app.UseAuthorization();
Security Best Practices
- Never Use
AllowAnyOrigin(): This effectively disables CORS protection. Always be explicit. - Avoid
AllowAnyMethod()for Public APIs: If a partner only needs read access, only allowGET. - Credential Safety: Use
.AllowCredentials()only when necessary (for cookies/Windows Auth). Note that you cannot use wildcards (*) with credentials enabled. - Middleware Order: If
app.UseCors()is placed afterapp.MapControllers(), it will never be executed for those routes.
Practice Exercise
Configure a policy that allows a mobile web app (https://m.myapp.com) to access your API using only GET and POST, and caches the preflight result for 10 minutes.
Answer
The Implementation
builder.Services.AddCors(options =>
{
options.AddPolicy("MobileApp", policy =>
{
policy.WithOrigins("https://m.myapp.com")
.WithMethods("GET", "POST")
.WithHeaders("Content-Type", "Authorization")
.SetPreflightMaxAge(TimeSpan.FromMinutes(10));
});
});
Why This Works
- Minimum Privilege: By restricting to
GETandPOST, we prevent the mobile app from accidentally or maliciously triggeringDELETEorPATCHoperations. - Performance:
SetPreflightMaxAgetells the browser to “remember” this permission for 10 minutes, avoiding the extraOPTIONSnetwork call for every subsequent request. - Header Guard: We only allow the
AuthorizationandContent-Typeheaders, reducing the attack surface for custom header exploits.
Summary
CORS is not a “bug” to be fixed with a wildcard—it is a critical security layer. By defining granular policies and understanding the preflight process, you can build APIs that are both accessible to your frontend and resilient against cross-site attacks.