{"id":238,"date":"2018-11-05T11:05:41","date_gmt":"2018-11-05T11:05:41","guid":{"rendered":"https:\/\/int64software.com\/blog\/?p=238"},"modified":"2019-03-05T10:47:07","modified_gmt":"2019-03-05T10:47:07","slug":"hardening-website-security-part-1-http-security-headers","status":"publish","type":"post","link":"https:\/\/int64software.com\/blog\/2018\/11\/05\/hardening-website-security-part-1-http-security-headers\/","title":{"rendered":"Hardening Website Security &#8211; Part 1: HTTP Security Headers"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It feels like almost every week there&#8217;s another news item\nabout personal information being stolen because yet another company&#8217;s website\ngot hacked.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Most of these attacks are perpetrated through social\nengineering, persuading somebody to hand over some detail which allows the\nhacker to gain additional privileges and, eventually, access to personal\ninformation. However, a lot are still carried out due to poor security or\nmisconfigured websites.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the first in a series of articles which will aim to demystify some of the internet security principals you must get your head around if you hope to run a secure website in the 21<sup>st<\/sup> century.<\/p>\n\n\n\n<figure class=\"wp-block-image internalad\"><a href=\"https:\/\/expertisematrix.com\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"216\" src=\"https:\/\/int64software.com\/blog\/wp-content\/uploads\/2018\/11\/emx-800-high.png\" alt=\"Expertise Matrix - Manage the skill development process of your team.\" class=\"wp-image-252\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Contents<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The series will be consist of the following topics:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>HTTP Security Headers<ul><li>1a. <a rel=\"noreferrer noopener\" aria-label=\"HTTP Security Headers\u20281a. HSTS Preloading\u2028User Session Security (Coming Soon)\u2028Database Security (Coming Soon)\u2028Safely Handling User Input (Coming Soon) (opens in a new tab)\" href=\"https:\/\/int64software.com\/blog\/2018\/11\/10\/hardening-website-security-part-1a-hsts-preloading\/\" target=\"_blank\">HSTS Preloading<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/int64software.com\/blog\/2018\/11\/13\/hardening-website-security-part-2-user-session-cookie-security\/\">User Session Security<\/a><\/li><li><a rel=\"noreferrer noopener\" aria-label=\"HTTP Security Headers\u20281a. HSTS Preloading\u2028User Session Security\u2028Database Security \u2028Safely Handling User Input (Coming Soon) (opens in a new tab)\" href=\"https:\/\/int64software.com\/blog\/2018\/11\/21\/hardening-website-security-part-3-website-database-security\/\" target=\"_blank\">Database Security<\/a> <\/li><li><a href=\"https:\/\/int64software.com\/blog\/2018\/12\/04\/hardening-website-security-part-4-safely-handling-user-input\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"HTTP Security Headers\u20281a. HSTS Preloading\u2028User Session Security\u2028Database Security \u2028Safely Handling User Input  (opens in a new tab)\">Safely Handling User Input<\/a> <\/li><\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Topics Not Covered<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">I have opted not to cover server infrastructure security concerns at this time due to the huge number of possible configurations (hosting packages or VPS, operating systems, dashboard systems, firewalls, etc.) While I may cover some of these specifically in future articles, there\u2019s too much nuance in these subjects to be able to do them justice here.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Disclaimer<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Everything presented in this article is the result of years of experience, or trial and (frequent) error. Messing around with HTTP headers on a live site can (and likely will) lead to unexpected, possibly disastrous results. The information presented here is correct to the best of the author&#8217;s knowledge, but in matters of security we strictly advise the reader to make sure they carry out additional research and understand the dangers before making any changes to their own systems or web sites. Any code presented here is done so as example only, and may be incomplete or contain errors. Readers should be careful when copying and pasting code from any website, this site included. Int64 Software Ltd, its employees and its representatives accept no liability for damage caused by the misuse, either intentional or unintentional, of the information presented in its posts and articles.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Section Overview<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">When it receives a standard HTTP GET request, a web server\nwill return the information that was requested along with certain header\ninformation. This is necessary information so that your browser knows how to\nhandle the response data, things like the encoding method, and length of the\ndata.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this part of the series, we\u2019ll be looking at the security related headers, what they do, and how to set them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Configuring Web Servers<\/h2>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"480\" src=\"https:\/\/int64software.com\/blog\/wp-content\/uploads\/2018\/11\/webservers-2.jpg\" alt=\"Configuring Web Servers for Security\" class=\"wp-image-243\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Depending on your web server setup, the way you set headers will vary. Below are some of the more popular options and information on how to set headers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Apache Web Server<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In Apache you\u2019ll need to use the \u201cHeader\u201d directive in either your .htaccess, .httpd.conf or the VirtualHost section of your virtual host configuration file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Header set [HeaderName] [HeaderValue]<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Nginx<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In your Nginx \u201c<em>config<\/em>\u201d folder, open the \u201c<em>nginx.conf<\/em>\u201d file and add the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>add_header [HeaderName] \"[HeaderValue]\";<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">IIS<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add a customHeaders section to the \u201c<em>&lt;system.webServer&gt;<\/em>\u201d section in your \u201c<em>Web.Config<\/em>\u201d file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;httpProtocol>\n\t&lt;customHeaders>\n\t\t&lt;add name=\u201d[HeaderName]\u201d value=\u201d[HeaderValue]\u201d \/>\n\t&lt;\/customHeaders>\n&lt;\/httpProtocol><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">PHP<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In PHP, headers can be defined at the very top of your PHP file (before any actual output has been sent, <strong>including blank spaces<\/strong>) using the \u201c<em>header<\/em>\u201d function.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>header('[HeaderName]: [HeaderValue]', true);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note that headers only need to be set once per request, not in every included PHP file. The \u201c<em>true<\/em>\u201d part of this command indicates that an existing header with this name should be replaced if encountered.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ASP.NET<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In your \u201c<em>global.asax<\/em>\u201d file add the following code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>protected void Application_BeginRequest(object sender, EventArgs e)\n{\n\tHttpContext.Current.Response.AddHeader(\u201c[HeaderName]\u201d, \u201c[HeaderValue]\u201d);\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">The Headers<\/h2>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"339\" src=\"https:\/\/int64software.com\/blog\/wp-content\/uploads\/2018\/11\/headers.jpg\" alt=\"HTTP Headers for Security\" class=\"wp-image-244\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">X-Frame-Options<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The \u201cX-Frame-Options\u201d header is used to prevent\n\u201cClickjacking\u201d, which is where an attacker attempts to make window user events\nsuch as clicking on a button do something other than the intended action. This\nmay be done by, for example, embedding your webpage in an iframe on their own\nsite and overlay a transparent interface over the top so that, while the user\nthinks they\u2019re using your website, they\u2019re actually feeding information to the\nattacker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By setting X-Frame-Options you can tell the client browser\nthat this page should not be allowed to load in a frame or iframe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">X-Frame-Options has three valid values:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>DENY<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This outright prevents any domain from rendering the page in a frame. This is the recommended setting for all pages except where a need has been specifically identified.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>SAMEORIGIN<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This will allow you to load your own content in frames, but will not allow it on other domains (including sub-domains).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>ALLOW-FROM &lt;uri&gt;<\/strong><br><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This prevents the page from being loaded in frames except for the specified URI (e.g. \u201cALLOW-FROM https:\/\/myothersite.com\u201d). <strong>Note that browser support for this value is limited.<\/strong><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Recommended Value<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">X-Frame-Options should always be set to \u201c<em>DENY<\/em>\u201d unless a need has been identified for it to be set otherwise.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Other Notes<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">X-Frame-Options is still widely supported, but has been deprecated in favour of the \u201cframe-src\u201d Content Security Policy (CSP) directive which is covered later. It is worth still including however as CSPs are only supported in Internet Explorer from version 11, X-Frame-Options support goes back to version 8.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">X-XSS-Protection<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Designed to prevent Cross-site\nscripting (XSS), the \u201cX-XSS-Protection\u201d header allows you to enable XSS\nfiltering in the client\u2019s browser. XSS is an attack whereby the attacker\ninjects scripts into your website through, for example, the use of improperly\nsecured user input fields, and those scripts then either steal data or\notherwise impact on the use of the page where that script is loaded.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A common example of this can be\nseen in certain recent hacking incidents where scripts were injected through\ncompromised ad networks. The end result could be that instead of submitting\npayment details to the site you\u2019re on, they get sent to a hacker instead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The X-XSS-Protection header reduces this risk by instructing browsers to filter and block XSS attacks. It has 4 possible values:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>0.<\/strong> <br>DisablesXSS filtering (not recommended)<br><strong>1.<\/strong> <br>EnablesXSS filtering. If detected, the browser removes the unsafe code and displaysthe page as normal. This is usually the default.<br><strong>1; mode=block<\/strong> <br>EnablesXSS filtering. If detected, the browser blocks the page from rendering.<br><strong>1; report=&lt;reporting-uri&gt;<\/strong><br>EnablesXSS filtering and sanitizes the page if detected. It will then report theviolation to the specified URI.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Recommended Value<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">X-XSS-Protection should be set to 1, optionally enabling theblock or reporting options as your service requires.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Other Notes<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">As with the X-Frame-Options, this value should be setalongside a strong Content Security Policy which fully disables the use ofinline scripts. This is covered later.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">X-Content-Type-Options<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This header only has one possible value (\u201c<em>nosniff<\/em>\u201d) and a single purpose: it will\nprevent the client\u2019s browser from interpreting files as anything other than\nthat declared by the content type header.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When a browser downloads a file, in addition to being told\nwhat the file type is by the Content-Type header, it will perform Content\nSniffing (or Media Type Sniffing or MIME Sniffing) where it uses various\ntechniques to find out what the file is and then handle it appropriately.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, this can lead to files which the user expects to be of one type, being another completely, which can result in attacks such as cross-site scripting or malware distribution.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Recommended Value<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">As mentioned above, X-Content-Type-Options should be set to \u201c<em>nosniff<\/em>\u201d to block this behaviour.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Strict-Transport-Security<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The HTTP Strict Transport Security (HSTS) header is used to\nnotify a browser that it should only interact with this server over a secure\nHTTPS connection, and never on an unencrypted HTTP protocol.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The header has two values to set:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>max-age=&lt;MaxAgeInSeconds&gt;<\/strong><br>Indicates for how long (in seconds) the browser should remember that this site must only be accessed over HTTPS.<br><strong>includeSubDomains<\/strong><br>If specified, indicates that all sub-domains of this site should also only use secure HTTP connections.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Recommended Value<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">This header should be defined with the maximum age set to some appropriate values such as 1 year (31536000 seconds). Optionally, the includeSubDomains property can be set if you require it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Strict-Transport-Security: max-age=31536000; includeSubDomains<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">Content-Security-Policy<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Perhaps the most important, widely supported and often\ndifficult to understand header is the Content Security Policy. This single\npolicy, if configured correctly, can do the most good for securing your\nwebsite. But if misconfigured, can break a lot of things.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The policy\u2019s settings are mostly concerned with specifically\ndictating where scripts and server resources can be loaded from.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A CSP header consists of two parts: a directive and a list\nof sources. Directives specify the type of resource you\u2019d like to control, and\nthe list of sources specify where the current directive can be safely loaded\nfrom.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Note that, as the CSP can contain multiple individual\npolicies, you can either set one header, separating the directives with a\nsemi-colon (;), or you can define multiple Content-Security-Policy headers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, if I wanted to specify that I would allow JavaScript files from our own server and a JS repository at \u201cjs.example.com\u201d, and style sheets only from our server, I may set a header in either of these two ways (shown in PHP).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>header('Content-Security-Policy: script-src \\\u2018self\\\u2019 js.example.com; style-src \\\u2019self\\\u2019;', true);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Or like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>header('Content-Security-Policy: script-src \\\u2018self\\\u2019 js.example.com;', false);\nheader('Content-Security-Policy: style-src \\\u2019self\\\u2019;', false);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note that the header replace parameter is changed to \u201c<em>false<\/em>\u201d on the latter to allow us to set more than one header with this name.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Directives<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>default-src<\/strong><br>This is what all of the other fetch directives will look to if the fetch type is not explicitly defined. So if an image is requested but the \u201cimg-src\u201d directive isn\u2019t set, then this directive will be used instead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>child-src (deprecated)<\/strong><br>Previously used for web workers and nested browsing contexts such as iframes. However, because of the need to occasionally have separate policies for frames and workers, this is now deprecated in favour of (the un-deprecated) \u201c<em>frame-src<\/em>\u201d and \u201c<em>worker-src<\/em>\u201d.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>connect-src<\/strong><br>Restricts sources that can be used by script interfaces (&lt;a&gt;<a> <\/a>pings, Fetch, XMLHttpRequest such as Ajax requests, WebSocket and EventSource)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>font-src<\/strong><br>Where fonts can be fetched from (e.g. Google Fonts)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>frame-src<\/strong><br>When using or &lt;frame&gt; or &lt;iframe&gt; thisspecifies where the source can come from.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>frame-ancestors<\/strong><br>Unlike frame-src, this directive specifies which sources the current page can be loaded in a frame or iframe. Setting this to \u201cnone\u201d works similar to setting \u201cX-Frame-Options: DENY\u201d.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>img-src<\/strong><br>Restrict where images can be loaded from.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>manifest-src<\/strong><br>Specifies where application manifest files can be sourced from.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>media-src<\/strong><br>Restricts loading of media using the <em>&lt;audio><\/em> and <em>&lt;video><\/em> HTML5 elements.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>object-src<\/strong><br>Restricts the sources of elements using the <em>&lt;object><\/em>, <em>&lt;embed><\/em> and <em>&lt;applet><\/em> tags.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>prefetch-src<\/strong><br>Where resources may be pre-fetched or pre-rendered from.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>script-src<\/strong><br>Valid sources for JavaScript files. This should be tied down to \u2018self\u2019 and any third-party repositories as needed. You should never allow \u2018unsafe-inline\u2019 for scripts as this would allow for inline scripts running and therefore defeats the primary purpose of a CSP, which is to help block XSS attacks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>style-src<\/strong><br>Restrict where CSS stylesheets can be loaded from.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>webrtc-src<\/strong><br>Specifies where WebRTC (Web Real-Time Communications) connections can come from.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>worker-src<\/strong><br>Valid sources for Worker, SharedWorker and ServiceWorker scripts.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Other Notable Directives<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>form-action<\/strong><br>This can be used to restrict the URLs that can be used as the target of a form submission.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>report-uri<\/strong><br>Allows you to specify a URI where CSP violations are reported to.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>block-all-mixed-content<\/strong><br>Stops the loading of any assets over HTTP when the current page has been loaded over HTTPS.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Sources that can be added to Directives<br><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>*<\/strong><br>Allows all sources except for data streams (blob:, data:, filesystem:).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u2018none\u2019<\/strong><br>Blocks all attempts to load this resource type.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u2018self\u2019<\/strong><br>Allows loading resources from the same origin (scheme, host and port).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Data:<\/strong><br>Data scheme sources are allowed (such as Base64 encoded images).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>domain.example.com<\/strong><br>The specified domain is allowed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>*.example.com<\/strong><br>The specified domain and all subdomains are allowed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>https:\/\/domain.example.com<\/strong><br>The specified domain is allowed only over HTTPS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>https:<\/strong><br>Any domain as long as it is over HTTPS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u2018unsafe-inline\u2019<\/strong><br>Allows for the use of inline sources, such as element styles or script blocks (shouldn\u2019t be used unless absolutely necessary).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u2018unsafe-eval\u2019<\/strong><br>Allows the use of code evaluation commands such as the JavaScript \u201ceval()\u201d command.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u2018nonce-&lt;noncevalue&gt;\u2018<\/strong><br>Allows a random nonce value to be added to style or script tags which match this \u201c<em>&lt;noncevalue&gt;<\/em>\u201d, the styles or scripts in this tag can then be run. Note that bypasses for this have been demonstrated in the past, so it should not be considered secure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u2018sha256-&lt;hashvalue&gt;\u2018<\/strong><br>Allows scripts or stylesheets to be used if their SHA256 hash matches this value.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Recommended Values<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">This one is a bit harder to recommend an exact value for as it should be catered to your website. My policy (generally when talking about anything security related) is to lock it down as tight as you can, then find out what breaks and then either fix those things, or start loosening it up until you get everything working again. That applies here, but there may be one or two extra steps.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s consider \u201cstyle-src\u201d, for example. It\u2019s easy enough to lock it down to \u2018self\u2019, meaning that only Cascading Style Sheet (.css) files on the same source can be loaded. But what if you have a few small inline style elements? The best thing to do here is to move them into a css file, but that isn\u2019t always possible or convenient, so you may need to allow \u2018unsafe-inline\u2019.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I figured the best way to tackle this would be to walk through one of my CSPs, why I made each decision, and what negative connotations it could have.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>default-src 'none\u2019; <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The default fall-back is to deny all resources that we haven\u2019t specifically defined below. Since I have gone through and validated all of the other sources, nothing should be reaching this anyway, and if it does then it wasn\u2019t added by me. Be warned that you will get caught out by this when adding new content sources which is why most people set this to &#8220;self&#8221; instead.<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>script-src 'self' https:\/\/js.stripe.com;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I\u2019ve packed and minified all of my own scripts, so only \u2018self\u2019 is required for those (strictly no inline scripts). In addition to that, I use Stripe as a payment platform, so have to allow scripts to be loaded from their JavaScript repository.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>connect-src \u2018self\u2019 https:\/\/api.stripe.com; <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I make fairly extensive use of Ajax calls, so \u2018self\u2019 is required. Stripe also requires the use of script interfaces, so I allow them as a source. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>img-src 'self' data:; <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Obviously the page in question, having been written after the 1980\u2019s, features images, all of which are self-hosted so \u2018self\u2019 is included. I also make use of some inline SVG graphics, so I include \u2018data:\u2019.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>style-src 'self' 'unsafe-inline' https:\/\/fonts.googleapis.com; <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Stylesheets are mostly self-hosted except for Google Fonts, so both of them are allowed. For my sins I am also guilty of (rarely but occasionally) using some inline styles, so I have allowed \u2018unsafe-inline\u2019 on this directive. Note that proof of concept attacks have been shown for reading form data using CSS injection, so this is a potential weakness you will need to weigh up.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>font-src 'self' https:\/\/fonts.gstatic.com;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Another pretty simple one, we want to allow fonts to be loaded locally and from Google\u2019s Font repository.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>frame-src 'self' https:\/\/*.stripe.com https:\/\/stripe.com; <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A slightly more complicated one, Stripe allows the authorisation of 3D Secure protected cards. Unfortunately, the advice for implementing this is by using an Iframe (yeah, I hate this too). So here I\u2019ve added Stripe as the payment processor, and \u2018self\u2019 as the authorisation process returns the iframe to a URL of your choice once complete.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>For those that use Stripe, you may note that their documentation states to only add &#8220;https:\/\/js.stripe.com&#8221; as a frame source, however I&#8217;ve found problems with this and am currently working with their support team at the moment to get to the bottom of it.<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>frame-ancestors \u2018none\u2019;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As mentioned previously, this prevents the current page from being loaded in a frame or iframe on this site or any others. Note that I loosen this to \u201cself\u201d on the 3D Secure return page to allow it to be displayed in a frame on my own site. <br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>report-uri \/csp-report.php;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Any CSP violations should be reported (by the client\u2019s browser) to this URI, where it is logged for later analysis.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>block-all-mixed-content;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, even though I don\u2019t have any mixed content, I block it as a matter of course.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Extra Notes on Other Directives<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Most of the other directive sources will fallback on the default-src and get blocked. The exception being the worker-src, which will fallback on child-src and then script-src in most browsers (Chrome 59 and higher will skip child-src and go directly to default-src, and Edge 17 will skip script-src).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By not defining the &#8216;plugin-types&#8217; directive, this technically defaults to allowing all plugin types (&lt;embed&gt;, &lt;object&gt;, &lt;applet&gt;), however because we don&#8217;t define &#8220;object-src&#8221;, it falls back to default-src and blocks all plugin types at that level.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Code Examples<\/h2>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"314\" src=\"https:\/\/int64software.com\/blog\/wp-content\/uploads\/2018\/11\/code.jpg\" alt=\"C++ Code\" class=\"wp-image-245\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Apache Web Server<br><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Header set X-Frame-Options \u201cSAMEORIGIN\u201d\nHeader set X-XSS-Protection \u201c1; mode=block\u201d\nHeader set X-Content-Type-Options \u201cnosniff\u201d\nHeader set Strict-Transport-Security \u201cmax-age=31536000; includeSubDomains\u201d\nHeader set Content-Security-Policy \u201cdefault-src 'none'; script-src 'self' https:\/\/js.stripe.com; connect-src 'self' https:\/\/api.stripe.com; img-src 'self' data:; style-src 'self' 'unsafe-inline' https:\/\/fonts.googleapis.com; font-src 'self' https:\/\/fonts.gstatic.com; frame-src 'self' https:\/\/*.stripe.com https:\/\/stripe.com; frame-ancestors 'none'; report-uri \/csp_report.php; block-all-mixed-content;\u201d<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Nginx<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>add_header X-Frame-Options \u201cSAMEORIGIN\u201d\nadd_header X-XSS-Protection \u201c1; mode=block\u201d\nadd_header X-Content-Type-Options \u201cnosniff\u201d\nadd_header Strict-Transport-Security \u201cmax-age=31536000; includeSubDomains\u201d\nadd_header Content-Security-Policy \u201cdefault-src 'none'; script-src 'self' https:\/\/js.stripe.com; connect-src 'self' https:\/\/api.stripe.com; img-src 'self' data:; style-src 'self' 'unsafe-inline' https:\/\/fonts.googleapis.com; font-src 'self' https:\/\/fonts.gstatic.com; frame-src 'self' https:\/\/*.stripe.com https:\/\/stripe.com; frame-ancestors 'none'; report-uri \/csp_report.php; block-all-mixed-content;\u201d<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">IIS<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;httpProtocol>\n\t&lt;customHeaders>\n\t    &lt;add name=\u201dX-Frame-Options\u201d value=\u201dSAMEORIGIN\u201d \/>\n\t    &lt;add name=\u201dX-XSS-Protection\u201d value=\u201d1; mode=block\u201d \/>\n   \t    &lt;add name=\u201dX-Content-Type-Options\u201d value=\u201dnosniff\u201d \/>\n\t    &lt;add name=\u201dStrict-Transport-Security\u201d value=\u201dmax-age=31536000; includeSubDomains\u201d\/>\n\t    &lt;add name=\u201dContent-Security-Policy\u201d value=\u201ddefault-src 'none'; script-src 'self' https:\/\/js.stripe.com; connect-src 'self' https:\/\/api.stripe.com; img-src 'self' data:; style-src 'self' 'unsafe-inline' https:\/\/fonts.googleapis.com; font-src 'self' https:\/\/fonts.gstatic.com; frame-src 'self' https:\/\/*.stripe.com https:\/\/stripe.com; frame-ancestors 'none'; report-uri \/csp_report.php; block-all-mixed-content;\u201d \/>\n\t&lt;\/customHeaders>\n&lt;\/httpProtocol><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">PHP<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>header('[HeaderName]: [HeaderValue]', true);\n\nheader('X-Frame-Options: SAMEORIGIN\u2019);\nheader('X-XSS-Protection: 1; mode=block\u2019);\nheader('X-Content-Type-Options: nosniff\u2019);\nheader('Strict-Transport-Security: max-age=31536000; includeSubDomains\u2019);\nheader('Content-Security-Policy: default-src \\'none\\'; script-src \\'self\\' https:\/\/js.stripe.com; connect-src \\'self\\' https:\/\/api.stripe.com; img-src \\'self\\' data:; style-src \\'self\\' \\'unsafe-inline\\' https:\/\/fonts.googleapis.com; font-src \\'self\\' https:\/\/fonts.gstatic.com; frame-src \\'self\\' https:\/\/*.stripe.com https:\/\/stripe.com; frame-ancestors \\'none\\'; report-uri \/csp_report.php; block-all-mixed-content;\u2019);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">ASP.NET<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>protected void Application_BeginRequest(object sender, EventArgs e)\n{\n    HttpContext.Current.Response.AddHeader(\u201cX-Frame-Options\u201d, \u201cSAMEORIGIN\u201d);\n    HttpContext.Current.Response.AddHeader(\u201cX-XSS-Protection\u201d, \u201c1; mode=block\u201d);\n    HttpContext.Current.Response.AddHeader(\u201cX-Content-Type-Options\u201d, \u201cnosniff\u201d);\n    HttpContext.Current.Response.AddHeader(\u201cStrict-Transport-Security\u201d, \u201cmax-age=31536000; includeSubDomains\u201d);\n    HttpContext.Current.Response.AddHeader(\u201cContent-Security-Policy\u201d, \n      \u201cdefault-src 'none'; script-src 'self' https:\/\/js.stripe.com; connect-src 'self' https:\/\/api.stripe.com; img-src 'self' data:; style-src 'self' 'unsafe-inline' https:\/\/fonts.googleapis.com; font-src 'self' https:\/\/fonts.gstatic.com; frame-src 'self' https:\/\/*.stripe.com https:\/\/stripe.com; frame-ancestors 'none'; report-uri \/csp_report.php; block-all-mixed-content;\u201d);\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image internalad\"><a href=\"https:\/\/int64software.com\/overlaps\/\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"151\" src=\"https:\/\/int64software.com\/blog\/wp-content\/uploads\/2018\/11\/overlaps-800-tall.png\" alt=\"OverLAPS is a self-hosted web interface for Microsoft's Local Administrator Password Solution (LAPS)\" class=\"wp-image-276\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">What&#8217;s Next<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Please see our addendum to this topic on the use of <a href=\"https:\/\/int64software.com\/blog\/2018\/11\/10\/hardening-website-security-part-1a-hsts-preloading\/\">HSTS Preloading<\/a>, and then continue on to <a href=\"https:\/\/int64software.com\/blog\/2018\/11\/13\/hardening-website-security-part-2-user-session-cookie-security\/\">part 2 where we take a look at attacks that can be made against user sessions<\/a>, and how to mitigate them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Follow us on <a href=\"https:\/\/twitter.com\/Int64Software\">Twitter&nbsp;<\/a>for regular updates.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Reporting Errors<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If you notice any errors, or have something you think would add to the content of this article, feel free to email <a href=\"mailto:support@int64software.com\">support@int64software.com<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction It feels like almost every week there&#8217;s another news item about personal information being stolen because yet another company&#8217;s website got hacked. Most of these attacks are perpetrated through social engineering, persuading somebody to hand over some detail which allows the hacker to gain additional privileges and, eventually, access to personal information. However, a&#8230;<\/p>\n","protected":false},"author":1,"featured_media":240,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","footnotes":""},"categories":[13,50,7],"tags":[53,56,61,51,55,54,52,9,60,8,59,57,58],"class_list":["post-238","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","category-security","category-tutorial","tag-apache","tag-asp-net","tag-content-security-policy","tag-http","tag-iis","tag-nginx","tag-php","tag-security","tag-strict-transport-security","tag-tutorial","tag-x-content-type-options","tag-x-frame-options","tag-x-xss-protection"],"_links":{"self":[{"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/posts\/238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/comments?post=238"}],"version-history":[{"count":16,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/posts\/238\/revisions"}],"predecessor-version":[{"id":528,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/posts\/238\/revisions\/528"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/media\/240"}],"wp:attachment":[{"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/media?parent=238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/categories?post=238"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/tags?post=238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}