I have create a ASP.Net Core 2 application using IAntiforgery
apis.
This provides a method to return a cookie which it does.
The client takes that cookie and on subsequent POST requests it puts the value in a X-XSRF-TOKEN header.
Middleware validates this and allows the request to continue or not if it fails.
With the correct cookies and headers sent in the request always fails validation and I don't understand why.
The whole reproduction is here https://github.com/jchannon/AntiForgery
However, the main problem area is below.
public class Startup
{
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery, ILoggerFactory loggerFactory)
{
app.UseAuthentication();
app.Use(async (context, next) =>
{
var logger = loggerFactory.CreateLogger("ValidRequestMW");
//Don't validate POST for login
if (context.Request.Path.Value.Contains("login"))
{
await next();
return;
}
logger.LogInformation(context.Request.Cookies["XSRF-TOKEN"]);
logger.LogInformation(context.Request.Headers["X-XSRF-TOKEN"]);
//On POST requests it will validate the XSRF header
if (!await antiforgery.IsRequestValidAsync(context))
{
/****************************************************
*
*
* For some reason when the cookie and the header are sent in on the /create POST this validation always fails
*
*
***************************************************/
context.Response.StatusCode = 401;
logger.LogError("INVALID XSRF TOKEN");
return;
}
await next();
});
app.UseRouter(r =>
{
r.MapGet("", async context => { await context.Response.WriteAsync("hello world"); });
//This returns a XSRF-TOKEN cookie
//Client will take this value and add it as a X-XSRF-TOKEN header and POST to /create
r.MapPost("login", async (context) =>
{
antiforgery.SetCookieTokenAndHeader(context);
context.Response.Redirect("/");
});
//If XSRF validaiton is correct we should hit this route
r.MapPost("create", async context =>
{
context.Response.StatusCode = 201;
await context.Response.WriteAsync("Created");
});
});
}
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(x => x.AddConsole());
services.AddAntiforgery(options =>
{
options.HeaderName = "X-XSRF-TOKEN";
options.Cookie.Name = "XSRF-TOKEN";
options.Cookie.HttpOnly = false;
});
// services.AddAuthentication("MyCookieMW")
// .AddCookie("MyCookieMW", cookieOptions =>
// {
// cookieOptions.Cookie.Name = "MyCookie";
// cookieOptions.Cookie.HttpOnly = true;
// cookieOptions.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
// cookieOptions.SlidingExpiration = true;
// });
services.AddRouting();
}
}
In ASP.Net Core anti forgery token is automatically added to forms, so you don't need to add @Html. AntiForgeryToken() if you use razor form element or if you use IHtmlHelper. BeginForm and if the form's method isn't GET. And when user submits form this token is verified on server side if validation is enabled.
Validates that input data from an HTML form field comes from the user who submitted the data. Obsolete. Validates that input data from an HTML form field comes from the user who submitted the data and lets callers specify additional validation details.
Anti-Forgery Tokens The client requests an HTML page that contains a form. The server includes two tokens in the response. One token is sent as a cookie. The other is placed in a hidden form field. The tokens are generated randomly so that an adversary cannot guess the values.
ValidateAntiForgeryToken: The ValidateAntiForgeryToken attribute is used to prevent cross-site request forgery attacks.
So after diving into the source code of antiforgery and some badly named methods (SetCookieTokenAndHeader
I'm looking at you). The correct code should be:
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery, ILoggerFactory loggerFactory)
{
app.Use(async (context, next) =>
{
var logger = loggerFactory.CreateLogger("ValidRequestMW");
//Don't validate POST for login
if (context.Request.Path.Value.Contains("login"))
{
await next();
return;
}
logger.LogInformation("Request Cookie is " + context.Request.Cookies["XSRF-TOKEN"]);
logger.LogInformation("Request Header is " + context.Request.Headers["X-XSRF-TOKEN"]);
//On POST requests it will validate the XSRF header
if (!await antiforgery.IsRequestValidAsync(context))
{
context.Response.StatusCode = 401;
logger.LogError("INVALID XSRF TOKEN");
return;
}
await next();
});
app.UseRouter(r =>
{
r.MapGet("", async context => { await context.Response.WriteAsync("hello world"); });
//This returns a XSRF-TOKEN cookie
//Client will take this value and add it as a X-XSRF-TOKEN header and POST to /create
r.MapPost("login", async (context) =>
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
context.Response.Redirect("/");
});
//If XSRF validaiton is correct we should hit this route
r.MapPost("create", async context =>
{
context.Response.StatusCode = 201;
await context.Response.WriteAsync("Created");
});
});
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IAntiforgeryTokenGenerator, MyTokenGenerator>();
services.AddSingleton<IAntiforgery, MyAntiforgery>();
services.AddLogging(x => x.AddConsole());
services.AddAntiforgery(options =>
{
options.HeaderName = "X-XSRF-TOKEN";
options.Cookie.Name = "MyAntiforgery";
options.Cookie.HttpOnly = false;
});
services.AddRouting();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With