Content Security Policy

CSP (Content Security Policy) has been introduced in WEM 4.2, enhancing the security of your WEM and web applications in general. However, CSP restricts certain common web features, requiring us to implement them differently to ensure they function correctly in a CSP-compliant environment. Below are some common cases that need to be updated for compatibility with CSP.

Startup Scripts, Script Modules, etc.

Fortunately, script blocks such as startupscript, submitscript, unloadscript, and scriptmodule do not require any modifications, as they are already trusted. However, a few unsafe functions and classes should not be used, as they can allow malicious code to be executed. We will discuss these cases below.

The Nonce Attribute

The nonce attribute is used in HTML to enhance security by preventing certain types of attacks, such as Cross-Site Scripting (XSS). It helps control which scripts and styles are allowed to run on a webpage, thereby improving the overall security posture of web applications. When a <script> or <style> tag includes a nonce, it indicates that the content is trusted and can be executed or applied. Implementing this in WEM is straightforward; simply add the nonce attribute and use the CspNonce magic constant as follows:

<script nonce="<?attr CspNonce ?>"> console.log("Hello"); </script>
<style nonce="<?attr CspNonce ?>"> .my-cool-element { border-radius: 5px; } </style>

Inline Scripts

Inline scripts, such as JavaScript within an onclick attribute, must now be placed inside a trusted <script> block. This block should either be annotated with a nonce or run within a startupscript.

Previously, you might have written something like this:

<button onclick="handler1()">Button 1</button>
<button onclick="handler2()">Button 2</button>

This is no longer allowed. Instead, the handlers should be defined in a JavaScript code block, as shown below:

<button id="<?attr OutputId() ?>-btn1">Button 1</button>
<button id="<?attr OutputId() ?>-btn2">Button 2</button>
<? startupscript ?>
    document
        .getElementById(<?js OutputId() ?> + "-btn1")
        .addEventListener("click", () => handler1());

    document
        .getElementById(<?js OutputId() ?> + "-btn2")
        .addEventListener("click", () => handler2());
<? end ?>

JavaScript in Href

Another case that is considered an inline script, and is mostly used in design templates, is the use of JavaScript in the href attribute of an <a> tag. This is often used in conjunction with an onclick event to prevent navigation, as shown below:

<a href="javascript:void(0)" onclick="navigateToHome()">Home</a>

This is an interesting scenario because using href styles the <a> element with a blue underline, similar to a normal link. If we remove the href attribute, this styling is lost. Depending on the situation, this may or may not be an issue. However, if we want to maintain the styling, what are our options?

We cannot simply set href="" because that will refresh the page, which is not our intention since we want to execute navigateToHome(). We could use href="#", but this would cause the viewport to scroll to the top, which may not always be desirable. What about using href="#0"? In this case, if no <a name="0"> exists, the viewport will not scroll. Unfortunately, the downside is that #0 will be appended to the URL in the address bar, resulting in a URL like https://website.com/#0.

Another approach is to handle this with styling. Since we need to change the way we handle the onclick event, we can implement it as follows:

<a id="home-button">Home</a>
<? startupscript ?>
    const linkEl = document.getElementById("home-button");
    linkEl.addEventListener("click", () => navigateToHome());
    linkEl.style.color = "linktext";
    linkEl.style.cursor = "pointer";
    linkEl.style.textDecoration = "underline linktext";
<? end ?>

Please select the option that best fits your situation.

Inline Styles

A similar restriction applies to inline styles. Styles defined within a style attribute are no longer permitted. Instead, styles should be added via an external CSS resource, a <style> block, or a trusted <script>.

For example, the style on this <div> will be ignored:

<div style="color: tomato">Text 1</div>
<div style="color: tomato">Text 2</div>

If you have access to the design template, you can create a new CSS selector and use that class name:

.color-tomato {
    color: tomato;
}

Then, in HTML:

<div class="color-tomato">Text 1</div>
<div class="color-tomato">Text 2</div>

If you are dealing with a static HTML element and there is no alternative to using the style attribute, we recommend adding a <style> block with a nonce, along with an id attribute using OutputId() like this:

<div id="<?attr OutputId() ?>-txt1">Text 1</div>
<div id="<?attr OutputId() ?>-txt2">Text 2</div>
<style nonce="<?attr CspNonce ?>">
    #<?= OutputId() ?>-txt1,
    #<?= OutputId() ?>-txt2 {
        color: tomato;
    }
</style>

Depending on the situation, you can also apply styles using JavaScript:

<div id="<?attr OutputId() ?>-txt1">Text 1</div>
<div id="<?attr OutputId() ?>-txt2">Text 2</div>
<? startupscript ?>
    document
        .getElementById(<?js OutputId() ?> + "-txt1")
        .style.color = "tomato";

    document
        .getElementById(<?js OutputId() ?> + "-txt2")
        .style.color = "tomato";
<? end ?>

A keen observer will notice that we used two buttons or divs as examples of how to handle these cases when we have multiple elements. If you only need to update the code for a single HTML element, you can simply use <?attr OutputId() ?> instead of an identifier like <?attr OutputId() ?>-btn1.

Script Evaluation

In JavaScript, script evaluation refers to the process of executing a string as JavaScript code. This practice poses significant security risks, especially when the string contains user input. For this reason, script evaluation is disallowed under CSP.

The following code examples are no longer permitted:

// The use of eval is evil.
const userInput = "console.log('Hi mom, I can hack!'); 2 + 2";
const result = eval(userInput);

// This will not function.
const add = new Function("a", "b", "return a + b");

// Using setTimeout() or setInterval() with a string as the first argument is not allowed.
setTimeout("console.log('Bad stuff');", 100);

By avoiding script evaluation and adhering to CSP guidelines, you can significantly enhance the security of your web applications.

Last updated

Was this helpful?