🛡️ Security in .NET: Content Security Policy (CSP)

While input sanitization protects your database, Content Security Policy (CSP) acts as your application's "Bodyguard" in the browser.

Even if a malicious script bypasses validation filters, CSP instructs the browser:

Do not execute this script.

In this guide, we implement a production-ready Security Middleware that includes:

  • Content Security Policy (CSP)
  • HTTP Strict Transport Security (HSTS)
  • Cross-Origin Isolation headers
  • Swagger compatibility

1️⃣ Why CSP is Mandatory

Modern XSS attacks are highly sophisticated. Attackers may attempt to:

  • Inject malicious inline <script> tags
  • Load malicious JavaScript from external domains
  • Execute dynamic code using eval()
  • Inject scripts through compromised third-party libraries

A Content Security Policy is an HTTP response header that tells browsers which resources are trusted and allowed to execute.

By blocking inline scripts and restricting resource origins, CSP significantly reduces the risk of:

  • Cross-Site Scripting (XSS)
  • Data exfiltration attacks
  • Malicious third-party script execution

2️⃣ The Professional Implementation

We implement CSP using custom middleware in ASP.NET Core.

Middleware ensures security headers are applied consistently across:

  • API responses
  • Error responses
  • Static files
  • Swagger documentation

Using context.Response.OnStarting guarantees headers are attached before the response body begins streaming.

SecurityHeadersMiddleware

namespace ProductApi.Middleware;

public class SecurityHeadersMiddleware(RequestDelegate next)
{
    public async Task InvokeAsync(HttpContext context)
    {
        context.Response.OnStarting(() =>
        {
            var headers = context.Response.Headers;
            var isSwagger = context.Request.Path.StartsWithSegments("/swagger");

            // 1. Basic Hardening
            headers.TryAdd("X-Frame-Options", "DENY");
            headers.TryAdd("X-Content-Type-Options", "nosniff");
            headers.TryAdd("Referrer-Policy", "no-referrer");

            // 2. Enforce HTTPS (HSTS)
            if (context.Request.IsHttps)
            {
                headers.TryAdd(
                    "Strict-Transport-Security",
                    "max-age=31536000; includeSubDomains; preload"
                );
            }

            // 3. Conditional CSP Policy (Swagger Friendly)
            if (isSwagger)
            {
                headers.TryAdd("Content-Security-Policy",
                    "default-src 'self'; " +
                    "script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
                    "style-src 'self' 'unsafe-inline'; " +
                    "img-src 'self' data:;");
            }
            else
            {
                headers.TryAdd("Content-Security-Policy",
                    "default-src 'self'; " +
                    "img-src 'self' data: https:; " +
                    "script-src 'self'; " +
                    "style-src 'self'; " +
                    "font-src 'self'; " +
                    "object-src 'none'; " +
                    "frame-ancestors 'none';");
            }

            // 4. Cross-Origin Isolation (Spectre Protection)
            headers.TryAdd("Cross-Origin-Opener-Policy", "same-origin");
            headers.TryAdd("Cross-Origin-Resource-Policy", "same-origin");

            return Task.CompletedTask;
        });

        await next(context);
    }
}

3️⃣ Breaking Down the CSP Policy

A CSP header is a set of rules that instruct the browser what is allowed to load and execute.

Directive Purpose
default-src 'self' Only allow resources from the same origin
script-src 'self' Blocks inline scripts and external malicious JavaScript
style-src 'self' Prevents injected inline styles
img-src 'self' data: Allows local images and base64 images
object-src 'none' Blocks plugins such as Flash
frame-ancestors 'none' Prevents clickjacking via iframe embedding

4️⃣ Solving the Swagger Compatibility Problem

Swagger UI relies on inline scripts and styles, which are blocked by strict CSP rules.

We detect Swagger requests dynamically:

var isSwagger = context.Request.Path.StartsWithSegments("/swagger");

When Swagger is detected:

  • A relaxed CSP policy is applied
  • Inline scripts are temporarily allowed
  • Developer experience remains smooth

All other API endpoints continue using a strict CSP policy.

💡 Pro Tip In production environments, consider:
  • Disabling Swagger completely
  • Protecting Swagger with authentication
  • Restricting Swagger access via IP allow-list

5️⃣ Advanced Browser Isolation (COOP & CORP)

Modern browsers provide Cross-Origin Isolation headers to prevent memory-based attacks like Spectre.

Header Purpose
COOP Prevents other websites from accessing your window object
CORP Blocks other websites from embedding your resources

🏁 Summary

By implementing this middleware, your API gains multiple layers of protection:

  1. XSS Protection – Malicious scripts are blocked
  2. HTTPS Enforcement – Secure transport is guaranteed
  3. Clickjacking Defense – UI cannot be embedded in iframes
  4. Memory Isolation – Prevents advanced browser attacks
  5. Developer Productivity – Swagger continues to function
🛡️ Security is a process, not a destination. Combine CSP with:
  • Input sanitization
  • Output encoding
  • Authentication
  • HTTPS enforcement
for a true 360° security architecture.

Comments

Popular posts from this blog

Promises in Angular

Debouncing & Throttling in RxJS: Optimizing API Calls and User Interactions

Comprehensive Guide to C# and .NET Core OOP Concepts and Language Features