Security Headers Best Practices

Mehdi Karimi, Ph. D.

Front-end web security, especially through mechanisms like Content Security Policy (CSP) and Permission Policy, plays a crucial role in safeguarding users from potentially harmful attacks, notably Cross-Site Scripting (XSS). CSP acts as a virtual bouncer, controlling what scripts and resources can be loaded and executed on a web page. By setting up a robust CSP, developers can specify trusted sources and prevent malicious scripts from running, significantly reducing the risk of XSS attacks. This not only protects users from potential data theft and manipulation but also helps in maintaining the integrity of the website.

The Permission Policy of a website serves as a crucial asset in the security toolkit, empowering developers to establish precise guidelines for particular browser features and APIs. Through the configuration of Permission Policy, developers gain the ability to regulate the accessibility of features on a web page, effectively reducing the potential attack surface. This proactive approach is instrumental in preventing unauthorized access to sensitive user information, solidifying its status as a vital component of comprehensive front-end security. Adhering to best practices, developers should disable any unnecessary features in their Permission Policy to prevent malicious actors from exploiting the site as a gateway to access user data.

While these security measures are highly effective, the unfortunate reality is that not enough developers utilize them. Implementing a Content Security Policy and Permission Policy is often more straightforward than people might think, and there are many resources available to guide developers through the process. The ease of setup makes it surprising that these security features aren't more widely adopted. Increasing awareness about the importance of front-end web security and providing accessible guides for implementation could encourage more developers to incorporate these measures, ultimately creating a safer online environment for users.

What is an XSS Attack

Cross-Site Scripting (XSS) is a type of security vulnerability that occurs when a web application allows an attacker to inject malicious scripts into web pages viewed by other users. These scripts are typically written in JavaScript, although other scripting languages can also be used.

XSS attacks are dangerous because they enable an attacker to execute scripts in the victim's browser, which can potentially steal sensitive data, such as login credentials, session cookies, or other personal information, manipulate web page content, or perform actions on behalf of the victim without their consent.

There are three main types of XSS attacks:

  1. Stored XSS (Persistent XSS): In this type of attack, the malicious script is permanently stored on the target server. It is then served to users when they visit a particular web page. For example, an attacker might inject a script into a forum post, and when other users view that post, the script is executed in their browsers.

  2. Reflected XSS: This type of XSS attack involves injecting a script into a URL or a web form. The injected script is reflected off a web server and executed in the victim's browser when they click on the malicious link or submit the form. The attacker often tricks the victim into clicking on the malicious link, which contains the script.

  3. DOM-Based XSS: DOM (Document Object Model) is a way to represent the structure of a web page. In DOM-based XSS, the malicious script manipulates the Document Object Model in the victim's browser, resulting in the execution of the script. This can happen without the script ever being sent to the server, making it harder to detect.

Some Real-world XSS Examples & Incidents

Cross-Site Scripting (XSS) vulnerabilities pose a threat to websites of all sizes, transcending the boundaries of large enterprises to affect smaller entities. Regardless of the scale, the ubiquity of web applications makes them susceptible to these attacks. It is crucial for both big and small organizations to remain vigilant in their cybersecurity efforts, understanding that a momentary lapse in security measures can lead to significant consequences. In the realm of cybersecurity, even small proactive measures can go a long way in averting the potential damage that a XSS attack can inflict, emphasizing that age-old adage that an ounce of prevention is indeed worth a pound of cure.

Sony PlayStation Network (2011): The PlayStation Network suffered a massive security breach caused, in part, by an XSS vulnerability. The breach exposed personal information, including credit card data, of millions of users. Sony incurred an estimated cost of $171 million to rectify the breach, compensate users, and strengthen security measures.

Microsoft Teams (2021): A researcher identified a stored XSS vulnerability in Microsoft Teams that could allow attackers to send a malicious message that, when clicked, could lead to various malicious actions such as stealing tokens.

WordPress (2021): Due to its widespread use, WordPress frequently comes under scrutiny for vulnerabilities. In 2021, several plugins and themes had XSS vulnerabilities reported. For instance, a popular plugin named "Elementor" had an XSS vulnerability that potentially impacted millions of websites.

Google (2021): Google’s Vulnerability Reward Program often finds and fixes XSS vulnerabilities in various Google services. In 2021, several XSS vulnerabilities were reported and fixed.

SharePoint (2021): Microsoft's SharePoint saw patches for various vulnerabilities, including some XSS issues.

Ways to Keep Your Site & Users Safe

To prevent XSS attacks, developers should follow best practices such as input validation, output encoding, and using security mechanisms like Content Security Policy (CSP) and a Permissions Policy. Users should also be cautious about clicking on unfamiliar links and should keep their web browsers and software up to date, as security patches may address known vulnerabilities.

What is a Content Security Policy

A Content Security Policy (CSP) is a security feature implemented by web developers to mitigate the risk of Cross-Site Scripting (XSS) attacks and data injection attacks on websites. It is a security header that can be added to a web page's HTTP response to specify which content sources are considered trusted or allowed to be loaded and executed by the browser. CSP helps in reducing the risk of executing malicious scripts on a web page, even if an attacker manages to inject code.

