Transport Layer Security (TLS) and its predecessor Secure Sockets Layer (SSL), facilitates confidential communications on the web by providing end-to-end encryption between browsers and web servers. Without TLS, other security measures are fragile or ineffective. TLS is the cornerstone of HTTP security.
TLS implementation is not straight-forward. Protocol versions and cipher choice need to be carefully chosen and configured. X509 certificates by valid authorities need provisioning and cryptographic keys have to be securely managed. The excellent Qualys SSL Labs tool helps with fine tuning implementation details.
Properly configure and use TLS for all local and linked resources.
Instructs the browser to connect exclusively to the target server using HTTPS (SSL/TLS). This defends against a range of potential man-in-the-middle attacks, including SSL stripping, session cookie theft (if not properly protected). It also prevents the browser from connecting to a website if any certificate-related errors are encountered. HSTS is activated when a browser visits a HTTPS website which sets the corresponding HTTP header.
HSTS has a set duration, controlled by the max-age field value. This value can either be static or relative to a specific date in the future, perhaps to coincide with the expiration of a SSL certificate.
HSTS preference can be hard-coded in browsers by submitting domains to Chromium’s HSTS preload list which all browsers implementing HSTS use.
Note that HSTS does have it’s pitfalls. It has a provision to include subdomains, which may prove too broad in practise. Also, client errors can have grave consequences - an incorrect clock on the client causing it to consider the server’s SSL certificate as not yet valid or expired, or a missing root CA certificate - will no longer cause a certificate error in the browser. The browser will completely refuse to access the page, and probably display an error totally incomprehensible to everyone except security professionals.
Set the HSTS header with a long lifetime, preferably half a year or longer.
Strict-Transport-Security: max-age=31536000
HTTP Public-Key Pinning (HPKP) instructs browsers to only connect to the host if it can present a known SSL/TLS public key - or a certificate signed by a known key. In other words, browsers won’t connect to hosts if the SSL/TLS key has changed in an unexpected way. This mainly guards against fraudulent certificates issued by trusted certificate authorities (CAs) or rogue CA certificates which a user might have been tricked to install.
As an example, a browser connects to https://example.com which presents this header. The header tells the browser to only connect again in the future if the certificate key matches, or if a key in the issuing certificate chain matches. Other combinations of instructions are possible. They all greatly reduce the possibilities for attackers to impersonate the host or intercept communications between the client and the legitimate host.
Like HSTS, HPKP requires careful thought and planning before implementing. Mistakes can lock users out of your site, with no easy way to fix.
Determine if public-key pinning is needed for your site. If so, start with a short lifetime and increase it if no problems are encountered after a while. Establish a backup plan if the SSL/TLS key needs renewing. Preferebly create a backup key and store offline.
Example HTTP header:
Public-Key-Pins: max-age=5184000; pin-sha256="+oZq/vo3Kcv0CQPjpdwyInqVXmLiobmUJ3FaDpD/U6c="; pin-sha256="47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
The main website is served securely over HTTPS but loads some files (images, js, css) over HTTP. This is a huge security hole and undermines the security provided by HTTPS. Affected sites could be leaking session cookies or user behavior information. They could also be vulnerable to injection and other MITM attacks, which HTTPS normally prevents.
If HTTPS is deployed for the main website, use it everywhere and for all content.
Provides browsers with clear instructions on type and behavior of content on the website. A good content security policy (CSP) hardens security and can help defend against attacks like cross-site-scripting (XSS) and other injection attacks. CSP is supported by all major browsers, although only partially in IE 11 and earlier.
A good CSP is based on a white-listing approach, disallowing everything except explicitly allowed content. It also restricts javascript origin and allowed operations.
CSP can be hard to enable for legacy codebases. To ease implementation, CSP provides a “report-only" mode where CSP violations in browsers are sent to a website endpoint, but the policy is not otherwise enforced.
New projects should use CSP from the start.
Start with a restrictive policy and relax where necessary and appropriate. Example disallowing everything:
Content-Security-Policy: default-src 'none';
Now let's allow self hosted scripts, images, CSS, fonts and AJAX, along with jQuery CDN hosted scripts and Google Analytics:
Content-Security-Policy: default-src 'none'; script-src 'self' https://code.jquery.com https://www.google-analytics.com; img-src 'self' https://www.google-analytics.com; connect-src 'self'; font-src 'self'; style-src 'self';
Be warned that disallowing everything might break your site, e.g. if you use the child-src directive and a browser doesn't support it. A less restrictive policy might start with the following:
Content-Security-Policy: default-src 'self';
A even less restrictive policy could even use default-src '*' and then add restrictions. I recommend against this, unless you understand the implications fully. Otherwise you might be relying on a CSP which is only providing you with a false sense of security.
Controls whether the site can be placed in an <iframe>, <frame> or <object> tag. Disallowing framing can prevent clickjacking attacks, amongst others. For example, Internet Explorer’s universal cross-site-scripting bug from February 2015 was mitigated by this header.
The X-Frame-Options is a non-standard header, superseded by the frame ancestor directive in Content Security Policy level 2. However, frame ancestors is not universally supported yet, while X-Frame-Options is widely supported.
Determine if your website needs to be allow being rendered in a frame. Disallow entirely by setting the option to deny or allow same-origin framing by sameorigin. Avoid the allow-from option because of limited or buggy browser support. Example HTTP header:
X-Frame-Options: deny
Cross-site scripting (XSS or CSS) protection is built into most popular browsers, with the notable exception of Firefox. This protection is user configurable and can be turned off. Therefore, it's a good idea to explicitly request for the browser to employ its XSS filter on your website.
Conversely, websites can request for the XSS protection to be disabled on a page-by-page basis. This is definitely not a good idea.
Use the following HTTP header:
X-Xss-Protection: 1; block
Indicates the preferences for caching the page output. The appropriate value varies with the nature of the website data, but including a preference is strongly recommended. Otherwise, it's up to the browsers and proxies to choose whether to cache the content. An inappropriate choice can cause performance problems, security issues, or both.
Develop a caching strategy and then include the cache preference as a HTTP header.
Cache-Control: public*
*One of public, private, no-cache or no-store. If caching is to be allowed, a max-age value should be included for Cache-Control as well as a Etag header to allow for cache validation by clients.
MIME sniffing is when browsers treat files from server in a different way than the server instructs. This can be dangerous when a website is hosting untrusted (e.g. user-provided) content. Lets say the server allows image uploads from users. Should a user upload a HTML document instead, a browser might render it as a web execute the scriptpage even though the server clearly says it is sending an image. The non-standard header X-Content-Type-Options instructs browsers not do any MIME sniffing away from the specified type.
Always set the header:
X-Content-Type-Options: nosniff
Browsers typically load a host of resources, javascripts and stylesheets, from external domains. Content delivery networks are often used. Should the external resource be compromised, the security of the dependant site can be as well. Subresource integrity allows the browser to verify the javascript or stylesheet has not been unexpectedly modified.
Set the integrity attribute for external javascripts and stylesheets.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js" integrity="sha384-6ePHh72Rl3hKio4HiJ841psfsRJveeS+aLoaEf3BWfS+gTF0XdAqku2ka8VddikM"></script>
You should always provide local copies of the external script and implement a way to fall back to loading them in case the external load fails the integrity check. Otherwise your site might break. Example:
<script>window.jQuery || document.write('<script src="/jquery.min.js"><\/script>')</script>
Iframes are everywhere on the WWW. The average website has 5.1 iframes, mostly for loading third-party content. These iframes have many ways to harm the hosting websites, including running scripts and plugins and redirecting visitors. The sandbox attribute enables restrictions to be placed on what can be done inside the iframe.
Set the sandbox attribute for iframes and then add the required permissions.
<iframe src="https://example.com" sandbox="allow-same-origin allow-scripts"></script>
Servers include a timestamp in all responses. An inaccurate clock doesn’t cause problems for the client browser. However, issues can arise when interfacing with other systems or services.
Use the network time protocol (NTP) to keep server clocks accurate.
Most web servers set headers to identify themselves and their version number. This serves only informational purpose and practical uses are very limited. Removing the entire header, while perfectly acceptable, is usually not necessary. However, it is recommended to remove the version numbers from the header. In the case that bugs exist for a specific web server version, including the version number can act as an invitation for script kiddies to try exploits against the server.
Include the server name but remove the version number.
Server: nginx
Many web frameworks set HTTP headers identifying the framework and/or version number. This serves little purpose except for satisfying the curiosity of users and mainly acts as an advertisement for the technology stack. These headers are non-standard and have no effect on how browsers render the site.
While they have little practical use, these headers can prove invaluable to robots or spiders searching for websites running outdated versions of software which may contain security vulnerabilities. These headers can make easy targets out of websites if they aren’t regularly updated.
Remove these headers from the server responses: X-Powered-By, X-Runtime, X-Version and X-AspNet-Version
Cookies containing sensitive information, especially session IDs, need to be marked as secure, assuming the website is server over HTTPS. This stops the cookies from being sent clear-text over HTTP. The only other way for a website to prevent non-secure cookies from being transferred over HTTP is through HSTS. Using both secure cookies and HSTS is recommended.
Session cookies should be marked with the HttpOnly value to prevent them from being accessible from javascript. This prevents attackers from leveraging XSS to steal session cookies, amongst other things. Other cookies might not strictly need to be marked this way. But unless there’s a clear need to access their values from javascript, it’s best to stay on the safe side and mark all cookies as HttpOnly
Mark all cookies secure and HttpOnly.
Set-Cookie: Key=Value; path=/; secure; HttpOnly, Key2=Value2; secure; HttpOnly