Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to site defacement, to malware distribution.
- MDN

Blogthedata.com now has a Content Security Policy and an A+ rating on Mozilla Observatory. The journey began early this month when I got an F on Observatory’s security audit. Since then, I’ve beefed up security, including creating an SRI and a CSP. W3C published the latest CSP standard on June 29th, 2021.

In Django, we implement a CSP using the django-csp module. After installation, you add CSP directives to your settings.py file.

# settings.py
# Content Security Policy
CSP_DEFAULT_SRC = ("'none'",)
CSP_STYLE_SRC = ("'self'", "https://cdn.jsdelivr.net", "'unsafe-inline'")
CSP_SCRIPT_SRC = (
	"'self'",
	"https://cdn.jsdelivr.net",
)
CSP_IMG_SRC = ("'self'", "data:")
CSP_FONT_SRC = ("'self'",)
CSP_CONNECT_SRC = ("'self'",)
CSP_FRAME_SRC = ("*",)
CSP_FRAME_ANCESTORS = ("'none'",)
CSP_BASE_URI = ("'none'",)
CSP_FORM_ACTION = ("'self'", "https://blogthedata.us14.list-manage.com")
CSP_OBJECT_SRC = ("'none'",)

CSP_DEFAULT_SRC - The master directive. With a value of ‘none’ it’s saying ‘block everything unless it’s specifically allowed,’ It’s a good security practice because an allow list won’t block items you might have forgotten. Block everything by default and then selectively allow what you need.

CSP_STYLE_SRC - This tells Django where CSS may come from. In my case, I am allowing styles hosted on my server (self) and Bootstrap CSS, served through jsdeliver. I needed to include ‘unsafe-inline’ because a few plugins I use have inline styles. There’s an open issue to resolve this one.

CSP_SCRIPT_SRC - Same as STYLE_SRC, but this concerns what’s inside a <script> tag.

CSP_IMG_SRC - I host site images locally, so I don’t need to allow external sites like Imgur. The downside to this directive is that it prevents me from having any images on my site hosted on another domain. I also added data: to allow CKEditor’s drag/drop image support, which embeds the image as a base44 encoded string. I could opt for regular image uploads only, but I kept this in for convenience.

CSP_FONT_SRC - Where fonts can come from.

CSP_CONNECT_SRC - Restricts URLs that load using script interfaces such as WebSocket and XMLHttpRequests.

CSP_FRAME_SRC - I used  ‘ * ‘ to wildcard all domains in a child iframe. I am not restricting myself from putting something inside <iframe> when the iframe lives on blogthedata.com. This contrasts with SRC_FRAME_ANCESTORS, which dictates which domains can put blogthedata.com into an iframe.

CSP_BASE_URI - The <base> tag specifies the target of relative URLs in a site.

CSP_FORM_ACTION - Where <form> can submit. Most of my forms POST to routes within my application, except the Mailchimp newsletter sign-up which uses .us14.list-manage.com

CSP_OBJECT_SRC - Limts what sources for <object>, <embed>, and <applet> tags.

My Approach and Gotchas Encountered #

I first set CSP_DEFAULT_SRC to ‘none’ (block everything) and began visiting pages and running unit tests. My first issue was that CKEditor 4 bundles inline CSS with JavaScript in the ckeditor.js file. CSP policies restrict inline <style> and <script> tags. CKEditor improved CSP support in version 5, so I decided to migrate. You can read more about it in this blog post.

Social Share embedded Scripts and Styles#

Most social share buttons provided by companies such as LinkedIn and Twitter have embedded scripts and styles. I worked my way around that and wrote on it in this post.

Kofi Donate Button and Mailchimp Embed Form had inline styles and scripts#

I had to reverse engineer HTML templates, so Kofi and Mailchimp did not violate the CSP. This involved hosting the JavaScript locally and moving inline styles into a separate CSS file. The changes are in this PR.

Broken styling on the sitemap page#

Another interesting issue I ran into was that I broke inline styles on the sitemap page. I don’t think it’s a problem because this page doesn’t need styling. The only reason I have a sitemap page is for site spiders to understand my site better. I came across this Reddit thread that confirmed my thoughts. CSPs are for protecting users on your site, not robots.

Conclusion#

Implementing a CSP added XSS protection to blogthedata.com besides increasing the site’s score on Mozilla Observatory. I ran into many setbacks, but they were surmounted. Consider adding a CSP to your site to make it more secure.