Key features and directives of a Content Security Policy include:

  1. Content Sources: You can specify which sources of content are trusted. For example, you can allow content to be loaded only from the same domain, specific subdomains, or completely disallow any external content.

  2. Script Sources: You can define where scripts can be loaded from. This includes JavaScript files, inline scripts, and event handlers. By limiting the sources, you can prevent the execution of malicious scripts injected by attackers.

  3. Style Sources: You can specify where styles (CSS) can be loaded from. This helps in preventing the injection of malicious styles that can alter the appearance of a web page.

  4. Connect Sources: You can control which domains can be contacted using technologies like XMLHttpRequest, fetch, and WebSocket. This helps in preventing data exfiltration or unauthorized data requests.

  5. Plugin Types: You can control which types of plugins (like Flash or Java applets) are allowed on your site.

  6. Default Sources: You can set a default source for elements that don't have a specific source defined. This helps in defining a base level of security.

Here's an example of a simple Content Security Policy header:

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

In this example, it allows content to be loaded only from the same domain. You can customize this policy to meet your website's requirements and security needs.

Here’s another example of a Content Security Policy that we use on the dotCMS demo site:

default-src 'none' ;

    base-uri 'self' ;

    connect-src 'self' ;

    font-src 'self' ;

    form-action 'self' ;

    frame-src 'self' ;

    img-src 'self' data: ;

    script-src 'self' ;

CSP headers are a valuable security measure as they help protect against various web security vulnerabilities, such as XSS attacks, data injection attacks, and clickjacking. However, they need to be carefully configured to strike a balance between security and functionality, as overly restrictive policies can break the normal operation of a website. It's important for web developers to thoroughly test and fine-tune their CSP to ensure it provides the desired level of protection without causing issues for legitimate users.

What is a Permissions Policy

Formerly known as Feature Policy, Permissions Policy empowers developers to regulate the browser features accessible to a page, its iframes, and subresources by specifying a set of policies for the browser to enforce. These policies are applied to origins listed in a response header origin list, which can consist of same-origins and/or cross-origins. This provides developers with the ability to govern first-party and third-party access to browser features. Essentially, Permissions Policy serves as a tool to restrict access to browser features, allowing developers to assert, "I don't anticipate my website needing access to your camera or geolocation, so any requests for such access are not part of the code I authored or approved!"

Scanning Tools for Testing Security Headers

Security Header scanning tools are simple to use. Simply paste in your site’s URL and you’re set! The scans will give you a simplified letter grade indicating security health, and also provide you with information on why each scan is important, and how to improve your site’s score.

Tools to Check Your Site's CSP

Note: Mozilla Observatory & Security Headers are Now Included Inside of dotCMS

And now there’s an even easier way to access these tools from inside dotCMS’s Content Preview, under the “Page Tools” menu. With just one click, you can run Mozilla Observatory or Security Headers scans of your site!


How to Configure Content Security Policies & Permissions Policies Inside of dotCMS

How We Improved Our Scores

These images represent where we started, vs. where we ended up. If you follow the steps in the next section of this document, you can get there too, and it will likely only take you about 10 minutes or so to complete!


Before You Get Started

Note: For all of these Rules, it’s good to Evaluate “Every Page” and NOT “Every Request” or any of the other options.


Content Security Policy

Imagine your website is the hottest new night club, and there’s a line around the block to get in. To make sure you can maintain the vibe that’s drawing all these guests, and that all your guests continue to have a great time, you can’t just let anyone in! Guests need to be of age, dressed to the nines, and you need to weed out the troublemakers.

A Content Security Policy (CSP)  is like a set of rules given to the bouncers. These rules specify who's allowed to enter (trusted sources). For instance, if a person tries to bring in something suspicious, like a backpack full of firecrackers, or a harmful script (malicious code), the bouncer can just point to the CSP rules and stop them at the entrance. The CSP is the club's way of saying, "Hey, only let in the guests who we trust to follow the dress code, and who we know won’t cause trouble inside."

Configure Your Content Security Policy

Step 0

Install one of the browser extensions for generating a Content Security Policy. Play around, but for the purpose of this document I’m using “Laboratory” by April King.

NOTE: If your site isn’t live yet you can use this tool.

Step 1

NOTE: For best results, you’ll want to disable all ad blockers, and make sure you aren’t behind any sort of restrictive firewall while you are completing this step.

Once the plug-in is installed, navigate to your site, hit “Record” – what this does is create a “whitelist” for approved domains you want to allow to load content on your site. 


Step 2

While recording, navigate across your site, ensuring that you click on every page type and load at least one page with every unique feature – such as pages incorporating Google Maps or Hubspot Forms. This step should go pretty fast, and you’ll be left with a raw Content Security Policy that looks something like this.

default-src 'none'; font-src; img-src 'self' data:

Step 3

If your site uses inline styles and scripts, you’ll likely need to fix those in order to set up a meaningful Content Security Policy.


You may need a developer to help complete this step. It’s never best practice to load CSS and JavaScript in-line.

