diff --git a/src/Microsoft.AspNetCore.Antiforgery/IAntiforgery.cs b/src/Microsoft.AspNetCore.Antiforgery/IAntiforgery.cs index 0cdcf35a8b..8d62f67ec1 100644 --- a/src/Microsoft.AspNetCore.Antiforgery/IAntiforgery.cs +++ b/src/Microsoft.AspNetCore.Antiforgery/IAntiforgery.cs @@ -36,12 +36,13 @@ namespace Microsoft.AspNetCore.Antiforgery AntiforgeryTokenSet GetTokens(HttpContext httpContext); /// - /// 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. /// /// The associated with the current request. /// - /// A that, when completed, returns true if the request contains a - /// valid antiforgery token, otherwise returns false. + /// A that, when completed, returns true if the is requst uses a safe HTTP + /// method or contains a value antiforgery token, otherwise returns false. /// Task IsRequestValidAsync(HttpContext httpContext); diff --git a/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgery.cs b/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgery.cs index a22576bd0b..7788a2bb0b 100644 --- a/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgery.cs +++ b/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgery.cs @@ -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) { diff --git a/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTest.cs b/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTest.cs index 6fd193db1a..670b7f607b 100644 --- a/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTest.cs +++ b/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTest.cs @@ -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(), + It.IsAny(), + 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(), + It.IsAny(), + 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(), + It.IsAny(), + 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() {