In this post we take a look at what is Cross-Site Request Forgery (CSRF) and how to mitigate it in Blazor WASM.
Published on September 29, 2020 by Coding Flamingo
Blazor Cross-Site Request Forgery CSRF Security Tutorial
3 min READ
In this post we are going to look at how to mitigate Cross-Site Request Forgery in your blazor site.
Cross-Site Request Forgery is a type of exploit of a website where unauthrized commands are submitted from a user that the web application trusts. Which means that a bad actor phises or somehow gets a user to go to thier site where the site responds with a script that tells the browser to use the save credential to your site and send a request to your site that the bad actor needs.
In the diagram below you can see this scenario playing out with the bad actor stealing money from the user without the user knowing.
To mitigate this, on pages were you do an action that affects the server status (for example a post or a delete), the server should send a cookie if you are in that page when you send the request you attached that cookie back so the server knows the request is coming from your site and not from a bad site.
coming soon :)
In Startup.cs we are going to first add at the top the following dependencies:
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http;
then under your public void ConfigureServices(IServiceCollection services)
we are going to add the following service:
services.AddAntiforgery(options =>
{
options.HeaderName = "X-CSRF-TOKEN";
});
then to the Configure method (usually public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
we are gping to add an antiforgery input so it looks like this: public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery)
and inside of it we are going to add:
app.Use(next => context =>
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false });
return next(context);
});
That concludes the startup.cs code. To protect your controllers you can add this [ValidateAntiForgeryToken]
or [AutoValidateAntiforgeryToken]
(the only difference is that [AutoValidateAntiforgeryToken]
will only validate the token in http methods that ar at risk aka exclude: GET, HEAD, OPTIONS and TRACE) to each route you want to protect. For Example:
[AutoValidateAntiforgeryToken]
[HttpPost]
public IEnumerable<WeatherForecast> Post()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
}) .ToArray();
}
Or you can put it at the top of your controller:
[ApiController]
[Route("[controller]")]
[Authorize]
[AutoValidateAntiforgeryToken]
public class WeatherForecastController : ControllerBase
{
//controller code
}
First we are going to add a getcookie.js
fie inside the wwwroot folder with the following code:
function getCookie(cname) {
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for (var i = 0; i < ca.length; i++) {
var arr = ca[i].split('=');
if (arr[0] == cname)
return arr[1]
}
return "";
}
and add the refference to that script in the index.html
file insde the body tag:
<script src="getcookie.js"></script>
Then in each page you are using it you can get it by
@inject IJSRuntime _jSRuntime
for razor page or [Inject] IJSRuntime _jSRuntime { get; set; }
if you are breaking up your controller code into a Component Base with all your C# code.string csrfCookieValue = await _jSRuntime.InvokeAsync<string>("getCookie", "XSRF-TOKEN");
_httpClient
is the name of the HttpClient you inject to your page):HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, url);
HttpResponseMessage response;
if (!string.IsNullOrWhiteSpace(csrfCookieValue))
{
requestMessage.Headers.Add("X-CSRF-TOKEN", csrfCookieValue);
}
response = await _httpClient.SendAsync(requestMessage);
Comments
No comments found for this article.
Join the discussion for this article by commenting in this ticket. Comments appear on this page instantly.