ASP.NET Core  

Content Security Policy in ASP.NET Core

Introduction

In a previous article, I explained how modern web applications rely heavily on JavaScript, CSS, images, fonts, APIs, and third-party libraries. While these resources make applications more interactive, they also increase the attack surface.

And input validation and output encoding help reduce these risks, but they are not always sufficient. A single overlooked vulnerability can still allow malicious scripts to execute.

This is where Content Security Policy (CSP) comes in.

Content Security Policy (CSP) is an HTTP response header that instructs browsers which resources are trusted.

For example:

Content-Security-Policy:
default-src 'self';

This simple policy tells the browser:

Only load resources from the same origin.

Unlike many security mechanisms that operate on the server, CSP is enforced by the browser.

If a resource violates the policy, it is blocked.

How CSP Works

Every time the browser encounters a resource, it asks a simple question:

Does this resource satisfy the Content Security Policy?

For example:

Capture d'écran 2026-08-03 235459

The same process applies to:

JavaScript, CSS, Images, Fonts, AJAX requests, WebSockets, iframes, Forms, Media files.

Adding CSP in ASP.NET Core

The simplest approach is using middleware.

app.Use(async (context, next) =>
{
    context.Response.Headers.Append(
        "Content-Security-Policy",
        "default-src 'self';");

    await next();
});

Now every response includes:

Content-Security-Policy:
default-src 'self';

Understanding CSP Directives

A CSP policy is made up of directives.

Each directive controls a different type of resource.

For example:

Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self';

Let's examine the most important directives.

default-src

The fallback directive.

If another directive isn't specified, the browser uses default-src.

Example:

default-src 'self'

Allows:

https://myapp.com/logo.png
https://myapp.com/app.js
https://myapp.com/site.css

Blocks:

https://cdn.example.com/script.js

script-src

Controls JavaScript execution.

script-src 'self'

Allowed:

<script src="/js/site.js"></script>

Blocked:

<script src="https://cdn.example.com/jquery.js"></script>

Unless the CDN is explicitly allowed:

script-src 'self'
https://cdn.example.com

style-src

Controls CSS.

style-src 'self'

Allows:

<link rel="stylesheet" href="/css/site.css">

Blocks:

<style>
body{
    background:red;
}
</style>

unless hashes, nonces, or 'unsafe-inline' are used (will be explained later).

img-src

Controls image loading.

img-src 'self'

Allows:

/images/logo.png

Blocks:

https://images.example.com/logo.png

Need to allow a CDN?

img-src 'self'
https://images.example.com

font-src

Controls web fonts.

font-src 'self'

If using Google Fonts or another provider, you must explicitly allow it.

connect-src

Controls network connections initiated by JavaScript.

Includes: Fetch API, XMLHttpRequest, SignalR, WebSockets, EventSource.

Example:

connect-src 'self'
https://api.example.com

Report-Only Mode

Before enforcing a strict CSP, it's useful to observe what would be blocked.

Use:

Content-Security-Policy-Report-Only

instead of

Content-Security-Policy

Example:

app.Use(async (context, next) =>
{
    context.Response.Headers.Append(
        "Content-Security-Policy-Report-Only",
        "default-src 'self'; script-src 'self';");

    await next();
});

Now resources are not blocked, but violations are reported in the browser console.

This allows you to refine your policy without disrupting users.

CSP for Different ASP.NET Core Applications

Although the CSP header is configured the same way, the policy often differs depending on the application type.

Web APIs

APIs typically return JSON instead of HTML.

A minimal policy is often sufficient:

default-src 'none';
frame-ancestors 'none';
base-uri 'none';

Since no scripts or styles are served, the attack surface is much smaller.

Razor Pages and MVC

Razor applications usually serve HTML, CSS, JavaScript, and images. A policy commonly includes directives such as:

default-src 'self';
script-src 'self';
style-src 'self';
img-src 'self' data:;
font-src 'self';
connect-src 'self';

⚠ Avoid inline scripts and styles whenever possible. If they're required, prefer nonces over 'unsafe-inline'.

Caution

Many developers encounter problems when first adopting CSP.

Some of the most common mistakes include:

  • Using 'unsafe-inline' for scripts, which weakens one of CSP's primary protections.

  • Assuming default-src automatically covers every scenario without verifying browser behavior.

A successful CSP policy is usually refined over time rather than written perfectly on the first attempt.

Conclusion

Content Security Policy is one of the most effective browser-based defenses against Cross-Site Scripting. Rather than trying to identify malicious code, it establishes a clear set of rules that defines which resources the browser is allowed to load and execute.

In ASP.NET Core, implementing CSP can be as simple as adding a response header through middleware. However, creating a robust policy requires understanding your application's resource requirements, and continuously refining the policy as the application changes.

In the next article, we'll explore one of the most frequently misunderstood aspects of CSP: nonces, hashes, and why 'unsafe-inline' should almost always be avoided.