Introduction
It was at TYPO3camp Stuttgart 2016 that Jochen Weiland held a comprehensive session on TYPO3 Security. During the talk he showed how you can improve the security of a TYPO3 website with a single line of Typoscript: it was a series of HTTP response headers, divided by pipe symbols, stored in config.additionalHeaders.
Today this no longer works for the two following reasons, which is why I do not show the old TypoScript:
- config.additionalHeaders was changed to a numeric array in TYPO3 7.6. Dividing multiple headers with a pipe symbol is no longer possible.
- Existing HTTP headers are changed, new headers are introduced.
Table of contents
- Define HTTP headers in TYPO3
- Explanation of the HTTP Security Headers
- HTTP Strict Transport Security (HSTS)
- X-Content-Type-Options
- X-Powered-By
- Referrer Policy
- Permissions-Policy (previously named Feature-Policy)
- Content-Security-Policy (CSP)
- Weren't there more Security Headers?
- X-Frame-Options
- X-XSS-Protection
- Analysis software for HTTP headers (and more)
- Sources
Define HTTP headers in TYPO3
First of all, here's the TypoScript configuration with the currently recommended HTTP Security Headers.
Please don't just copy the following lines. Keep reading so you understand the mechanisms behind them.
config {
additionalHeaders.10.header = Strict-Transport-Security: max-age=31536000; includeSubDomains
additionalHeaders.20.header = X-Content-Type-Options: nosniff
additionalHeaders.30.header = X-Powered-By: nothing
additionalHeaders.40.header = Referrer-Policy: strict-origin-when-cross-origin
# additionalHeaders.50.header = Permissions-Policy: geolocation=(), midi=(), camera=(), usb=(), magnetometer=(), accelerometer=(), vr=(), speaker=(), ambient-light-sensor=(), gyroscope=(), microphone=()
# additionalHeaders.51.header = Feature-Policy: geolocation 'none'; midi 'none'; camera 'none'; usb 'none'; magnetometer 'none'; accelerometer 'none'; vr 'none'; speaker 'none'; ambient-light-sensor 'none'; gyroscope 'none'; microphone 'none'
// The following header will need our full attention at a later time:
additionalHeaders.60.header = Content-Security-Policy: frame-ancestors 'self'
}
You can analyse and evaluate the HTTP headers of your website with a testing tool like https://securityheaders.com. If you use all of the above additionalHeaders, you've already created a good foundation.
At the moment, the tool will even award you with an A+ grade. However, I consider this a small bug: the existing Content Security Policy is included in the calculation, but the relevant directives to protect from Cross-site scripting are still missing.
We will have to extend this last header for this purpose (and will do so in the second part of this tutorial).
You will find more analysis software at the end of this tutorial!
Of course TypoScript is not the only way to set HTTP headers.
Some HTTP headers are already contained in the default .htaccess file of TYPO3. This includes the Security Header X-Content-Type-Options (needs the Apache module mod_headers):
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
</IfModule>
You can also add HTTP headers directly in PHP:
header('X-Content-Type-Options: nosniff');
You should avoid duplicate headers. Don't set headers in both .htaccess and TypoScript.
Explanation of the HTTP Security Headers
Below you will find an overview of advantages and configuration of these headers. If a HTTP header has a lot of configuration options, I'll limit myself to describing the basic functionality and otherwise refer to dedicated posts by more qualified people. At the end of this tutorial, I have added further links which I can recommend.
The following applies to all HTTP headers: browser support varies. Some browsers can ignore a header completely. Some will not consider every possible directive of a header. You can find the browser compatibility of every HTTP header on the Mozilla Developer Network (MDN).
When setting up HTTP headers, please note the following:
- Header names are not case sensitive
- The header name is followed by a colon (exception: configuration in the .htaccess; see above)
- The colon is followed by one or more values or directives
- Values or directives are separated by a semicolon
- If a directive has options, these are written in single quotes
- Line breaks within a header are not allowed
HTTP Strict Transport Security (HSTS)
The HSTS header tells the browser to connect to your website only with an encrypted connection (HTTPS). This will offer protection against a downgrade attack.
A valid SSL (or rather TSL) certificate for the website is required. If your website does not yet have a certificate, you mustn't add this HTTP header! Many hosting providers now offer free certificates with Let's Encrypt.
With max-age (time in seconds) you'll tell the browser for which period of time it should establish only encrypted connections. Often recommended values are one year (31536000 seconds) or two years. When you first introduce HTTPS on your website, use shorter periods and check for possible issues.
You should also consider to extend the policy to all subdomains by using the optional directive includeSubDomains.
In addition, there's the third but unofficial directive preload. If you sign up for the preload lists of Chrome or Firefox and meet all their requirements, these browsers will always open your website via HTTPS.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options
This HTTP header needs a short preface: the content type or MIME type classifies file formats on the Internet. An image with the file ending .jpg has the MIME type image/jpeg.
It's possible that the MIME type of a file is missing. In these cases the browser can use the so-called MIME sniffing to determine the file type by checking the first bytes of the file.
However, MIME sniffing can be misused to execute Cross-site scripting. The malicious code is hidden by the attacker in a file and then uploaded to the website. The browser finds the hidden HTML code via MIME sniffing and executes it as such.
You can use the header X-Content-Type-Options to deactivate MIME sniffing in the browser. The only available value is 'nosniff'. It will block script-like and style types without a valid MIME type.
X-Content-Type-Options: nosniff
X-Powered-By
Some server applications add this header by default to share information about server configurations (e.g. 'PHP/5.5.9-1ubuntu4.9'). This information is not needed to render your website, but it could be helpful for an attacker.
Depending on the server it can get difficult to remove this HTTP header completely. But you can easily overwrite the value. Feel free to get creative.
X-Powered-By: Unicorns and kittens
Referrer Policy
What's a referrer? When a browser opens a new page, it sends the address (referer) of the last opened page (source) to the new page (destination). This allows the destination to determine where the request came from. The referrer is sent in the HTTP request header 'Referer' (yes, that's a misspelling in the official header name).
You can use the Referrer Policy to control in which cases a browser sends a referrer–and which information it contains. Currently there are eight possible directives, which you can read in detail in Scott Helme's blog.
In short: you could limit the referrer to the domain of the source, or completely remove it. You can make this dependent on whether the target is on your own website or external, or whether the scheme changes from HTTPS to HTTP.
By using a Referrer Policy you can improve your visitor's privacy by not sharing informations about visited pages with external sites.
Referrer-Policy: strict-origin-when-cross-origin
Presented by the example of strict-origin-when-cross-origin:
Source | Destination | Referrer |
---|---|---|
https://www.sebkln.de/tutorials/ | https://www.sebkln.de/about/ | https://www.sebkln.de/tutorials/ |
https://www.sebkln.de/tutorials/ | https://demo.sebkln.de/ | https://www.sebkln.de/ |
https://www.sebkln.de/tutorials/ | https://www.example.org/ | https://www.sebkln.de/ |
https://www.sebkln.de/tutorials/ | http://www.sebkln.de/about/ | NULL |
https://www.sebkln.de/tutorials/ | http://www.example.org/ | NULL |
Permissions-Policy (previously named Feature-Policy)
Attention: This header was originally developed as 'Feature-Policy'! It was renamed to 'Permissions Policy' in May 2020. Both versions are quite new HTTP headers in an experimental stage.
The former Feature-Policy is supported by Chrome, Firefox, Edge Chromium, and Opera (as of 09/2020). Not all directives are necessarily supported. For current browser support of the new Permission-Policy, please visit caniuse.com.
I recommend setting both versions of this HTTP header until the browser support of the Permissions-Policy has improved.
Browsers offer some features and APIs for us developers to use. This includes the camera and microphone of devices. You can use the Permissions Policy to enable or disable features, or restrict them to a source. If you disable a feature, it is disabled for third parties, too (e.g. an <iframe>). You can customize each feature via its own directive individually.
The Permissions Policy also includes directives that enforce best practices on a site, such as the unoptimized-images directive. If implemented, pictures on the website which are significantly larger than their container will be replaced with placeholders. Smartphone users will not be sent unnecessary megapixel images (using responsive images would be the smarter choice, no question). The user must explicitly enable this experimental directive in his browser.
// New since May 2020:
Permissions-Policy: geolocation=(), midi=(), camera=(), usb=(), magnetometer=(), accelerometer=(), vr=(), speaker=(), ambient-light-sensor=(), gyroscope=(), microphone=()
// Former name and syntax:
Feature-Policy: geolocation 'none'; midi 'none'; camera 'none'; usb 'none'; magnetometer 'none'; accelerometer 'none'; vr 'none'; speaker 'none'; ambient-light-sensor 'none'; gyroscope 'none'; microphone 'none'
You can find additional examples of the new header value syntax in this W3C document.
Content-Security-Policy (CSP)
Personally, I consider this the most powerful weapon among the HTTP headers. It can be a very effective protection against Cross-site scripting. However, setting up the header can be complex and usually requires technical adjustments to the website. A CSP works as a whitelist and will block all ressources that aren't listed.
I will explain the most relevant directives and common pitfalls (particularly with regard to TYPO3) in the second part of this tutorial: How to (not) break your TYPO3 website with a Content Security Policy.
For starters the following directive is helpful. Its sole purpose it to disallow embedding your website in external frames (<frame>, <iframe>, <embed> or <object>). You can avoid clickjacking attacks in this way.
Content-Security-Policy: frame-ancestors 'self'
Weren't there more Security Headers?
Admitted. Two headers were relevant in the past, but not anymore:
X-Frame-Options
This HTTP header also provides the ability to disallow embedding your website in frames. But two of the three possible directives (sameorigin and allow-from) have issues with the evaluation of chained frames, whereby they are considered largely useless. The better and current approach is a Content Security Policy (CSP) with the directive frame-ancestors, which was developed for the same purpose (see above).
If you really need to support the outdated Internet Explorer (which is the only browser that doesn't understand the equivalent CSP directive), you could use X-Frame-Options: deny.
X-XSS-Protection
Some browsers (namely Chrome, Safari and Internet Explorer/Edge) have a built-in filter to prevent Cross-site scripting (XSS). If the browser detects an XSS attack, it will either open the page and remove the malicious code, or cancel the website rendering and display a warning message instead. By using the X-XSS-Protection Header we could always enable this filter, regardless of whether the user has turned it off.
The problem is: these filters now have many security holes and no longer work reliably. Microsoft now has disabled his filter in Edge by default. And Google is already planning to deprecate and remove its XSS auditor from Chrome.
By using a strong Content Security Policy (CSP) the X-XSS-Protection header becomes obsolete.
Analysis software for HTTP headers (and more)
The following services can assist you checking your HTTP headers. Certain header directives are evaluated differently.
- https://securityheaders.com (good start)
- https://webbkoll.dataskydd.net/en (broken down in detail, with additional information about data privacy and GDPR)
- https://observatory.mozilla.org (third party scanners as an option, e.g. TLS analysis)
- https://csp-evaluator.withgoogle.com