Make IsRequestValid check HTTP method

This code was popping up everywhere this method is called. Seems bad to
duplicate it. Really what the caller wants to know is 'is the request
valid or a potential CSRF exploit?'. This gets the API closer to that.
This commit is contained in:
Ryan Nowak 2016-02-12 15:56:07 -08:00
parent 668b67170f
commit ac107b5371
3 changed files with 84 additions and 3 deletions

View File

@ -36,12 +36,13 @@ namespace Microsoft.AspNetCore.Antiforgery
AntiforgeryTokenSet GetTokens(HttpContext httpContext);
/// <summary>
/// Asynchronously returns a value indicating whether the request contains a valid antiforgery token.
/// Asynchronously returns a value indicating whether the request passes antiforgery validation. If the
/// request uses a safe HTTP method (GET, HEAD, OPTIONS, TRACE), the antiforgery token is not validated.
/// </summary>
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
/// <returns>
/// A <see cref="Task{bool}"/> that, when completed, returns <c>true</c> if the request contains a
/// valid antiforgery token, otherwise returns <c>false</c>.
/// A <see cref="Task{bool}"/> that, when completed, returns <c>true</c> if the is requst uses a safe HTTP
/// method or contains a value antiforgery token, otherwise returns <c>false</c>.
/// </returns>
Task<bool> IsRequestValidAsync(HttpContext httpContext);

View File

@ -95,6 +95,16 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
CheckSSLConfig(httpContext);
var method = httpContext.Request.Method;
if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase) ||
string.Equals(method, "HEAD", StringComparison.OrdinalIgnoreCase) ||
string.Equals(method, "OPTIONS", StringComparison.OrdinalIgnoreCase) ||
string.Equals(method, "TRACE", StringComparison.OrdinalIgnoreCase))
{
// Validation not needed for these request types.
return true;
}
var tokens = await _tokenStore.GetRequestTokensAsync(httpContext);
if (tokens.CookieToken == null)
{

View File

@ -448,6 +448,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
// Arrange
var contextAccessor = new DefaultAntiforgeryContextAccessor();
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
context.HttpContext.Request.Method = "POST";
string message;
context.TokenGenerator
@ -490,6 +491,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
},
};
var context = CreateMockContext(new AntiforgeryOptions(), contextAccessor: contextAccessor);
context.HttpContext.Request.Method = "POST";
string message;
context.TokenGenerator
@ -519,6 +521,74 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
Times.Never);
}
[Theory]
[InlineData("GeT")]
[InlineData("HEAD")]
[InlineData("options")]
[InlineData("TrAcE")]
public async Task IsRequestValidAsync_SkipsAntiforgery_ForSafeHttpMethods(string httpMethod)
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
context.HttpContext.Request.Method = httpMethod;
string message;
context.TokenGenerator
.Setup(o => o.TryValidateTokenSet(
context.HttpContext,
It.IsAny<AntiforgeryToken>(),
It.IsAny<AntiforgeryToken>(),
out message))
.Returns(false)
.Verifiable();
var antiforgery = GetAntiforgery(context);
// Act
var result = await antiforgery.IsRequestValidAsync(context.HttpContext);
// Assert
Assert.True(result);
context.TokenGenerator
.Verify(o => o.TryValidateTokenSet(
context.HttpContext,
It.IsAny<AntiforgeryToken>(),
It.IsAny<AntiforgeryToken>(),
out message),
Times.Never);
}
[Theory]
[InlineData("PUT")]
[InlineData("post")]
[InlineData("Delete")]
[InlineData("Custom")]
public async Task IsRequestValidAsync_ValidatesAntiforgery_ForNonSafeHttpMethods(string httpMethod)
{
// Arrange
var context = CreateMockContext(new AntiforgeryOptions());
context.HttpContext.Request.Method = httpMethod;
string message;
context.TokenGenerator
.Setup(o => o.TryValidateTokenSet(
context.HttpContext,
It.IsAny<AntiforgeryToken>(),
It.IsAny<AntiforgeryToken>(),
out message))
.Returns(true)
.Verifiable();
var antiforgery = GetAntiforgery(context);
// Act
var result = await antiforgery.IsRequestValidAsync(context.HttpContext);
// Assert
Assert.True(result);
context.TokenGenerator.Verify();
}
[Fact]
public async Task ValidateRequestAsync_FromStore_Failure()
{