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()
{