Step 4

In dotCMS, navigate to the Rules section. It’s currently under Marketing > Rules…


… and Create an entry for “Content-Security-Policy”


Step 5

Paste in the values from Step 3, turn the Rule on, and you’re done!

Learn More about Content Security Policies

Permissions Policy

Following the nightclub example from above, a Permission Policy is like the house rules. Guests are allowed to come in, but they can’t go behind the bar and help themselves to drinks, and they can’t plug their own phones into the sound system, or harass other guests. The bouncers have a list of behaviors that are allowed, and they are empowered to stop you from crossing the line and ruining anyone else’s night. If the Content Security Policy is rules for who gets let into the club, the Permission Policy is just rules for what they are allowed to do in the club.

Step 1

Create a simple Permissions Policy; here’s a good tool you can use to get a head start.

If you just want to load from your own site, it can be as simple as this:

accelerometer=(self), ambient-light-sensor=(self), autoplay=(self), battery=(self), camera=(self), cross-origin-isolated=(self), display-capture=(self), document-domain=(self), encrypted-media=(self), execution-while-not-rendered=(self), execution-while-out-of-viewport=(self), fullscreen=(self), geolocation=(self), gyroscope=(self), keyboard-map=(self), magnetometer=(self), microphone=(self), midi=(self), navigation-override=(self), payment=(self), picture-in-picture=(self), publickey-credentials-get=(self), screen-wake-lock=(self), sync-xhr=(self), usb=(self), web-share=(self), xr-spatial-tracking=(self)

Step 2

In the Rules section, create an entry for “Permissions-Policy” and fill in the values from above.


Learn More About Permissions Policy

Referrer Policy

Imagine you're on the internet, clicking from one page to another. Now, every time you go from one page to another, your browser shares information about where you came from. This information is in something called the "Referer Header," and it usually includes the URL of the page you were on before. The Referrer Policy is like a set of rules that website owners can use to control how much of this information gets shared. So, it's like having the power to decide whether the browser tells the new page where you came from and, if it does, how much detail it shares. It gives website owners control over this sharing process.

There are several values that can be set for the Referrer Policy, including:

  • no-referrer : The Referer header will not be sent.

  • no-referrer-when-downgrade : The Referer header will be sent to the same-origin, but it won't be sent to a less secure destination (e.g., from HTTPS to HTTP).

  • origin : Only send the origin (the scheme, host, and port) of the referring page.

  • origin-when-cross-origin : Send the full URL for same-origin requests but only send the origin for cross-origin requests.

  • same-origin : The full URL will be sent for same-origin requests, but for cross-origin requests, only the origin will be sent.

  • strict-origin : Send the origin of the referring page for both same-origin and cross-origin requests.

  • strict-origin-when-cross-origin : Send the full URL for same-origin requests and the origin for cross-origin requests. (This is the setting that we find works best for Marketing Sites.)

  • unsafe-url : The full URL will be sent for same-origin and cross-origin requests.

Website owners can choose the appropriate Referrer Policy based on their privacy and security requirements. This header helps in controlling the amount of information shared with external websites when a user clicks on a link or loads a resource from a different origin.

Configure Your Referrer Policy

Step 1

In the Rules section, create an entry for “Referrer Policy” and we recommend setting the value to “strict-origin-when-cross-origin” and you’re done!


Learn More About Referrer Policy

Strict Transport Security Policy

The Strict Transport Security Policy is like a time limit for how long your rules around trust last. Think of it like having a rule for your dog, you establish trust and look him in the eye, “No jumping on people!” But, as soon as you leave the room, your poorly trained and excited dog starts jumping on people again. So you have to keep resetting and enforcing  that rule each time you come back into the room. However, by setting the Strict Transport Security Policy to last for a really, really long time, your rules don’t get challenged again until they expire. So your dog no longer jumps on people, or looks to someone else to see if it’s OK to jump. This longer "max-age" makes sure your rules are honored, and your dog won’t accidentally learn bad behavior by following the instructions of the next person who walks into the room.

Configure Your Strict Transport Security Policy

Step 1

In the Rules section, create an entry for “Strict Transport Security Policy” and we recommend setting the value to “max-age=63072000; includeSubDomains; preload” and you’re done!


Learn More About Strict Transport Security Policy

Image Credit: FLY:D
Mehdi Karimi, Ph. D.
Director of Cyber Security
May 01, 2024

Recommended Reading

Mastering the New Universal Visual Editor in dotCMS: A Technical Deep Dive for Developers

Explore dotCMS's Universal Visual Editor, merging WYSIWYG simplicity with headless CMS flexibility. This tool offers drag-and-drop editing, inline content editing, and NoCode tooling for seamless omni...

Benefits of a Multi-Tenant CMS and Why Global Brands Need to Consolidate

Maintaining or achieving a global presence requires effective use of resources, time and money. Single-tenant CMS solutions were once the go-to choices for enterprises to reach out to different market...

Headless CMS vs Hybrid CMS: How dotCMS Goes Beyond Headless

What’s the difference between a headless CMS and a hybrid CMS, and which one is best suited for an enterprise?