Content security policy headers (CSP)
Summary
Create a possibility to generate Content security policy (csp) headers .
Content Security Policy is a crucial security standard that helps protect your web applications from various types of attacks, including Cross-Site Scripting (XSS), clickjacking, and other code injection attacks. It works by allowing you to specify which resources (scripts, styles, images, etc.) your browser should be allowed to load. more info about Content security policy
Description of solution
The intend is to manage CSP for WebForms and MVC pipeline.
-
Webforms can not be very struct in the policy that can be used. Here script-src 'unsafe-inline' and 'unsafe-eval' will be automatically added to csp. It is not added in the settings because it's specific for webforms.
-
In the future MVC pipeline can be very strict. Here no js evaluation will be used and all inline javascript will be marked with a nonce.
This is the default csp for the setting
default-src 'self'; script-src 'self' 'report-sample'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'; frame-src 'self'; connect-src 'self';
- It dous not accept external resources.
- It accept inline css : actually used by the default page of dnn and generated by ckeditor with default configuration. But it is not recomended. ckeditor can be configurated to not use inline css.
- It accept image src starting with data: bacause it is used by default dnn page. But it is not recomended.
3 http headers will be managed : Content-Security-Policy, Content-Security-Policy-Report-Only and Reporting-Endpoints
Persona bar - Security settings
Implementation
It commes in a new project for csp management and a test project.
The IContentSecurityPolicy service that can be used with DI had all the stuff for skin and module developers to contribute to the policy.
For webforms skin developers actually the way to contribute is
<script runat="server">
protected void Page_Init()
{
var defaultPage = (DotNetNuke.Framework.DefaultPage)this.Page;
defaultPage.AddCsp("script-src https://ajax.aspnetcdn.com");
}
</script>
Details of DotNetNuke.ContentSecurityPolicy library
The DotNetNuke.ContentSecurityPolicy library provides a fluent API for building and emitting Content Security Policy (CSP) headers in DNN. The IContentSecurityPolicy interface is the main entry point to compose directives, manage sources, configure reporting, and generate final header strings.
Interface: IContentSecurityPolicy
Namespace: DotNetNuke.ContentSecurityPolicy
Properties
- Nonce: Cryptographically secure nonce value to use with inline script/style tags.
- DefaultSource:
SourceCspContributorfordefault-src. - ScriptSource:
SourceCspContributorforscript-src. - StyleSource:
SourceCspContributorforstyle-src. - ImgSource:
SourceCspContributorforimg-src. - ConnectSource:
SourceCspContributorforconnect-src. - FontSource:
SourceCspContributorforfont-src. - ObjectSource:
SourceCspContributorforobject-src. - MediaSource:
SourceCspContributorformedia-src. - FrameSource:
SourceCspContributorforframe-src. - FrameAncestors:
SourceCspContributorforframe-ancestors. - FormAction:
SourceCspContributorforform-action. - BaseUriSource:
SourceCspContributorforbase-uri.
Methods
- RemoveScriptSources(CspSourceType cspSourceType): Remove script sources of the specified type (e.g.,
Inline,Self,Nonce). - AddPluginTypes(string value): Add values for
plugin-types(e.g.,application/pdf). - AddSandboxDirective(string value): Add
sandboxoptions (e.g.,allow-scripts allow-same-origin). - AddFormAction(CspSourceType sourceType, string value): Add a
form-actionsource. - AddFrameAncestors(CspSourceType sourceType, string value): Add a
frame-ancestorssource. - AddReportEndpoint(string name, string value): Add a named reporting endpoint.
- AddReportTo(string value): Add a
report-togroup name to the policy. - AddHeaders(string cspHeader): Parse and merge a CSP header string; returns the same
IContentSecurityPolicyfor chaining. - GeneratePolicy(): Build the
Content-Security-Policyheader value. - GenerateReportingEndpoints(): Build the reporting header value(s).
- UpgradeInsecureRequests(): Add the
upgrade-insecure-requestsdirective.
Working with sources
Directive properties expose a SourceCspContributor, which supports adding/removing sources such as:
AddSelf()→'self'AddNone()→'none'AddInline()→'unsafe-inline'AddEval()→'unsafe-eval'AddStrictDynamic()→'strict-dynamic'AddNonce(string)→'nonce-<value>'AddHash(string)→'sha256-...','sha384-...','sha512-...'AddHost(string)→example.com,https://cdn.example.comAddScheme(string)→https:,data:,blob:RemoveSources(CspSourceType)to remove by type
See: CspSourceType.cs, CspSource.cs, SourceCspContributor.cs.
Usage examples
Configure a baseline policy with a nonce
using DotNetNuke.ContentSecurityPolicy;
public class CspExample
{
private readonly IContentSecurityPolicy _csp;
public CspExample(IContentSecurityPolicy csp)
{
_csp = csp;
}
public void Configure()
{
// Default baseline
_csp.DefaultSource.AddSelf();
_csp.ScriptSource.AddSelf().AddNonce(_csp.Nonce);
_csp.StyleSource.AddSelf();
_csp.ImgSource.AddSelf().AddScheme("data:");
// Lock down frames and forms
_csp.FrameAncestors.AddNone();
_csp.FormAction.AddSelf();
// Reporting
_csp.AddReportEndpoint("csp-endpoint", "/api/csp/report");
_csp.AddReportTo("csp-endpoint");
// Optionally upgrade insecure requests
_csp.UpgradeInsecureRequests();
// Generate header values
var cspHeader = _csp.GeneratePolicy();
var reportingHeader = _csp.GenerateReportingEndpoints();
}
}
Parse and merge an existing CSP header
_csp.AddHeaders("default-src 'self'; img-src 'self' data:")
.ScriptSource.AddNonce(_csp.Nonce);
var headerValue = _csp.GeneratePolicy();
Remove an unsafe source
_csp.RemoveScriptSources(CspSourceType.Inline);
Notes
- Nonce: use
Noncein your inline tags:<script nonce="{policy.Nonce}">. - Reporting: ensure your endpoint exists to accept violation reports.
- Parsing:
AddHeadersis useful to import settings from configuration and extend them programmatically. - This is not only about js and css but also about images, iframes and more (parts of the content).
Update 22 october 2025
There are 2 point of views :
-
The site settings are there for the site admin to enforce some policies. In this case, the policy need to include all requirements (from skins, modules, skin object, content, ...) and all pages (not only dnn pages but also edit urls need to be included in this policies to get them work properly). Actually this can already done with a the web.config setting. Or you need different policies for different use cases/ user profils (Anonymous, loged in, page admin, module editor, host, ...).
-
(The point of view of this PR) The site settings are there for defining a starting policy where skins, modules, ... can add dynamically there policy. So the policy will be adjusted automatically to make the site work.
What is sure is that modules and skins will paticipate in the ability to make the policies strict. Knowing witch policies are required by a skin or modules is key even what kind of content is permited, if you want to be strict.
Know that browsers include also a way to report csp policy violations to the browser console and to a endpoint.
Fixes #6720
With this, how exactly does the configured process in the persona bar work with the fact that anyone could manipulate this via other methods in the skin, module, etc?
Is the portal done first and then changes applied?
How is the users input validated in the personarbar validated? is it serialized to an object with detailed validation errors?
With this, how exactly does the configured process in the persona bar work with the fact that anyone could manipulate this via other methods in the skin, module, etc? Is the portal done first and then changes applied?
Yes, the portal settings are applied first in the OnInit of the page (default.aspx). Then skin and modules can add there policy. Normally skins and modules will only add addional policy. And finally in the OnPreRender of the page the full policy string is generated and added to the http response header.
How is the users input validated in the personarbar validated? is it serialized to an object with detailed validation errors?
The library containt a policy parser. But actully no validation error is showed to the user that enter the settings. I will look to add this.
@sachatrauwaen: can you add a switch for the admin that governs whether a module can enlarge the CSP scope or not? That way the admin decides what is allowed and knows what is going to happen.
@sachatrauwaen: can you add a switch for the admin that governs whether a module can enlarge the CSP scope or not? That way the admin decides what is allowed and knows what is going to happen.
In reality there 2 ways to add policies to CSP : 1) site settings 2) api (used by the core, modules, skins, editor providers and potentialy all other extensions).
The site settings where actually for define the default strict policy, policy for content (images, iframe, css) and policy for modules that not automatically add there policy.
When we want to add a "switch for the admin that governs whether a module can enlarge the CSP scope or not" it is for all policies added by api not only modules. So we add only the policy from the site settings.
New Sitting is added