Content Security Policy
- 7 minutes to read
A Content Security Policy (CSP) is an additional layer of security built into most modern browsers. It allows the browser to recognize and mitigate certain types of risks, including Cross Site Scripting (XSS) and data injection attacks. These attacks include, but are not limited to, data theft, page spoofing, and malware distribution.
The CSP defines a list of policies/directives and initial values that specify which resources your site will allow/disallow.
To enable CSP protection, specify a Content-Security-Policy
header or use the <meta>
tag, and explicitly define permitted functions using CSP directives.
You can only specify the default fetch directive to enable CSP:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
This policy prevents DevExpress Reporting components from functioning properly and causes DevExpress Reports to require additional permissions, as described below.
Directives for Reporting Components
The following meta
tag specifies minimum required directives for DevExpress Reporting components:
<head>
<!--...-->
<meta http-equiv="Content-Security-Policy" content="default-src 'self';
img-src data: https: http:;
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
connect-src 'self';
worker-src 'self' blob:;
frame-src 'self' blob:;" />
<!--...-->
</head>
default-src 'self';
- Fallback for other fetch directives.
img-src data: https: http:;
- Allows components to load specific images and document pages.
script-src 'self' 'unsafe-inline';
Allows only scripts loaded from the same source as the current page protected with CSP.
In ASP.NET Core and Blazor applications, you can remove the
unsafe-inline
keyword. Refer to the following section for more information: Disallow Inline Styles and Inline Scripts.style-src 'self' 'unsafe-inline';
Allows the use of inline style elements.
In ASP.NET Core and Blazor applications, you can remove the
unsafe-inline
keyword. Refer to the following section for more information: Disallow Inline Styles and Inline Scripts.connect-src 'self' (optional) 'http:my_backend_url';
- The optional
my_backend_url
value specifies the server endpoint. This is necessary for JavaScript-Based Reporting apps when client and server applications have different URLs. worker-src 'self' blob:;
- Required for printing.
frame-src 'self' blob: (optional) 'http:my_backend_url';
- Required for printing. The optional
my_backend_url
value specifies the server endpoint. This is necessary if client and server applications have different URLs.
Note
Blazor WebAssembly applications require the unsafe-eval
rule in the script-src
directive.
Refer to the following article for more information: Enforce a Content Security Policy for ASP.NET Core Blazor.
Disallow Inline Styles and Inline Scripts
ASP.NET Core Applications (Nonce-Based CSP)
You can disallow inline scripts and inline styles for DevExpress Reporting components in ASP.NET Core applications to remove the unsafe-inline
keyword for script-src
and style-src
directives and implement a nonce-based CSP. The main idea behind this approach is to generate a cryptographic nonce (“number used once”), include it in the desired policy , and add the nonce value in the nonce attribute to every script/style tag. The browser only executes inline code that includes the correct nonce value. The nonce value should regenerate each time you reload the page.
To implement the nonce-based approach in ASP.NET Core applications with DevExpress Reporting components, create a cryptographic nonce and pass it to the ReportDesignerBuilder.Nonce / WebDocumentViewerBuilder.Nonce methods.
Example
This example demonstrates how to implement a nonce-based Content Security Policy (CSP) for an ASP.NET Core Application through an HTTP response header:
In the HomeController.cs file, generate the nonce value. The RandomNumberGenerator class is used to generate cryptographically strong random values. Add an HTTP header with the Content Security Policy with nonce for the script-src
directive.
The following code snippet shows how to add a nonce-based CSP for the Report Designer component:
using DevExpress.XtraReports.Web.ReportDesigner;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading.Tasks;
//...
public async Task<IActionResult> Designer(
[FromServices] IReportDesignerClientSideModelGenerator clientSideModelGenerator,
[FromQuery] string reportName) {
var nonceBytes = new byte[32];
using var generator = RandomNumberGenerator.Create();
generator.GetBytes(nonceBytes);
var nonce = Convert.ToBase64String(nonceBytes);
HttpContext.Response.Headers.Add("Content-Security-Policy",
string.Format("script-src 'self' 'nonce-{0}';", nonce) +
"img-src data: https: http:;" +
"style-src 'self';" +
"connect-src 'self';" +
"worker-src 'self' blob:;" +
"frame-src 'self' blob:;"
);
Models.ReportDesignerCustomModel model = new Models.ReportDesignerCustomModel();
model.ReportDesignerModel = await CreateDefaultReportDesignerModel(clientSideModelGenerator, reportName, null);
model.Nonce = nonce;
return View(model);
}
//...
The new nonce value is generated each time the page loads.
On the page, pass the nonce value to the Nonce method:
@{
var designerRender = Html.DevExpress().ReportDesigner("reportDesigner")
.Height(null)
.Width(null)
.Nonce(Model.Nonce)
.CssClassName("my-reporting-component")
.Bind(Model.ReportDesignerModel);
@designerRender.RenderHtml()
}
Note
Set the Height and Width properties to null
for the application to work properly. You can specify the required height and width in a CSS class.
Blazor Applications (JavaScript-Based Controls)
To remove the unsafe-inline
keyword for script-src
and style-src
directives, set the Height
and Width
properties of the DxDocumentViewer / DxWasmDocumentViewer and DxReportDesigner / DxWasmReportDesigner to null
. You can specify the required height and width in a CSS class.
Note
For Blazor WebAssembly applications, add additional script-src
and style-src
hashes as required by the app to remove the unsafe-inline
rule.
The following code snippets show how to set the Height
and Width
properties of the DxDocumentViewer to null and use a css class:
@page "/documentviewer"
<DxDocumentViewer ReportName="TestReport" Height="@null" Width="@null" CssClass="my-reporting-element-container">
<DxDocumentViewerTabPanelSettings Width="340" />
</DxDocumentViewer>
You can define a css class as follows:
/*...*/
.dx-blazor-reporting .dx-designer.my-reporting-element-container {
width: 100%;
height: calc(100vh - 130px);
}
Troubleshooting
Error with ‘unsafe-eval’ Directive
The use strict
directive in scripts may generate a “Refused to evaluate a string as JavaScript because ‘unsafe-eval’ is not an allowed source of script…” error.
To address this error, include the Knockout library in the global scope and use an alias to reference the library.
For Angular applications:
Add the Knockout library to the page:
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.js"></script>
Create the
src/knockout_global.js
file with the following content:module.exports = window['ko'];
Specify paths in the tsconfig.json file:
{ //... "compilerOptions": { //... "paths": { "knockout": ["src/knockout_global.js"] }, //... }, //... }
Custom Templates Do Not Work
DevExpress Reporting custom templates are based on the Knockout JavaScript library. The Knockout library uses the data-bind
attribute to render a value in the following manner: it generates a function as a JavaScript string and passes the string to the new Function
constructor.
To function properly, Knockout templates require the script-src 'unsafe-eval'
CSP directive.
Important
We do not recommend the inclusion of the script-src 'unsafe-eval'
directive in your content security policy. This directive may introduce a vulnerability as it enables script execution from a string on your page.
DevExpress Reporting stores JavaScript functions related to data-bind
attributes in the cache, thus eliminating the need to run the script on the page. Our components do not need the ‘unsafe-eval’ directive.
Follow the steps below to use custom templates.
Call the addToBindingsCache Function
To add a custom template to the function cache, call the addToBindingsCache
function before the component is rendered. You can handle the BeforeRender
event to call the function.
Example: DevExtreme Template
<div data-options="dxTemplate: { name: 'content' }"></div>
Example: Knockout Binding
<div data-bind="text: text, attr: { title: text }"></div>
Use the CLI Utility
v22.2 ships with our @devexpress/analytics-core-cli
CLI utility package. It includes the processBindings
command. You can use this command to automatically generate a file with the code that calls the addToBindingsCache
function to add your templates to the cache.
Run the following command to install the package:
npm i @devexpress/analytics-core-cli
To process custom templates, execute the following command:
node node_modules/@devexpress/analytics-core-cli/utils/processBindings <templates folder path> <result file path>
Command parameters are as follows:
- templates folder path
- A folder that contains template files (.HTML)
- result file path
- Path to the file being created
When prompted, select application type (Modules or Namespaces):
The generated file contains JavaScript code that must be run in the DevExpress Reporting component’s BeforeRender
event handler.