Developer Blog

Content Security Policy

How to manage and edit the Content Security Policy with the Sharetribe Web Template.

Jan 26, 2024

A person in a brown sweater using their laptop.

Content Security Policy is an HTTP header instructing the browser which web resources it can access. CSP is an important addition to the security of your site, and in particular, it helps mitigate cross-site scripting (XSS) attacks. 

The Sharetribe Web Template enforces CSP by default to mitigate malicious attacks against your users. However, if you need to load resources via a third party, which is often the case when implementing analytics libraries or chat widgets, you need to extend the CSP by safelisting the sources the external resources are loaded through.

What is Content Security Policy?

CSP is a web security standard designed to prevent various types of attacks, primarily focusing on mitigating Cross-Site Scripting (XSS) vulnerabilities. CSP allows you to define a set of policies that instruct the browser which resources are allowed to be loaded on a particular web page.

CSP is implemented through an HTTP header which includes a set of directives that define the security policies for the page. These directives specify allowed sources for different types of content, such as scripts, stylesheets and images.

How does CSP prevent XSS attacks?

Cross-site scripting (XSS) attacks involve injecting harmful client-side code into a website in order to capture user data or leverage it as a means to propagate malicious activities. The vulnerability is born from the trust browsers place in all requests originating from your website. Therefore, if a malicious actor is able to inject malicious code into your site, they can exploit this vulnerability.

Sites that allow users to interact and create content are particularly vulnerable, as this creates a potential route for a malicious actor to inject code into the site. For example, an attacker may post a comment with a payload that includes a malicious script. Vulnerability exploits like this are prevented by validating all user input and by enforcing a Content Security Policy. The Sharetribe Web Template and our APIs validate all data and the Web Template comes in with in-built CSP handling.

The CSP policy in the Web Template

The Sharetribe Web Template uses Helmet’s Content Security Policy middleware. You can discover the full CSP configuration in the server/csp.js file.

The template uses multiple third-party resources, such as Stripe and Google Analytics. By default, all these services are safelisted in the CSP policy.

The following resources are safelisted by default:

APIs and External Resources:

  • Sharetribe’s asset delivery API (*.st-api.com)
  • Google Maps (maps.googleapis.com)
  • Mapbox (*.tiles.mapbox.com, api.mapbox.com, events.mapbox.com)
  • Google Analytics (www.googletagmanager.com, *.google-analytics.com, stats.g.doubleclick.net)
  • Plausible Analytics (plausible.io, *.plausible.io)
  • Google Fonts (fonts.googleapis.com)
  • Sentry (sentry.io, *.sentry.io)
  • Stripe (*.stripe.com)

Fonts:

  • Sharetribe fonts (assets-sharetribecom.sharetribe.com)
  • Google Fonts (fonts.gstatic.com)

Frames and Embedding:

  • Stripe (*.stripe.com)
  • YouTube (*.youtube-nocookie.com)

Images:

  • Imgix (*.imgix.net, sharetribe.imgix.net)
  • Picsum Photos (picsum.photos, *.picsum.photos)
  • Mapbox and Google Maps images (api.mapbox.com, maps.googleapis.com, *.gstatic.com, *.googleapis.com, *.ggpht.com)
  • Google Images (www.google.com, *.google-analytics.com, stats.g.doubleclick.net)
  • YouTube static images (*.ytimg.com)
  • Stripe (*.stripe.com)

Scripts:

  • Google Maps and Mapbox scripts (maps.googleapis.com, api.mapbox.com)
  • Google Tag Manager (www.googletagmanager.com)
  • Google Analytics (*.google-analytics.com)
  • Stripe (js.stripe.com)
  • Plausible Analytics (plausible.io)

Stylesheets:

  • Google Fonts and Mapbox styles (fonts.googleapis.com, api.mapbox.com)

Enabling CSP

You can run the CSP in two different modes or deactivate it altogether. However, you should never turn off the CSP in your Live marketplace, as it's an important security feature. You can run the CSP with two active modes: "report" and "block". The report mode prints out CSP violations in the developer tools Console and your server logs without blocking the actual requests. The block mode should always used in your Live environment – it blocks all requests that violate the CSP.

The CSP can be enabled and disabled using the REACT_APP_CSP environment variable. As mentioned above, it accepts two values:

REACT_APP_CSP=report
REACT_APP_CSP=block

If no value is set in the environment variables, the CSP is deactivated.

You need to be running your web application using the "yarn run dev-server" command in order to test the CSP. When using the "yarn run dev" command, CSP is not enforced, regardless of what you have in your environment variables.

When using REACT_APP_CSP=report, you’ll see the following error in your browser’s developer tools console tab and your server logs (note the "[Report Only]" tag in the upper left-hand corner).

A screenshot showing a warning message from the developer tools console in Chrome.

When using REACT_APP_CSP=block, the script will actually be blocked for violating the CSP.

A screenshot showing an error message from the developer tools console in Chrome.

How to extend the policy?

You will always need to extend the policy if you want to load external resources. This applies not only for external scripts, but also for images, fonts and stylesheets. For example, we recently introduced support for adding animated GIF in Markdown from Giphy via Pages. To do this, we extended the policy to allow loading images from Giphy. 

Adding the following code to a Block in Pages previously resulted in the image being blocked due to the img-src directive:

![Animated GIF](https://media3.giphy.com/media/.../giphy.gif)
A screenshot showing a warning message from the developer tools console in Chrome.

To allow the browser to load the image, we added '*.giphy.com' to the imgSrc directive. However, when extending the policies yourself, we recommend adding them to the customDirectives object. This makes it easier for you to pull in new changes from the remote repository if we add new exceptions in the future.

const { imgSrc = [self] } = defaultDirectives;
const customImgSrc = imgSrc.concat('your-custom-domain.example.com');

 const customDirectives = {
   imgSrc: customImgSrc,
 };

An easy way to identify missing sources in your CSP is to use the REACT_APP_CSP=report configuration during development, and always test your web application using yarn run dev-server. This will reveal any policy violations and you can then add the missing sources to the csp.js file. 

Any custom directives should be written to the end of the csp.js file, into the customDirectives object. Avoid overwriting the existing policies, as it makes it harder to pull new changes from the web-template repository if new directives are introduced in updates.

Allowing your site to be embedded as an iFrame on other sites

Content Security Policy also prevents your website from being embedded on other sites. This prevents clickjacking attacks, where the user is tricked into clicking on something different from what they perceive, potentially leading to unintended actions or disclosure of sensitive information. The frame-ancestors directive allows you to specify what parent source may embed your web page. Note that this differs from the frame-src directive, which specifies the domains from where iframes can be loaded from onto your page.

Here’s an example of how to use the frame-ancestors directive:

const customDirectives = {
   'frame-ancestors': [self, '*.example.com'],
};

This directive would allow for your site to be embedded at www.example.com

The web template uses Helmet’s Content Security Policy middleware which by default sets the x-frame-options header as SAMEORIGIN. This prevents your website from being embedded anywhere else but within itself. In addition to the frame-ancestors directive, you need to disable the x-frame-options header by setting xFrameOptions to false in server/index.js:

app.use(
 helmet({
   contentSecurityPolicy: false,
   xFrameOptions: false,
 })
);

Note that when embedding third party sites on your webpage, you may encounter issues if the embedded site has the x-frame-origin header set to SAMEORIGIN or if the site’s CSP blocks embedding the site.

Photo by Christin Hume on Unsplash.