Have you ever heard of cross-site scripting (XSS)? This is when unauthorized users take advantage of security gaps in internet browsers and on web servers to plant malware and run it anonymously. But what lies behind these attacks and how can website operators and users protect themselves?
Content Security Policy: more security with web content
Development of the Content Security Policy
The Content Security Policy dates back to 2004 when it was still known as 'Content Restriction'. The reason for this effort was an increasing number of vulnerabilities in internet scripts. Cross-site scripting (XSS) is a criminal method of infiltrating malicious code into a website, and poses a major risk for users. Users access a trustworthy website, but there is a script running that loads malicious data from an external source. Cyber criminals often make use of security vulnerabilities in the website’s commentary functions. By doing this, they can gain access to personal computers without internet users knowing it. Website operators won’t even notice that strange code has been introduced.
To solve this problem, the Mozilla Foundation promoted the development of the CSP. The advantage behind the security standard is that rules can be set up in the browser to tell the software which scripts it is allowed to load and which it isn’t. To do this, the Content Security Policy uses HTTP headers.
The Mozilla Foundation is behind the development of the Firefox browser. The non-profit organization is responsible for the orientation of Mozilla’s projects and innovations to do with the internet. The Foundation was founded by former Netscape employees, who developed one of the first web browsers.
How does the Content Security Policy work?
When communicating online, clients and server exchange data via the Hypertext Transfer Protocol (HTTP). HTTP header fields are an important part of the requests and responses: parameters and arguments are transferred in these, which are important for exchanging information between the two call participants (server and client). They are generally divided into inquiry and response fields, and can contain information such as character set, language, and cookies. CSP is implemented as a response header field. This means that the server delivers the information and the browser processes it.
The Content Security Policy header is created by the website operator and inserted on every subpage of the website where you want the security standard to be applied. As the operator of the website, you have the possibility of defining different security precautions for each individual page. You can implement the security concept more easily by creating .htaccess files, which are located in the same (or hierarchically higher) folders as the corresponding web pages, or by embedding CSP directly into the server configuration.
If you want to check whether your browser supports the Content Security Policy, you can use the CSP browser test. This test attempts to load scripts and images from unknown (harmless) sources and tells you if this was successful.
In terms of the Content Security Policy and preventing cross-site scripting, website operators should store all scripts in separate files instead of embedding them directly in the website’s source code. The CSP then works in a similar way to a whitelist: in the header, the sources, from which scripts and data can be loaded, are named. If an unknown script has slid undetected into the HTML code of the page and is now trying to load external data, the user’s browser needs to prohibit this. By default, the CSP blocks all scripts that can be found directly in the code (inline scripts). This protects both the website and the internet user as well as any sensitive data.
Cross-site scripting is relatively easy for cyber criminals to manipulate. Almost every website has an input field i.e. comment function, search bar, or log-in field. Instead of simple text, it’s possible to also enter scripts. If the server is not adequately secured, criminals can implement phishing interfaces, bringing the entire website to a standstill or gaining control of the user’s web browser using malicious software. CSP (or to be more precise: the corresponding header field) tells the web browser which sources it is permitted to load data from. If the policy is implemented in the website’s code, attempting to retrieve XSS-implemented code will be met with an error message.
The Content Security Policy also enables website operators to change many other settings. This can be done through these directives:
- base-uri: Restricts the URLs, which can be used in a website’s <base> element
- child-src: Defines valid sources for web workers and nested browsing contexts loaded using elements such as <frame> and <iframe>
- connect-src: Limits the sources that the site can connect to i.e. links
- font-src: Defines valid font sources
- form-action: Defines valid sources that can be used as an HTML <form> action
- frame-ancestors: Defines valid sources for embedding the resource using <frame> and <iframes>
- img-src: Defines valid images sources
- media-src: Defines valid audio and video sources
- object-src: Defines valid plugin sources
- plugin-types: Defines valid plugins types
- report-uri: Lets the browser know when a URL (to which reports are sent) violates security measures
- style-src: Defines valid stylesheet sources
- upgrade-insecure-requests: Specifies that unsafe HTTP sites should be treated like HTTPS sites
- sandbox: Moves the site in question to a sandbox where forms, pop-ups, and scripts are forbidden
These directives only apply if they are explicitly set. Otherwise, they are open by default and therefore pose a security risk. However, this can be changed using default-src: you can set the default state of all directives ending with -src. For example, instead of leaving it open, you can specify that only data from your own website may be loaded unless you have specified otherwise in the HTTP header of the individual web page. In separate directives, you add additional sources.
You can enter any number of directives into the header field. If you want to include several directives, separate them with semicolons. In addition, you as the website operator must specify all sources within a directive. Multiple entries of the same directive with additional sources are not permitted (such as in the following example):
script-src example1.local; script-src example2.local
In this case, only the first source is relevant, the second one is ignored by the client. Instead, you must record both sources in one directive:
script-src example1.local example2.local
If you do not need certain types of content for a page or the entire website, you can enter the value 'none' in the header – so you can specify that no sources may be loaded at all. You can also use the value 'self' – this means that the browser can only load content from the same source. Both values must always be quoted in single quotation marks, otherwise none and self will be interpreted as domains.
There are different header options for defining a Content Security Policy:
Not all browsers support every term. However, the W3C (the body responsible for defining web standards) suggests Content Security Policy. Therefore, all modern browsers have adapted to this security standard in the meantime (the other two versions are considered obsolete). To make sure that you reach as many internet users as possible (even those with out-of-date browsers) with your CSP, it is advisable to include all header fields. If a corresponding web browser can’t handle the Content Security Policy header, it will simply ignore it and display the website without any problems – however, additional protection isn’t given to the affected users.
Usually, you will set HTTP headers across your entire domain. For sub-directories, you can use the .htaccess file. You then use the CSP security standard to set special rules for individual subpages. For example, if you have implemented a social media button on one page, but not on the next one, it makes sense to only allow access to the external source on the first page.
Sources can be entered as addresses, in their own way or as wildcards. The following entries are therefore allowed:
- script-src example.com:443 – scripts are only allowed from this domain via HTTPS
- script-src 'none' – scripts aren’t allowed to be loaded
- script-src 'self' – scripts may be loaded from the same source as the current page, but not from subdomains
- script-src https: – scripts can be loaded from any domain as long as it starts with HTTPS
- script-src example.com – scripts may be loaded from this domain
- script-src *.example.com – scripts from this domain and all subdomains are allowed
- img-src data: – images can be loaded via data URLs
you can secure unsafe-inline using a detour. Hash values or nonce source close this vulnerability as much as possible.
If scripts are no longer allowed to appear directly in the code, you must create a separate file for each script. The script’s function is stored in a .js File. The website’s code only refers to these:
What the script does in the end is described in the example.js. You must also store style elements in separate stylesheets. If you want to implement an internet security policy as a website operator, it is not enough to simply insert the header. You also need to check and customize your website’s source code.
do you want to know how secure your own website is? Mozilla has provided an easy-to-use test: Observatory by Mozilla scans your website and gives you a grade after it’s completed, and tells you how to make your site safer
Content Security Policy: an example
As an example, we now show you how to implement a Content Security Policy header and explain what can be achieved with it.
- Content-Security-Policy: "default-src 'none'; script-src 'self' *.example.com; style-src 'self'; img-src 'self' data:; font-src 'self' fonts.google.com/; report-uri example.org/report.html"
- X-Content-Security-Policy: "default-src 'none'; script-src 'self' *.example.com; style-src 'self'; img-src 'self' data:; font-src 'self' fonts.google.com/; report-uri example.org/report.html"
- X-WebKit-CSP: "default-src 'none'; script-src 'self' *.example.com; style-src 'self'; img-src 'self' data:; font-src 'self' fonts.google.com/; report-uri example.org"
You can see that every CSP variant appears in the header so that as many different browsers as possible can be addressed. Inside each header name, the content is identical: sources are listed one after the other, and the directives are separated by a semicolon. The syntax is always the same. In fact, only the name of the field changes, so you can easily duplicate the content.
First, we determine that, unless otherwise specified in a directive, data should not be loaded from just any source (default-src). This makes things a little bit safer. You should always define default-src first. This will prevent a potentially forgotten directive from leaving a gap in your Content Security Policy.
Next, we define the source from which scripts can be loaded (script-src). In the example, we determine that the browser only loads scripts from the same source and from example.com including all subdomains (you assign the wildcard using *). Furthermore, we determine that clients are only allowed to load stylesheets from their own sources (style-src). Images are only allowed from their own source as a data URL (img-src). According to our Content Security Policy header, fonts may only be downloaded from Google’s own sources. In the example, we specify a location to which notifications are sent if someone attempts to violate the security standard (report-uri).
You might have noticed that we haven’t included all the directives in the header. This doesn’t cause any problems: In this case, we don’t need any more whitelists, and the default src turns off all other sources.