Security headers are essential for safeguarding your .NET web apps and websites. If you’re looking for good security practices that are reliable and effective, look no further.
This guide provides some essential tips from a well-known senior developer in the .NET community, allowing you to confidently configure your application’s security settings. Follow these steps, and enjoy the peace of mind that comes with knowing your application is safe 😉
Configuring Security Headers
These tips are provided by Stefan Djokic, Senior Software Engineer working at EXLRT, a digital customer experience agency specializing in retail, travel and hospitality working with huge companies such as Adidas, IBM or Disneyland Paris.
I very recommend you to follow him on Linkedin because he is always sharing valuable content about C#, .NET and more!
First Stefan reminds us how an ASP.NET application works: it runs on a server and then sends data to the user’s browser.
This scheme is well known and common but has a problem. As he mentions:
“The browsers are unsafe. As developers, we don’t have control over what happens with the data after we send it.
The browsers are very permissive — it can be easly exploited.”
One of these major problems — as you might have guessed — is XSS attacks.
In a Cross Site Scripting (XSS) attack the attacker will execute malicious code (commonly scripts) in the victim’s browser (a random user, it could be you). These malicious scripts are injected into a legitimate page and executed when the user enters it.
At that moment the user does not notice the execution of the malicious script thinking that there should not be any problem being a legitimate page.
To keep our users or visitors safe we must check that the input does not contain malicious code before sending the data. In other words: sanitize the application input.
Stefan provides the solution using the X-XSS-Protection:
“X-XSS-Protection is a header that can be set on a webpage to activate “limited” XSS protection in certain browsers.”
The syntax is as follows:
X-XSS-Protection: 1; mode=block
And the practical example shown by Stefan would look like this:
By enabling this header, the browser will prevent the page from loading in case it detects an attack (XSS filtering).
This tip helps to avoid XSS attacks in a simple way. Stefan also makes a point about popular browsers: their policy for implementing XSS protection has changed.
“This means that if you do not need to support legacy browsers, it is recommended that you use Content-Security-Policy without allowing unsafe-inline scripts instead.”
You may be wondering how to implement this. Well, you’ll learn it right in his next tip 🙂
As you read a moment ago, XSS attacks are not easy to avoid. We can always do something to defend against them and that is why Stefan mentions the CSP header.
This header refers to the Content Security Policy. CSP allows you to enable the restriction of resources that attempt to run from invalid or unverified sources.
“CSP implements the *𝘴𝘢𝘮𝘦-𝘰𝘳𝘪𝘨𝘪𝘯 𝘱𝘰𝘭𝘪𝘤𝘺, ensuring that the browser only executes code from valid sources.
As developers, we can use precisely-defined CSPs to eliminate common attack vectors by defining the content sources.”
Best of all, browser support for this Policy is almost complete:
Except for our old friend:
The Content Security Policy has 21 different directives that we can use (one or several) to exclude the loading of resources of unknown origin.
Stefan has grouped these policies into the following 4 main groups:
- Fetch directives: specify the locations for loading certain resource types: child-src, connect-src, default-src, etc.
- Document directives: help control the properties of the working environment or document where a policy will be effective: sandbox, base-uri.
- Navigation directives: These directives govern the locations of a form submission or where the document initiates any navigations: form-action, frame-ancestors.
- 𝐑𝐞𝐩𝐨𝐫𝐭𝐢𝐧𝐠 𝐝𝐢𝐫𝐞𝐜𝐭𝐢𝐯𝐞𝐬 — These directives govern how CSP violations are documented and reported: report-to, report-uri.
A good usage example — the default— is to allow only content from the same source as the site. This directive is the
Content-Security-Policy: default-src ‘self’
And in this way it would be implemented, as Stefan shows:
This policy may work in most cases, but you may need more customization. In that case consult the Content Security Policy docs to learn about its possibilities and adapt it to your requirements.
🧠Curious fact: I didn’t know this either until I found out about it from Stefan:
“CSP was first designed to reduce the attack surface of Cross Site Scripting (XSS) attacks, later versions of the spec also protect against other forms of attack such as 𝐂𝐥𝐢𝐜𝐤 𝐉𝐚𝐜𝐤𝐢𝐧𝐠 attacks”
Yes, you read that right. It is also possible to avoid Clickjacking attacks with CSP.
The question is: how can this be achieved? Read on and you will find out.
As you read, CSP was primarily designed to prevent XSS attacks but was later adapted to prevent Clickjacking as well.
Clickjacking attacks — also called UI redressing — modify the web UI by getting the user to click on a button that appears to perform one action but then performs another — usually malicious.
An example always helps more when it comes to understanding how it works and our good Stefan knows this well! It couldn’t be better explained:
An attacker who builds a web site that has a button on it that says “Free iPhone — Click here!”.
However, on top of that web page, the attacker has loaded an iframe with your mail account, and lined up exactly the “Delete all messages” button directly on top of the “Free iPhone — Click here!” button.
The victim tries to click on the “Free iPhone” button but instead actually clicked on the invisible “Delete all messages” button.
In essence, the attacker has “hijacked” the user’s click, hence the name “Clickjacking”.
We all know that person who would click on a “Free iPhone” button so let’s see how we can prevent bad things from happening to them.
Wait, you have a page with a “Free iPhone” button? Hope it’s a giveaway..
The solution is to use the
X-Frame-Options HTTP response header. It is used to allow or disallow the browser to render elements such as
Its syntax is similar to the previous ones:
And there are two possible directives:
X-Frame-Options: DENY: The page cannot be displayed in a frame, regardless of the site attempting to do so.
X-Frame-Options: SAMEORIGIN: The page can only be displayed if all ancestor frames are same origin to the page itself.
There is (was) a third directive to allow the page to be displayed only in a frame by url specified. This directive is (was)
ALLOW-FROM=url and I say was because it still exists but modern browsers have deprecated it, so it is no longer recommended to use it.
And that’s how easily Stefan implements it:
Very simple but useful.
HSTS Response Header
You know that when working on the security of a site the most important thing is the HTTPS connection. This is not new and Stefan knows it well.
The HTTPS connection is the secure version of HTTP and is used to transmit information between the web page and the browser.
It is possible to bypass HTTPS connections and send data over HTTP without encryption. To ensure that all communication is always sent over HTTPS, you need to use HSTS.
HSTS helps guarantee that all future requests will be sent over secure channels, so your data will remain encrypted and protected from attackers. HSTS also helps verify the identity of the server, which further protects your data from man-in-the-middle attacks.
Stefan’s recommended policy is
And the syntax:
Strict-Transport-Policy: max-age: 31536000; includeSubDomains; preload
The Strict-Transport-Security documentation explains the parameters:
max-age=<expire-time>: The time, in seconds, that the browser should remember that a site is only to be accessed using HTTPS.
includeSubDomains: If this optional parameter is specified, this rule applies to all of the site’s subdomains as well.
preload: Alist of domains baked into Chrome that get Strict Transport Security enabled automatically, even for the first visit.
With the theory explained, let’s move on to practice. Stefan implements Strict-Transport-Security like this:
The implementation remains as simple as in the previous ones. Stefan and I know that this is not the only way to configure HSTS in .NET. What way do you use to do it? Leave a comment!
Thanks again to Stefan Djokic for sharing these tips and bringing value to the great and wonderful community of .NET developers. If you liked them I would recommend you to follow him on Linkedin because he is always active and uploads a lot of valuable .NET content!