clean up antiforgery

This commit is contained in:
Shannon
2020-05-27 11:28:25 +10:00
parent ef02571c8d
commit ac57aeb066
3 changed files with 73 additions and 63 deletions

View File

@@ -36,7 +36,7 @@ namespace Umbraco.Extensions
&& context.HttpContext.Request.Cookies.TryGetValue(Constants.Web.CsrfValidationCookieName, out var csrfCookieVal))
{
//if they are not valid for some strange reason - we need to continue setting valid ones
var valResult = await _antiforgery.ValidateHeadersAsync(context.HttpContext);
var valResult = await _antiforgery.ValidateRequestAsync(context.HttpContext);
if (valResult.Success)
{
await next();

View File

@@ -1,18 +1,10 @@
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Hosting;
namespace Umbraco.Web.BackOffice.Security
{
@@ -38,48 +30,8 @@ namespace Umbraco.Web.BackOffice.Security
_antiforgeryOptions = antiforgeryOptions;
}
public async Task<bool> ValidateTokensAsync(HttpContext httpContext, string cookieToken, string headerToken)
{
// We need to do some tricks here, save the initial cookie vals, then reset later
var originalCookies = httpContext.Request.Cookies;
var originalCookiesHeader = httpContext.Request.Headers[HeaderNames.Cookie];
var originalHeader = httpContext.Request.Headers[_antiforgeryOptions.Value.HeaderName];
//var originalForm = httpContext.Request.Form;
try
{
// this is how you write to the request cookies, it's the only way
var cookieHeaderVals = CookieHeaderValue.ParseList(originalCookiesHeader);
cookieHeaderVals.Add(new CookieHeaderValue(_antiforgeryOptions.Value.Cookie.Name, cookieToken));
httpContext.Request.Headers[HeaderNames.Cookie] = cookieHeaderVals.Select(c => c.ToString()).ToArray();
// change the header/form val to ours
//var newForm = httpContext.Request.Form.ToDictionary(x => x.Key, x => x.Value);
//newForm.Add(_antiforgeryOptions.Value.FormFieldName, headerToken);
//httpContext.Request.Form = new FormCollection(newForm);
httpContext.Request.Headers[_antiforgeryOptions.Value.HeaderName] = headerToken;
return await _defaultAntiforgery.IsRequestValidAsync(httpContext);
}
finally
{
// reset
var cookieHeaderVals = CookieHeaderValue.ParseList(originalCookiesHeader);
httpContext.Request.Headers[HeaderNames.Cookie] = cookieHeaderVals.Select(c => c.ToString()).ToArray();
if (originalHeader.Count > 0)
httpContext.Request.Headers[_antiforgeryOptions.Value.HeaderName] = originalHeader;
//httpContext.Request.Form = originalForm;
}
}
/// <summary>
/// Validates the headers/cookies passed in for the request
/// </summary>
/// <param name="requestHeaders"></param>
/// <param name="failedReason"></param>
/// <returns></returns>
public async Task<Attempt<string>> ValidateHeadersAsync(HttpContext httpContext)
/// <inheritdoc />
public async Task<Attempt<string>> ValidateRequestAsync(HttpContext httpContext)
{
httpContext.Request.Cookies.TryGetValue(Constants.Web.CsrfValidationCookieName, out var cookieToken);
@@ -88,6 +40,61 @@ namespace Umbraco.Web.BackOffice.Security
cookieToken == null ? null : cookieToken);
}
/// <inheritdoc />
public void GetTokens(HttpContext httpContext, out string cookieToken, out string headerToken)
{
var set = _defaultAntiforgery.GetTokens(httpContext);
cookieToken = set.CookieToken;
headerToken = set.RequestToken;
}
/// <summary>
/// Validates the cookie and header tokens
/// </summary>
/// <param name="httpContext"></param>
/// <param name="cookieToken"></param>
/// <param name="headerToken"></param>
/// <returns></returns>
private async Task<bool> ValidateTokensAsync(HttpContext httpContext, string cookieToken, string headerToken)
{
// TODO: see https://github.com/dotnet/aspnetcore/issues/22217
// An alternative way to doing this would be to create a separate container specifically for antiforgery and add custom options to
// it and resolve services directly from there. Could be worth a shot and could actually be a way for us to deal with these global
// things later on if this problem arises again.
// We need to do some tricks here, save the initial cookie/header vals, then reset later
var originalCookies = httpContext.Request.Cookies;
var originalCookiesHeader = httpContext.Request.Headers[HeaderNames.Cookie];
var originalHeader = httpContext.Request.Headers[_antiforgeryOptions.Value.HeaderName];
try
{
// swap the cookie anti-forgery cookie value for the one we want to validate
// (this is how you modify request cookies, it's the only way)
var cookieHeaderVals = CookieHeaderValue.ParseList(originalCookiesHeader);
cookieHeaderVals.Add(new CookieHeaderValue(_antiforgeryOptions.Value.Cookie.Name, cookieToken));
httpContext.Request.Headers[HeaderNames.Cookie] = cookieHeaderVals.Select(c => c.ToString()).ToArray();
// swap the anti-forgery header value for the one we want to validate
httpContext.Request.Headers[_antiforgeryOptions.Value.HeaderName] = headerToken;
// now validate
return await _defaultAntiforgery.IsRequestValidAsync(httpContext);
}
finally
{
// reset
// change request cookies back to original
var cookieHeaderVals = CookieHeaderValue.ParseList(originalCookiesHeader);
httpContext.Request.Headers[HeaderNames.Cookie] = cookieHeaderVals.Select(c => c.ToString()).ToArray();
// change the header back to normal
httpContext.Request.Headers[_antiforgeryOptions.Value.HeaderName] = originalHeader;
}
}
private async Task<Attempt<string>> ValidateHeadersAsync(
HttpContext httpContext,
string cookieToken)
@@ -114,14 +121,5 @@ namespace Umbraco.Web.BackOffice.Security
return Attempt<string>.Succeed();
}
public void GetTokens(HttpContext httpContext, out string cookieToken, out string headerToken)
{
var set = _defaultAntiforgery.GetTokens(httpContext);
cookieToken = set.RequestToken;
headerToken = set.RequestToken;
}
}
}

View File

@@ -8,10 +8,22 @@ namespace Umbraco.Web.BackOffice.Security
/// <summary>
/// Antiforgery implementation for the Umbraco back office
/// </summary>
public interface IBackOfficeAntiforgery //: IAntiforgery
public interface IBackOfficeAntiforgery
{
Task<Attempt<string>> ValidateHeadersAsync(HttpContext httpContext);
Task<bool> ValidateTokensAsync(HttpContext httpContext, string cookieToken, string headerToken);
/// <summary>
/// Validates the headers/cookies passed in for the request
/// </summary>
/// <param name="requestHeaders"></param>
/// <param name="failedReason"></param>
/// <returns></returns>
Task<Attempt<string>> ValidateRequestAsync(HttpContext httpContext);
/// <summary>
/// Generates tokens to use for the cookie and header antiforgery values
/// </summary>
/// <param name="httpContext"></param>
/// <param name="cookieToken"></param>
/// <param name="headerToken"></param>
void GetTokens(HttpContext httpContext, out string cookieToken, out string headerToken);
}
}