Secure .NET Core Applications From CSRF Attack: .NET Core Security - Part Two

.Net core

You can find all of my .NET core posts here.

This is the second post on .NET Core security. The first part is here:

In this post, we will see how to secure your .NET Core applications from a CSRF attack.

What is CSRF?

CSRF stands for Cross-Site Request Forgery.

CSRF in simple words

Cross-Site Request Forgery (CSRF) is an attack where a malicious site sends a request to a vulnerable site where the user is currently logged in.

For example

  • You are logged into the sitecom with form authentication
  • The site will return an authentication cookie
  • This cookie will travel along with the user's every request
  • If the user visits a malicious sitecom, the site may contain an HTML form with submit (or any other button) button
  • If you click on the button, the form will be submitted to the X.cominstead of Y.com along with the authentication cookie
  • The hacker can do whatever you want to do using your authentication and they can post the form by running some scripts
  • This can be so dangerous when the person is logged into some banking or any other important websites.

Below, the image gives better information about CSRF,

.Net core

To prevent this, you need a token which will be included in the form and will be validated server side when the user submits the form.

Let us see how .NET Core handles this CSRF attack

If we compare .NET Core with .NET as far as the resolution of CSRF attacks is concerned, The ValidateAntiForgeryToken attribute and The AntiForgeryToken HTML helper(@Html.AntiForgeryToken()) are still there in .Net Core. On top of these, there are some more powerful options we have in .NET Core.

Let us see by creating .NET Core application.

Prerequisites

  • Visual studio 2017 community edition, download here
  • .NET Core 2.0 SDK from here  (I have written a post to install SDK here).
  • We will use the code which I created during the code first sample. The details of the code are here in this post.

Token Generation

If you open Create.cshtml page, then you will notice the below form tag:

  1. <form asp-action="Creates"

Here asp* is a tag helper and the best part is asp* tags will automatically add the token into your form when you submit the form.

For example, in below form, the __RequestVerificationToken is generated along with the form.

.Net core

Note

From .NET Core 2.0 onwards, the token would be added even if you do not have asp* tags in your form.

Also, note that simple form tag with action does not generate the token automatically. So if you have the below code in razor file then the token will not be generated automatically:

  1. <form action="Creates" >  

In such case you can generate the token by adding @Html.AntiForgeryToken()under the form as shown below:

  1. <form action="Creates" >  
  2.    @Html.AntiForgeryToken()  
  3. </form>  

This will generate the token.

Restricting auto-generation of the tokens

As .NET Core generates the token automatically along with the form tag, you can even restrict this autogeneration if you do not wish to generate this tokens.

For this you need to add asp-antiforgery=”false” in form tag as below,

  1. <form asp-action="Creates" asp-antiforgery="false">  

This will not generate the token for your form.

One more way to restrict this is using ! before the form tag as below:

  1. <!form asp-action="Creates" >  

! is called Opt-out symbol.

Token with jQuery and Ajax

If you are using AJAX call instead of using Form submit button, then you can use the below code to generate and send the token:

  1. var csrfToken = $.cookie("CSRF-TOKEN");  
  2. $.ajax({  
  3.     url: "/Employees/Creates",  
  4.     contentType: "application/json",  
  5.     data: JSON.stringify({}), //// Data here  
  6.     type: "POST",  
  7.     headers: {  
  8.         "X-CSRF-TOKEN": csrfToken  
  9.     }  
  10. });  

Validating the anti-forgery token

Generally, a controller may contain GET as well as POST action methods. POST action methods require validating the anti-forgery token and not the GET action methods. So, if the ValidateAntiforgeryToken is declared on the controller, the HTTP GET requests become invalid and would throw the error as below:

.Net core

I remember writing different code to resolve this issue. But not this .NET core. AutoValidateAntiforgeryToken will be your friend in such cases.

AutoValidateAntiforgeryToken

This is a new attribute added with .NET Core.

Sometimes it is not required to validate all the tokens, for example, the requests like:

  • GET
  • HEAD
  • OPTIONS
  • TRACE

AutoValidateAntiforgeryToken is almost similar to ValidateAntiforgeryTokenexcept for the fact that it doesn’t validate tokens on GET, HEAD, OPTIONS, and TRACErequests.

This filter can be applied either at controller level or globally.

You can add AutoValidateAntiforgeryToken filter above the action or above the controller as below:

  1. [HttpPost]  
  2. [AutoValidateAntiforgeryToken]  
  3. public async Task < IActionResult > Creates([Bind("EmployeeId,EmployeeName,EmployeeAge,DepartmentId")] Employee employee) { ...  
  4. }  
Or globally under ConfigureService method of Startup.cs class as below,

 

  1. services.AddMvc(options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));  

When you add this attribute globally, then you are not required to put the attribute above different actions as well as controllers.

ValidateAntiForgeryToken

This filter is same as we have in .NET MVC.

This filter validates the request token on each and every method it is placed on regardless of the HTTP verb. So it validates even on GET, HEAD etc requests.

We can put this filter on the action method(or above controller),

  1. [HttpPost]  
  2. [ValidateAntiForgeryToken]  
  3. public async Task < IActionResult > Creates([Bind("EmployeeId,EmployeeName,EmployeeAge,DepartmentId")] Employee employee) { ...  
  4. }  
Or Globally

 

  1. services.AddMvc(options => options.Filters.Add(new ValidateAntiForgeryTokenAttribute()));  

Note

It is advisable to use AutoValidateAntiforgeryTokenAttribute than using ValidateAntiForgeryTokenAttribute globally because if we apply ValidateAntiForgeryTokenAttribute globally then we will not receive the anti-forgery tokens for certain types of requests like GET, HEAD, TRACE etc which will cause the validation errors from those requests as I explained above.

IgnoreAntiforgeryToken

Sometimes there might be some requirements for ignoring the anti-forgery tokens or you need to ignore the tokens for specific actions of the controllers. In such a case, you can use an IgnoreAntiforgeryToken filter.

For example, if you want to ignore the tokens for any specific action of a controller then you can apply to the controller and to that action:

  1. [AutoValidateAntiforgeryToken]  
  2. public class EmployeesController: Controller {  
  3.     [HttpPost]  
  4.     [IgnoreAntiforgeryToken]  
  5.     public async Task < IActionResult > Creates([Bind("EmployeeId,EmployeeName,EmployeeAge,DepartmentId")] Employee employee) { ...  
  6.     }  
  7. }  
I hope it helps.