For a brief overview of the Encrypted Token Pattern, please refer to this post.
Overview
The Encrypted Token Pattern is a defence mechanism against Cross Site Request Forgery (CSRF) attacks, and is an alternative to its sister-patterns; Synchroniser Token, and Double Submit Cookie.
Each of these patterns have the same objective:
- To ensure that any given HTTP request originated from a trustworthy source
- To uniquely identify the user that issued the HTTP request
In the first instance, the need to ensure that requests originate from a trustworthy source is an obvious requirement. Essentially, we need to guarantee that any given request has originated not only from the user’s web-browser, but also from a non-malicious link, or connection.
Why the Encrypted Token Pattern?
A Simple CSRF Attack
Consider a banking application. Suppose that the application exposes an API that allows the transfer of funds between accounts as follows:
http://mybank.com/myUserId/fromAccount/toAccount/amount
Web browsers share state, in terms of cookies, across tabs. Imagine a user that is logged into mybank.com. They open a new tab in their internet browser and navigate to a website that contains a link to the above URI. An attacker that knows the user’s bank account number could potentially transfer any given sum from the user’s account to their own. Remember that the user is already logged in to mybank.com at this point, and have an established session on the web-server, if not a persistent cookie in their web-browser. The browser simply opens a new tab, leverages the user’s logged in credentials, and executes the HTTP request on the user’s behalf.
How to Defend Against CSRF Attacks
In order to defend against such attacks, we need to introduce a token on the user’s behalf, and validate that token on the web server during HTTP requests, ensuring that each request
- Originates from a trusted source
- Uniquely identifies the user
Why uniquely identify the user? Consider that CSRF attacks can potentially originate from valid users.
A More Sophisticated CSRF Attack
John and David are both valid users of mybank.com. John decides to post a malicious link. This time, the attacks is more sophisticated. John has built a small web server that issues a HTTP request to the mybank.com money-transfer API:
http://mybank.com/myUserId/fromAccount/toAccount/amount
This time, John has supplied a valid token – his own (remember, John is also a valid user). Now, assuming that mybank.com does not validate the identity of the user in the supplied token, it will determine the request to have originated from a trusted source, and allow the transfer to take place.
The Encrypted Token Pattern
The Encrypted Token Patterns protects web applications against CSRF attacks by generating a secure token at server level, and issuing the token to the client. The token itself is essentially a JSON Web Token (JWT) composed of a unique User ID, randomly generated number (nonce), and timestamp. Given that the token is a JSON object, it is possible to include any additional metadata in the token. The process flow is as follows:
Leveraging the Encrypted Token Pattern
The Advanced Resilient Mode of Recognition (ARMOR) is a C# implementation of the Encrypted Token Pattern, available on GitHub under the MIT license that provides a means of protecting ASP.NET applications from CSRF attacks, by leveraging the Encrypted Token Pattern. The following steps describes a typical setup configuration.
ARMOR
ARMOR is a framework composed of interconnecting components exposed through custom DelegatingHandler and AuthorizationAttribute classes. ARMOR is essentially an advanced encryption and hashing mechanism, leveraging the Rijndael encryption standard, and SHA256 hashing by default, though these are concrete implementations; ARMOR provides abstractions in terms of encryption, allowing developers to leverage custom concrete implementations. ARMOR has two primary directives:
- To generate secure ARMOR tokens
- To validate secure ARMOR tokens
ARMOR Web Framework
The ARMOR Web Framework is a set of components that leverage ARMOR itself, allowing developers to leverage the ARMOR framework in a plug-and-play fashion, without necessarily grappling with the underlying complexities of encryption and hashing. This tutorial focuses on leveraging the ARMOR Web Framework in C# to protect your ASP.NET applications from CSRF attacks.
Leveraging ARMOR in ASP.NET
ARMOR Web Framework Package
Download the ARMOR Web Framework package from Nuget:
PM> Install-Package Daishi.Armor.WebFramework
Apply Configuration Settings
Add the following configuration settings to your web.config file:
<add key=“IsArmed” value=“true” /> <add key=“ArmorEncryptionKey” value=“{Encryption Key}” /> <add key=“ArmorHashKey” value=“{Hashing Key}” /> <add key=“ArmorTimeout” value=“1200000” />
IsArmed
A toggle feature easily allowing developers to turn ARMOR on or off
ArmorEncryptionKey
The encryption key that ARMOR will use to both encrypt and decrypt ARMOR tokens
ArmorHashKey
The hashing key that ARMOR will use to generate and validate hashes contained within ARMOR tokens. ARMOR implements hashes as a means of determining whether or not tokens have been tampered with, and to add an extended level of entropy to token metadata, rendering them more difficult to hijack.
ArmorTimeout
The time in milliseconds that ARMOR Tokens remain valid.
In order to facilitate encryption and hashing, ARMOR requires two keys. You can generate both keys as follows:
byte[] encryptionKey = new byte[32]; byte[] hashingKey = new byte[32]; using (var provider = new RNGCryptoServiceProvider()) { provider.GetBytes(encryptionKey); provider.GetBytes(hashingKey); }
These keys must be stored in the ArmorEncryptionKey and ArmorHashKey values in your configuration file, in Base64-format.
Hook the ARMOR Filter to your application
Core Components
Authorization Filter
The Authorization filter reads the ARMOR Token from the HttpRequest Header and validates it against the currently logged in user. Users can be authenticated in any fashion; ARMOR assumes that your user’s Claims are loaded into the current Thread at the point of validation.
The following classes facilitate authorization for both MVC and Web API projects respectively:
- MvcArmorAuthorizeAttribute
- WebApiArmorAuthorizeAttribute
Fortification Filter
The Fortification filter refreshes and re-issues new ARMOR tokens. The following classes facilitate fortification for both MVC and Web API projects respectively:
- MvcArmorFortifyFilter
- WebApiArmorFortifyFilter
Generally speaking, it’s ideal that you refresh the incoming ARMOR token for every HTTP request, whether that request validates the Token or not; particularly for GET HTTP requests. Otherwise, the Token may expire unless the user issues a POST, PUT, or DELETE request within the Token’s lifetime.
To do this, simple register the appropriate ARMOR Fortification mechanism in your MVC application,
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new MvcArmorFortifyFilter()); }
or in your Web API application:
config.Filters.Add(new WebApiArmorFortifyFilter());
Now, each HttpResponse issued by your application will contain a custom ARMOR Header containing a new ARMOR Token for use with subsequent HTTP requests:
Decorating POST, PUT, and DELETE Endpoints with ARMOR
In an MVC Controller simply decorate your endpoints as follows:
[MvcArmorAuthorize]
And in Web API Controllers:
[WebApiArmorAuthorize]
Integrating Your Application’s Authentication Mechanism
AMROR operates on the basis of Claims and provides default implementations of Claim-parsing components derived from the IdentityReader class in the following classes:
- MvcIdentityReader
- WebApiIdentityReader
Both classes return an enumerated list of Claim objects consisting of a UserId Claim. In the case of MVC, the Claim is derived from the ASP.NET intrinsic Identity.Name property, assuming that the user is already authenticated. In the case of Web API, it is assumed that you leverage an instance of ClaimsIdentity as your default IPrincipal object, and that user metadata is stored in Claims held within that ClaimsIdentity. As Such, the WebApiIdentityReader simply extracts the UserId Claim. Both UserId and Timestamp Claims are the only default Claims in an ArmorToken and are loaded upon creation.
If your application leverages a different authentication mechanism, you can simply derive from the default IdentityReader class with your own implementation and extract your logged in user’s metadata, injecting it into Claims necessary for ARMOR to manage. Here is the default Web API implementation.
public override bool TryRead(out IEnumerable<Claim> identity) { var claims = new List<Claim>(); identity = claims; var claimsIdentity = principal.Identity as ClaimsIdentity; if (claimsIdentity == null) return false; var subClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type.Equals(“UserId”)); if (subClaim == null) return false; claims.Add(subClaim); return true; }
ARMOR downcasts the intrinsic HTTP IPrincipal.Identity object as an instance of ClaimsIdentity and extracts the UserId Claim. Deriving from the IdentityReader base class allows you to implement your own mechanism to build Claims. It’s worth noting that you can store many Claims as you like in an ARMOR Token. ARMOR will decrypt and deserialise your Claims so that they can be read on the return journey back to server from UI.
Adding ARMOR UI Components
The ARMOR WebFramework contains a JavaScript file as follows:
var ajaxManager = ajaxManager || { setHeader: function(armorToken) { $.ajaxSetup({ beforeSend: function(xhr, settings) { if (settings.type !== “GET”) { xhr.setRequestHeader(“Authorization”, “ARMOR “ + armorToken); } } }); } };
The purpose of this code is to detect the HttpRequest type, and apply an ARMOR Authorization Header for POST, PUT and DELETE requests. You can leverage this on each page of your application (or in the default Layout page) as follows:
<script> $(document).ready(function () { ajaxManager.setHeader($(“#armorToken”).val()); }); $(document).ajaxSuccess(function (event, xhr, settings) { var armorToken = xhr.getResponseHeader(“ARMOR”) || $(“#armorToken”).val(); ajaxManager.setHeader(armorToken); }); </script>
As you can see, the UI contains a hidden field called “armorToken”. This field needs to be populated with an ArmorToken when the page is initially served. The following code in the ARMOR API itself facilitates this:
public bool TryFortify() { var identityReader = identityReaderFactory.Create(); IEnumerable<Claim> identity; var isAuthenticated = identityReader.TryRead(out identity); if (!isAuthenticated) return false; var claims = identity.ToList(); var userId = claims.Single(c => c.Type.Equals("UserId")).Value; var platform = claims.SingleOrDefault(c => c.Type.Equals("Platform")); var encryptionKey = ArmorSettings.EncryptionKey; var hashingKey = ArmorSettings.HashingKey; var nonceGenerator = new NonceGenerator(); nonceGenerator.Execute(); var armorToken = new ArmorToken(userId, platform == null ? "ARMOR" : platform.Value, nonceGenerator.Nonce); var armorTokenConstructor = new ArmorTokenConstructor(); var standardSecureArmorTokenBuilder = new StandardSecureArmorTokenBuilder(armorToken, encryptionKey, hashingKey); var generateSecureArmorToken = new GenerateSecureArmorToken(armorTokenConstructor, standardSecureArmorTokenBuilder); generateSecureArmorToken.Execute(); httpContext.Response.AppendHeader("ARMOR", generateSecureArmorToken.SecureArmorToken); return true; }
Here we generate the initial ARMOR Token to be served when the application loads. This Token will be leveraged by the first AJAX request and refreshed on each subsequent request. The Token is then loaded into the ViewBag object and absorbed by the associated View:
<div><input id=“armorToken” type=“hidden” value=@ViewBag.ArmorToken /></div>
Now your AJAX requests are decorated with ARMOR Authorization attributes:
Summary
Now that you’ve implemented the ARMOR WebFramework, each POST, PUT and DELETE request will persist a Rijndael-encrypted and SHA256-hashed ARMOR Token, which is validated by the server before each POST, PUT, or DELETE request decorated with the appropriate attribute is handled, and refreshed after each request completes. The simple UI components attach new ARMOR Tokens to outgoing requests and read ARMOR Tokens on incoming responses. ARMOR is designed to work seamlessly with your current authentication mechanism to protect your application from CSRF attacks.
This applies only to webforms or just the mvc framework for .NET?
It applies to ASP.NET MVC and Web API as well.
Hey Paul, why are you advocating this approach instead of the AntiForgeryToken and [ValidateAntiForgeryToken] attribute already available in the ASP.NET MVC framework?
Hi Scott, Thanks for your comment. Here are some advantages to leveraging the Encrypted Token Pattern:
It does not require server-state
It does not require cookies
It does not require two tokens
It does not require any effort on the client-side other than including the token in HTTP requests
It does not require any other application in a subdomain to be XSS-proof
Essentially, if both patterns successfully prevent CSRF attacks, then each is good as the other. Which one you implement will depend on your overall architecture, and the above points will help influence that decision.
I hope this clarifies things.
Hi Paul, really liked this alternative !
So will you target asp.net 5/mvc 6 ?
And what do you use/recommend to store enc/hash keys (concretely) ?
Thanks !
Yes, I eventually plan on an official .NET 5 release, but feel free to fork the project and upgrade the runtime assembly. It should be straightforward.
Generating and storing keys is a topic in itself. Microsoft offer one such service in Azure, while there are open source alternatives as well.
Dose ARMOR surport distribute site?
Yes. Long as the encryption keys are available across all installations.
Thanks!
Dose ARMOR Surport Distribute Site
Hey Paul,can you send me a demo of asp.net webapi work with this ARMOR ,thank you very much, my email address is linjia06@163.com ,thank’s more
Yes, here is a working application: https://github.com/daishisystems/Daishi.AMQP
Hi, i am currently researching about prevention of CSRF attacks. Since i am on a php server i can not simple use ARMOR. Currently i like your encrypted token pattern most, since it has no server-state and is works well with forms and REST, etc. I though about not encrypting the whole data package of the token, but just signed the package to ensure integrity. There are actually no sensitive data in the package (session id, timestamp and nonce). You mentioned that the format of the token is JSON Web Token. Am I right that this is also only cryptographically signed or encoded? I can see no improvement of really encrypting the data against just signing them in this case. What are your thoughts on this?
Kind regards and thanks for your work on this topic.
Matthias
Thanks Matthias, ARMOR aside, encrypting the entire package simply ensures that your internal day structures are not exposed as an attack vector. Any degree of structure can potentially provide attackers a means to exploit. IMO, it’s best practise to cover your tracks.
Encrypting the package is critical in terms of leveraging ARMOR, because the premise is that the server should successfully decrypt the UI payload in the HTTP request. If the payload can’t be decrypted, the payload is a forgery. I hope this makes sense.
It’s my understanding that the purpose of the nonce is to mitigate replay attacks, but I don’t see the nonce ever actually being explicitly used in the examples I’ve seen. Is it so the result of the encryption has sufficient entropy?
Yes, that’s it exactly.