Skip to content

Commit 543de45

Browse files
author
MPCoreDeveloper
committed
csp L3/4
1 parent e608283 commit 543de45

File tree

13 files changed

+726
-72
lines changed

13 files changed

+726
-72
lines changed

.github/FUNDING.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# These are supported funding model platforms
2+
# See: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository
3+
4+
github: [MPCoreDeveloper]

PACKAGE.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# 🛡️ SafeWebCore
2+
3+
A lightweight, high-performance .NET 10 middleware library that adds security headers to your ASP.NET Core applications. Targets an **A+ rating** on [securityheaders.com](https://securityheaders.com) out of the box.
4+
5+
## Two Ways to Use SafeWebCore
6+
7+
### Option 1 — Strict A+ Preset (fastest)
8+
9+
One line for the strictest A+ configuration. Defined in `ServiceCollectionExtensions.AddNetSecureHeadersStrictAPlus()`.
10+
11+
```csharp
12+
using SafeWebCore.Extensions;
13+
14+
var builder = WebApplication.CreateBuilder(args);
15+
builder.Services.AddNetSecureHeadersStrictAPlus();
16+
17+
var app = builder.Build();
18+
app.UseNetSecureHeaders();
19+
app.Run();
20+
```
21+
22+
Customize the preset — CSP directives are **space-separated**, add multiple origins in one string:
23+
24+
```csharp
25+
builder.Services.AddNetSecureHeadersStrictAPlus(opts =>
26+
{
27+
// Single origin
28+
opts.Csp = opts.Csp with { ImgSrc = "'self' https://cdn.example.com" };
29+
30+
// Multiple origins — just separate with spaces
31+
opts.Csp = opts.Csp with { ImgSrc = "'self' https://cdn1.example.com https://cdn2.example.com data:" };
32+
33+
// Multiple directives at once
34+
opts.Csp = opts.Csp with
35+
{
36+
ConnectSrc = "'self' https://api.example.com wss://ws.example.com",
37+
FontSrc = "'self' https://fonts.gstatic.com https://cdn.example.com"
38+
};
39+
40+
// Non-CSP headers
41+
opts.ReferrerPolicyValue = "strict-origin-when-cross-origin";
42+
});
43+
```
44+
45+
### Option 2 — Fully Custom Configuration
46+
47+
Full control over every header via `ServiceCollectionExtensions.AddNetSecureHeaders()`:
48+
49+
```csharp
50+
using SafeWebCore.Builder;
51+
using SafeWebCore.Extensions;
52+
53+
var builder = WebApplication.CreateBuilder(args);
54+
55+
builder.Services.AddNetSecureHeaders(opts =>
56+
{
57+
// Transport security
58+
opts.EnableHsts = true;
59+
opts.HstsValue = "max-age=31536000; includeSubDomains";
60+
61+
// Framing
62+
opts.EnableXFrameOptions = true;
63+
opts.XFrameOptionsValue = "SAMEORIGIN";
64+
65+
// MIME sniffing
66+
opts.EnableXContentTypeOptions = true;
67+
opts.XContentTypeOptionsValue = "nosniff";
68+
69+
// Referrer
70+
opts.EnableReferrerPolicy = true;
71+
opts.ReferrerPolicyValue = "strict-origin-when-cross-origin";
72+
73+
// Permissions
74+
opts.EnablePermissionsPolicy = true;
75+
opts.PermissionsPolicyValue = "camera=(), microphone=(), geolocation=()";
76+
77+
// Cross-Origin isolation
78+
opts.EnableCoep = true;
79+
opts.CoepValue = "require-corp";
80+
opts.EnableCoop = true;
81+
opts.CoopValue = "same-origin";
82+
opts.EnableCorp = true;
83+
opts.CorpValue = "same-origin";
84+
85+
// Server header
86+
opts.RemoveServerHeader = true;
87+
88+
// CSP — use the fluent builder
89+
opts.Csp = new CspBuilder()
90+
.DefaultSrc("'none'")
91+
.ScriptSrc("'nonce-{nonce}' 'strict-dynamic' https:")
92+
.StyleSrc("'nonce-{nonce}'")
93+
.ImgSrc("'self' https: data:")
94+
.FontSrc("'self' https://fonts.gstatic.com")
95+
.ConnectSrc("'self' wss://realtime.example.com")
96+
.FrameAncestors("'none'")
97+
.BaseUri("'none'")
98+
.FormAction("'self'")
99+
.UpgradeInsecureRequests()
100+
.Build();
101+
});
102+
103+
var app = builder.Build();
104+
app.UseNetSecureHeaders();
105+
app.Run();
106+
```
107+
108+
Both methods are defined in **`SafeWebCore.Extensions.ServiceCollectionExtensions`**.
109+
110+
## Strict A+ Headers
111+
112+
| Header | Strict A+ Value |
113+
|--------|-----------------|
114+
| `Strict-Transport-Security` | `max-age=63072000; includeSubDomains; preload` |
115+
| `Content-Security-Policy` | Nonce-based, `strict-dynamic`, Trusted Types |
116+
| `X-Frame-Options` | `DENY` |
117+
| `X-Content-Type-Options` | `nosniff` |
118+
| `Referrer-Policy` | `no-referrer` |
119+
| `Permissions-Policy` | All 29 browser features denied |
120+
| `Cross-Origin-Embedder-Policy` | `require-corp` |
121+
| `Cross-Origin-Opener-Policy` | `same-origin` |
122+
| `Cross-Origin-Resource-Policy` | `same-origin` |
123+
| `Server` | _(removed)_ |
124+
125+
## Features
126+
127+
- 🔒 **Strict A+ preset** — one-line setup with the strictest security headers
128+
- 🛠️ **Fully custom** — configure every header and CSP directive individually
129+
- 🧩 **Nonce-based CSP** — per-request cryptographic nonces for scripts and styles
130+
- 📋 **Full CSP Level 3** (W3C Recommendation) — all 22 directives, nonce/hash support, `strict-dynamic`, `report-to`, `worker-src`, `frame-src`, `manifest-src`, `script-src-elem/attr`, `style-src-elem/attr`
131+
- 🔮 **CSP Level 4 ready** — Trusted Types (`require-trusted-types-for`, `trusted-types`), `fenced-frame-src` (Privacy Sandbox)
132+
- 🎯 **Fluent CSP Builder** — type-safe, chainable API with full XML documentation
133+
-**Zero-allocation nonce generation**`stackalloc` + `RandomNumberGenerator`
134+
- 🔌 **Extensible** — custom `IHeaderPolicy` implementations
135+
- 📊 **CSP violation reporting** — built-in `/csp-report` endpoint using Reporting API v1
136+
137+
## Documentation
138+
139+
Full documentation: [github.com/MPCoreDeveloper/SafeWebCore/docs](https://github.com/MPCoreDeveloper/SafeWebCore/tree/master/docs)
140+
141+
## License
142+
143+
MIT — see [LICENSE](https://github.com/MPCoreDeveloper/SafeWebCore/blob/master/LICENSE)

README.md

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# 🛡️ SafeWebCore
22

3+
[![NuGet](https://img.shields.io/nuget/v/SafeWebCore.svg?logo=nuget)](https://www.nuget.org/packages/SafeWebCore)
4+
[![NuGet Downloads](https://img.shields.io/nuget/dt/SafeWebCore.svg?logo=nuget)](https://www.nuget.org/packages/SafeWebCore)
35
[![.NET 10](https://img.shields.io/badge/.NET-10-512BD4?logo=dotnet)](https://dotnet.microsoft.com)
46
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
57
[![securityheaders.com](https://img.shields.io/badge/securityheaders.com-A%2B-brightgreen)](https://securityheaders.com)
8+
[![CSP](https://img.shields.io/badge/CSP-Level%203%20%2B%20Level%204-blue?logo=w3c)](https://www.w3.org/TR/CSP3/)
9+
[![Sponsor](https://img.shields.io/badge/Sponsor-❤-ea4aaa?logo=githubsponsors)](https://github.com/sponsors/MPCoreDeveloper)
610

711
**SafeWebCore** is a lightweight, high-performance .NET 10 middleware library that adds security headers to your ASP.NET Core applications. It targets an **A+ rating** on [securityheaders.com](https://securityheaders.com) out of the box — zero configuration required.
812

@@ -11,13 +15,23 @@
1115
## ✨ Features
1216

1317
- 🔒 **A+ in one line**`AddNetSecureHeadersStrictAPlus()` configures the strictest security headers instantly
18+
- 🛠️ **Fully custom**`AddNetSecureHeaders(opts => { ... })` gives you complete control over every header
1419
- 🧩 **Nonce-based CSP** — per-request cryptographic nonces for `script-src` and `style-src`
15-
- 📋 **CSP Level 3** — Trusted Types, `strict-dynamic`, `script-src-elem/attr`, `style-src-elem/attr`, `worker-src`, `fenced-frame-src`
16-
- 🎯 **Fluent CSP Builder** — type-safe, chainable API for building Content Security Policy
20+
- 📋 **Full CSP Level 3** (W3C Recommendation) — all directives including `worker-src`, `manifest-src`, `frame-src`, `script-src-elem/attr`, `style-src-elem/attr`, `report-to`, nonce/hash support, `strict-dynamic`
21+
- 🔮 **CSP Level 4 ready** — Trusted Types (`require-trusted-types-for`, `trusted-types`), `fenced-frame-src` (Privacy Sandbox)
22+
- 🎯 **Fluent CSP Builder** — type-safe, chainable API with full XML documentation for every directive
1723
-**Zero-allocation nonce generation**`stackalloc` + `RandomNumberGenerator` on the hot path
1824
- 🛑 **Server header removal** — hides server technology from attackers
1925
- 🔌 **Extensible** — add custom `IHeaderPolicy` implementations for any header
20-
- 📊 **CSP violation reporting** — built-in middleware for `/csp-report` endpoint
26+
- 📊 **CSP violation reporting** — built-in middleware for `/csp-report` endpoint using Reporting API v1
27+
28+
### CSP Compliance
29+
30+
| Standard | Status | Coverage |
31+
|----------|--------|----------|
32+
| **CSP Level 3** (W3C Recommendation) | ✅ Full | All 22 directives, nonce/hash, `strict-dynamic`, `report-to` |
33+
| **CSP Level 4** (Emerging) | ✅ Ready | Trusted Types, `fenced-frame-src` (Privacy Sandbox) |
34+
| Deprecated directives | ✅ Handled | `report-uri`, `block-all-mixed-content` marked `[Obsolete]` |
2135

2236
---
2337

@@ -66,22 +80,29 @@ That's it! Your application now returns these headers on every response:
6680

6781
### 3. Strict A+ with customization
6882

69-
The preset is intentionally strict. Relax only what your app needs:
83+
The preset is intentionally strict. Relax only what your app needs.
84+
CSP directives are **space-separated** — add multiple origins in a single string:
7085

7186
```csharp
7287
builder.Services.AddNetSecureHeadersStrictAPlus(opts =>
7388
{
74-
// Allow images from your CDN
75-
opts.Csp = opts.Csp with { ImgSrc = "'self' https://cdn.example.com" };
89+
// Multiple CDNs — just separate with spaces
90+
opts.Csp = opts.Csp with { ImgSrc = "'self' https://cdn1.example.com https://cdn2.example.com data:" };
7691

77-
// Allow API calls to your backend
78-
opts.Csp = opts.Csp with { ConnectSrc = "'self' https://api.example.com" };
92+
// Multiple directives at once using 'with { ... }'
93+
opts.Csp = opts.Csp with
94+
{
95+
ConnectSrc = "'self' https://api.example.com wss://ws.example.com",
96+
FontSrc = "'self' https://fonts.gstatic.com https://cdn.example.com"
97+
};
7998

80-
// Use strict-origin-when-cross-origin instead of no-referrer
99+
// Non-CSP headers are simple string properties
81100
opts.ReferrerPolicyValue = "strict-origin-when-cross-origin";
82101
});
83102
```
84103

104+
> 💡 **Tip:** Each CSP directive is one string with space-separated sources. Use a single `with { ... }` block to change multiple directives at once.
105+
85106
### 4. Full manual configuration
86107

87108
For complete control, use `AddNetSecureHeaders` with the fluent CSP builder:

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Welcome to the SafeWebCore documentation. SafeWebCore is a .NET 10 middleware li
2222
| I want to... | Go to |
2323
|---------------|-------|
2424
| Get A+ in one line | [Getting Started](getting-started.md) |
25+
| Configure everything custom | [Getting Started](getting-started.md#fully-custom-setup) |
2526
| Understand what each header does | [Security Headers](security-headers.md) |
2627
| Configure CSP with nonces | [CSP Configuration](csp-configuration.md) |
2728
| Customize the strict preset | [Presets](presets.md) |

docs/csp-configuration.md

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Content Security Policy (CSP) is the most powerful security header for preventing XSS attacks. SafeWebCore provides a fluent builder and nonce-based enforcement out of the box.
44

5+
SafeWebCore implements the **full CSP Level 3** (W3C Recommendation) directive set and forward-looking **CSP Level 4** features including Trusted Types and `fenced-frame-src`.
6+
57
---
68

79
## How CSP Works
@@ -42,6 +44,7 @@ These control where resources can be loaded from.
4244
| `media-src` | `<audio>`, `<video>` | _(inherits 'none')_ |
4345
| `object-src` | `<object>`, `<embed>`, `<applet>` | `'none'` |
4446
| `child-src` | `<frame>`, `<iframe>`, workers | `'none'` |
47+
| `frame-src` | `<frame>`, `<iframe>` (CSP L3, split from child-src) | _(inherits child-src)_ |
4548
| `worker-src` | Worker, SharedWorker, ServiceWorker | `'self'` |
4649
| `manifest-src` | Web app manifest | `'self'` |
4750
| `fenced-frame-src` | `<fencedframe>` (2025+) | _(disabled)_ |
@@ -104,12 +107,12 @@ Every method returns `this` for chaining. Call `.Build()` at the end to get the
104107

105108
## Using CspOptions Directly
106109

107-
Since `CspOptions` is a C# `record`, you can also use `with` expressions:
110+
Since `CspOptions` is a C# `record`, you can use `with` expressions. Each directive is a **space-separated** string of sources:
108111

109112
```csharp
110113
using SafeWebCore.Options;
111114

112-
// Start from Strict A+ and relax
115+
// Start from scratch
113116
opts.Csp = new CspOptions() with
114117
{
115118
DefaultSrc = "'none'",
@@ -119,7 +122,7 @@ opts.Csp = new CspOptions() with
119122
};
120123
```
121124

122-
Or modify the Strict A+ preset:
125+
Or modify the Strict A+ preset — change only what you need, the rest stays strict:
123126

124127
```csharp
125128
builder.Services.AddNetSecureHeadersStrictAPlus(opts =>
@@ -132,6 +135,32 @@ builder.Services.AddNetSecureHeadersStrictAPlus(opts =>
132135
});
133136
```
134137

138+
### Multiple origins per directive
139+
140+
Add as many origins as you need, separated by spaces:
141+
142+
```csharp
143+
builder.Services.AddNetSecureHeadersStrictAPlus(opts =>
144+
{
145+
opts.Csp = opts.Csp with
146+
{
147+
// Images from two CDNs + data URIs
148+
ImgSrc = "'self' https://cdn1.example.com https://cdn2.example.com data:",
149+
150+
// API + WebSocket + analytics
151+
ConnectSrc = "'self' https://api.example.com wss://ws.example.com https://analytics.example.com",
152+
153+
// Google Fonts + your own CDN
154+
FontSrc = "'self' https://fonts.gstatic.com https://cdn.example.com",
155+
156+
// Multiple script hosts (loaded via strict-dynamic trust chain)
157+
ScriptSrc = "'nonce-{nonce}' 'strict-dynamic'"
158+
};
159+
});
160+
```
161+
162+
> 💡 **Key rule:** one string per directive, spaces between origins. The `with { ... }` block lets you change multiple directives in one expression.
163+
135164
---
136165

137166
## Nonce Usage in HTML
@@ -254,8 +283,85 @@ opts.Csp = new CspBuilder()
254283
.ScriptSrc("'nonce-{nonce}' 'strict-dynamic'")
255284
.StyleSrc("'nonce-{nonce}'")
256285
.ImgSrc("'self' https://img.youtube.com")
257-
.ChildSrc("https://www.youtube.com")
286+
.FrameSrc("https://www.youtube.com")
258287
.FrameAncestors("'none'")
259288
.UpgradeInsecureRequests()
260289
.Build();
261290
```
291+
292+
---
293+
294+
## CSP Level 3 / Level 4 Compliance
295+
296+
SafeWebCore implements the **complete CSP Level 3** W3C Recommendation and includes forward-looking **CSP Level 4** directives.
297+
298+
### CSP Level 3 (W3C Recommendation) — ✅ Full Coverage
299+
300+
| Category | Directives | Status |
301+
|----------|------------|--------|
302+
| **Fetch** | `default-src`, `script-src`, `style-src`, `img-src`, `font-src`, `connect-src`, `media-src`, `object-src`, `child-src`, `frame-src`, `worker-src`, `manifest-src` | ✅ All 12 |
303+
| **Granular fetch (L3)** | `script-src-elem`, `script-src-attr`, `style-src-elem`, `style-src-attr` | ✅ All 4 |
304+
| **Document** | `base-uri`, `sandbox` | ✅ Both |
305+
| **Navigation** | `form-action`, `frame-ancestors` | ✅ Both |
306+
| **Reporting** | `report-to` (Reporting API v1) ||
307+
| **Transport** | `upgrade-insecure-requests` ||
308+
| **Nonce-based** | `'nonce-{nonce}'` per-request cryptographic nonces ||
309+
| **Hash-based** | `'sha256-...'`, `'sha384-...'`, `'sha512-...'` allowlisting ||
310+
| **Trust propagation** | `'strict-dynamic'` — trusted scripts can load further dependencies ||
311+
| **Deprecated (L3)** | `report-uri``[Obsolete]`, `block-all-mixed-content``[Obsolete]` | ✅ Handled |
312+
313+
#### Key CSP Level 3 improvements implemented
314+
315+
- **`frame-src` split from `child-src`** — In CSP Level 2, `child-src` governed both frames and workers. Level 3 separates them: `frame-src` for `<frame>`/`<iframe>`, `worker-src` for Worker/SharedWorker/ServiceWorker.
316+
- **`worker-src`** — Dedicated directive for controlling Worker, SharedWorker, and ServiceWorker sources.
317+
- **`manifest-src`** — Controls web app manifest loading.
318+
- **Granular script/style directives**`script-src-elem`, `script-src-attr`, `style-src-elem`, `style-src-attr` provide fine-grained control beyond the base `script-src`/`style-src`.
319+
- **`report-to`** — Replaces the deprecated `report-uri` directive with the modern Reporting API v1.
320+
- **Nonce + hash + `strict-dynamic`** — The recommended approach per Google and the W3C. SafeWebCore generates a unique cryptographic nonce per request using `stackalloc` + `RandomNumberGenerator` (zero heap allocations).
321+
322+
### CSP Level 4 (Emerging) — ✅ Ready
323+
324+
| Directive | Purpose | Status |
325+
|-----------|---------|--------|
326+
| `require-trusted-types-for` | Enforces Trusted Types for DOM XSS sinks (`innerHTML`, `eval()`, etc.) ||
327+
| `trusted-types` | Controls which Trusted Type policy names are allowed ||
328+
| `fenced-frame-src` | Controls `<fencedframe>` sources (Privacy Sandbox) ||
329+
330+
#### Trusted Types
331+
332+
Trusted Types prevent DOM-based XSS at the API level by requiring all dangerous DOM sinks to use type-safe objects instead of raw strings:
333+
334+
```csharp
335+
opts.Csp = new CspBuilder()
336+
.DefaultSrc("'none'")
337+
.ScriptSrc("'nonce-{nonce}' 'strict-dynamic'")
338+
.RequireTrustedTypesFor("'script'")
339+
.TrustedTypes("'none'")
340+
.Build();
341+
```
342+
343+
This blocks calls like `element.innerHTML = userInput` unless the value is wrapped in a Trusted Type policy.
344+
345+
#### Hash-Based Allowlisting
346+
347+
CSP Level 3 supports allowing specific inline scripts/styles by their SHA digest. Pass hash tokens directly in any directive value:
348+
349+
```csharp
350+
opts.Csp = new CspBuilder()
351+
.ScriptSrc("'sha256-abc123...' 'strict-dynamic'")
352+
.StyleSrc("'sha256-def456...'")
353+
.Build();
354+
```
355+
356+
Supported algorithms: `sha256`, `sha384`, `sha512`.
357+
358+
### Deprecated Directives
359+
360+
SafeWebCore correctly handles deprecated directives:
361+
362+
| Directive | Status | Replacement |
363+
|-----------|--------|-------------|
364+
| `report-uri` | `[Obsolete]` — still emitted when set for backward compatibility | `report-to` (Reporting API v1) |
365+
| `block-all-mixed-content` | `[Obsolete]` — modern browsers block mixed content by default | `upgrade-insecure-requests` |
366+
367+
Both deprecated directives are excluded from `CspBuilder` but remain available on `CspOptions` with `[Obsolete]` attributes and compiler warnings.

0 commit comments

Comments
 (0)