Have you ever heard of Content-Security-Policy? If not, then we’re sure that you will find this post extremely informative and interesting…
In short, Content-Security-Policy is a policy set in the httpd.conf file, the .htaccess file, or as a meta tag (in the HTML code) that is typically used to prevent the website from running external scripts. In other words, it is excellent for XSS protection.
Let us explain a bit more…
You see, when someone visits a normal Joomla website, that has no Content-Security-Policy set, the browser loads all the images and scripts that are on the visited page, no questions asked. That’s what everyone wants right? Well, not really.
What every website administrator wants is to have the browser load all the images and the scripts from legitimate, approved sources, and not from just any source. What if, for example, the Joomla website that you are running has an XSS vulnerability (through a 3rd party extension) and was exploited by an attacker to request a hacked JavaScript file from a malicious source? If all the scripts are allowed to be executed, then an innocent end user will end up being redirected to a very bad website while potentially getting his PC infected with malware in the process. Of course, one could argue that the originating website is technically clean (e.g. the filesystem is clean), since all the malicious code is elsewhere, but that argument will not hold, simply because the end user only ended up on the bad website and with a virus on his computer because of the XSS exploited Joomla website. Additionally, not a single (respectable) online scanning tool and not a single search engine will think that it’s not the originating website’s fault.
This is where Content-Security-Policy comes to the rescue. When you have a policy for only allowing specific content from specific, trusted sources on your website, then you have come a long way in protecting and securing your website.
For example, adding the following line to your .htaccess file ensures that the only JavaScript and CSS files that are allowed to be downloaded from your website are those that actually exist on your website:
Header set Content-Security-Policy "default-src 'self'"
(Note: The location of the above line can be anywhere in the .htaccess file, but we like to add these things in the very beginning.)
Note that the above will not even allow inline scripts. For example, if you have some inline JavaScript code in your HTML, then that HTML code will be blocked, and you will see the following in Google Chrome‘s console:
Refused to execute inline script because it violates the following Content Security Policy directive: “default-src ‘self'”. Either the ‘unsafe-inline’ keyword, a hash (‘sha256-FU2Yz9Y7Q/i92m6ZTOAqpzhUeVAiTp1am3CtdegsQXs=’), or a nonce (‘nonce-…’) is required to enable inline execution. Note also that ‘script-src’ was not explicitly set, so ‘default-src’ is used as a fallback.
It will also will not allow inline CSS styles, and you will see the following message in Chrome‘s console if you have an inline CSS style:
Refused to apply inline style because it violates the following Content Security Policy directive: “default-src ‘self'”. Either the ‘unsafe-inline’ keyword, a hash (‘sha256-HhdiwPT6NvHjIA9I6BIJ1crwfwF/RLkZ6B/Ne8+ViGY=’), or a nonce (‘nonce-…’) is required to enable inline execution. Note also that ‘style-src’ was not explicitly set, so ‘default-src’ is used as a fallback.
Now we understand that the absolute majority of websites (including Google, Facebook, and Twitter) have inline CSS and inline JavaScript, so why is it blocked by default when Content-Security-Policy is enabled? Well, because it is unsafe. See, those large websites (Google et al) have many layers of protection and are not really worried of XSS attacks. But smaller, less secure websites which are managed by people who don’t know much about security and who use extensions written by complete strangers (who may or may not care about security) should worry about XSS, and should ensure that no scripts and styles exist in the HTML code.
But what about those websites that are sure of their security and that need to run inline scripts?
Well, if you’re sure about your website’s security and protection and you need to run inline scripts and inline styles, then change the above line to the following:
Header set Content-Security-Policy "default-src 'self' 'unsafe-inline'"
But what if you want to use scripts/CSS from other trusted domains such as externaltrusteddomain.com and you want inline scripts (but not inline CSS), for example. Well, you switch the above line with the one below:
Header set Content-Security-Policy "default-src 'self'; script-src 'self' http://*.externaltrusteddomain.com https://*.externaltrusteddomain.com 'unsafe-inline'"
What if you want to enforce the Content-Security-Policy server wide?
If you really want to do that, then all you need is to place the .htaccess line of your choice above in a VirtualHost tag of your httpd.conf file.
How can Content-Security-Policy be defined in a meta tag?
If you don’t have access to the .htaccess file, you can just modify the main index.php of your Joomla template and add the following meta tag (just after the opening head tag) to simulate the security policy of our last example above:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' http://*.externaltrusteddomain.com https://*.externaltrusteddomain.com 'unsafe-inline'">
Which method is best for defining Content-Security-Policy?
We recommend you use the .htaccess file to store the content security policies, simply because it’s easy, it’s versatile, it’s clear, it’s part of the website (unlike the httpd.conf file which is part of the server, which means that migrating to a different server will cause you to lose these policies), and it is independent of the template (the meta tag option is template dependent, unless you use an extension to add it).
Is it a good idea to implement Content-Security-Policy directly on a production website?
Not really – Content-Security-Policy is a two edged sword. It will protect your website, but if you make a mistake in the rule, then your website will not function properly. And, because of its very confusing nature, it is very easy to make a mistake when writing such a rule (you may miss an external library, or an internal script, or an external font, etc…).
What we do is that we use the Content-Security-Policy-Report-Only directive instead of using Content-Security-Policy for testing prior to rule enforcement. The Content-Security-Policy-Report-Only is a directive that tells the system to silently send anything that doesn’t match the security policy to the URL of your choice as json encoded data in the body of the headers. The website will not be affected whatsoever when using this directive – with the exception that the browser’s console will display all policy violations.
For example, if you want to log everything that violates our last policy above, then you can add this line to your .htaccess file:
Header set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' http://*.externaltrusteddomain.com https://*.externaltrusteddomain.com 'unsafe-inline'; report-uri http://www.[yourjoomlawebsite].com/parse-content-security-policy-report.php"
Note that the only thing that we did was that we switched Content-Security-Policy with Content-Security-Policy-Report-Only and we added the URL that all policy violations should be sent to.
The parse-content-security-policy-report.php PHP file has one objective: to store the received data in the content-security-policy-report.json file. It should consist of the following PHP code:
$contentSecurityPolicyReportJSon = file_get_contents('php://input'); file_put_contents ('/home/[cpanel-user]/public_html/content-security-policy-report.json', $contentSecurityPolicyReportJSon, FILE_APPEND);
The saved json file can then be parsed using the json_decode PHP function. Keep in mind, that on high traffic websites, the json file can become very large very quickly. So, you should enable and disable this feature very quickly.
We hope that you found this post useful for protecting your Joomla website against XSS attacks. If you need help with the implementation, then you can always contact us. We’re always eager to help, we love our customers, and our fees are super duper affordable!