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.
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
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
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.
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.