* Add logging to Antiforgery

This commit is contained in:
ryanbrandenburg 2016-02-05 15:18:56 -08:00
parent 08cf13b870
commit c8a9ecc0c1
4 changed files with 141 additions and 23 deletions

View File

@ -0,0 +1,77 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Antiforgery.Internal
{
internal static class AntiforgeryLoggerExtensions
{
private static readonly Action<ILogger, string, Exception> _validationFailed;
private static readonly Action<ILogger, Exception> _validated;
private static readonly Action<ILogger, string, Exception> _missingCookieToken;
private static readonly Action<ILogger, string, string, Exception> _missingRequestToken;
private static readonly Action<ILogger, Exception> _newCookieToken;
private static readonly Action<ILogger, Exception> _reusedCookieToken;
static AntiforgeryLoggerExtensions()
{
_validationFailed = LoggerMessage.Define<string>(
LogLevel.Warning,
1,
"Antiforgery validation failed with message '{Message}'.");
_validated = LoggerMessage.Define(
LogLevel.Debug,
2,
"Antiforgery successfully validated a request.");
_missingCookieToken = LoggerMessage.Define<string>(
LogLevel.Warning,
3,
"The required antiforgery cookie '{CookieName}' is not present.");
_missingRequestToken = LoggerMessage.Define<string, string>(
LogLevel.Warning,
4,
"The required antiforgery request token was not provided in either form field '{FormFieldName}' "
+ "or header '{HeaderName}'.");
_newCookieToken = LoggerMessage.Define(
LogLevel.Debug,
5,
"A new antiforgery cookie token was created.");
_reusedCookieToken = LoggerMessage.Define(
LogLevel.Debug,
6,
"An antiforgery cookie token was reused.");
}
public static void ValidationFailed(this ILogger logger, string message)
{
_validationFailed(logger, message, null);
}
public static void ValidatedAntiforgeryToken(this ILogger logger)
{
_validated(logger, null);
}
public static void MissingCookieToken(this ILogger logger, string cookieName)
{
_missingCookieToken(logger, cookieName, null);
}
public static void MissingRequestToken(this ILogger logger, string formFieldName, string headerName)
{
_missingRequestToken(logger, formFieldName, headerName, null);
}
public static void NewCookieToken(this ILogger logger)
{
_newCookieToken(logger, null);
}
public static void ReusedCookieToken(this ILogger logger)
{
_reusedCookieToken(logger, null);
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Antiforgery.Internal namespace Microsoft.AspNetCore.Antiforgery.Internal
@ -19,17 +20,20 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
private readonly IAntiforgeryTokenGenerator _tokenGenerator; private readonly IAntiforgeryTokenGenerator _tokenGenerator;
private readonly IAntiforgeryTokenSerializer _tokenSerializer; private readonly IAntiforgeryTokenSerializer _tokenSerializer;
private readonly IAntiforgeryTokenStore _tokenStore; private readonly IAntiforgeryTokenStore _tokenStore;
private readonly ILogger<DefaultAntiforgery> _logger;
public DefaultAntiforgery( public DefaultAntiforgery(
IOptions<AntiforgeryOptions> antiforgeryOptionsAccessor, IOptions<AntiforgeryOptions> antiforgeryOptionsAccessor,
IAntiforgeryTokenGenerator tokenGenerator, IAntiforgeryTokenGenerator tokenGenerator,
IAntiforgeryTokenSerializer tokenSerializer, IAntiforgeryTokenSerializer tokenSerializer,
IAntiforgeryTokenStore tokenStore) IAntiforgeryTokenStore tokenStore,
ILoggerFactory loggerFactory)
{ {
_options = antiforgeryOptionsAccessor.Value; _options = antiforgeryOptionsAccessor.Value;
_tokenGenerator = tokenGenerator; _tokenGenerator = tokenGenerator;
_tokenSerializer = tokenSerializer; _tokenSerializer = tokenSerializer;
_tokenStore = tokenStore; _tokenStore = tokenStore;
_logger = loggerFactory.CreateLogger<DefaultAntiforgery>();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -46,6 +50,11 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
if (tokenSet.IsNewCookieToken) if (tokenSet.IsNewCookieToken)
{ {
SaveCookieTokenAndHeader(httpContext, tokenSet.CookieToken); SaveCookieTokenAndHeader(httpContext, tokenSet.CookieToken);
_logger.NewCookieToken();
}
else
{
_logger.ReusedCookieToken();
} }
return Serialize(tokenSet); return Serialize(tokenSet);
@ -76,8 +85,14 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
CheckSSLConfig(httpContext); CheckSSLConfig(httpContext);
var tokens = await _tokenStore.GetRequestTokensAsync(httpContext); var tokens = await _tokenStore.GetRequestTokensAsync(httpContext);
if (tokens.CookieToken == null || tokens.RequestToken == null) if (tokens.CookieToken == null)
{ {
_logger.MissingCookieToken(_options.CookieName);
return false;
}
if (tokens.RequestToken == null)
{
_logger.MissingRequestToken(_options.FormFieldName, _options.HeaderName);
return false; return false;
} }
@ -87,11 +102,22 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
// Validate // Validate
string message; string message;
return _tokenGenerator.TryValidateTokenSet( var result = _tokenGenerator.TryValidateTokenSet(
httpContext, httpContext,
deserializedCookieToken, deserializedCookieToken,
deserializedRequestToken, deserializedRequestToken,
out message); out message);
if (result)
{
_logger.ValidatedAntiforgeryToken();
}
else
{
_logger.ValidationFailed(message);
}
return result;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -133,6 +159,8 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
} }
ValidateTokens(httpContext, tokens); ValidateTokens(httpContext, tokens);
_logger.ValidatedAntiforgeryToken();
} }
private void ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet) private void ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)

View File

@ -6,6 +6,9 @@ using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal; using Microsoft.AspNetCore.Http.Internal;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
@ -18,13 +21,12 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
public async Task ChecksSSL_ValidateRequestAsync_Throws() public async Task ChecksSSL_ValidateRequestAsync_Throws()
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = GetHttpContext();
var options = new AntiforgeryOptions() var options = new AntiforgeryOptions()
{ {
RequireSsl = true RequireSsl = true
}; };
var antiforgery = GetAntiforgery(httpContext, options);
var antiforgery = GetAntiforgery(options);
// Act & Assert // Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>( var exception = await Assert.ThrowsAsync<InvalidOperationException>(
@ -39,13 +41,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
public async Task ChecksSSL_IsRequestValidAsync_Throws() public async Task ChecksSSL_IsRequestValidAsync_Throws()
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = GetHttpContext();
var options = new AntiforgeryOptions() var options = new AntiforgeryOptions()
{ {
RequireSsl = true RequireSsl = true
}; };
var antiforgery = GetAntiforgery(options); var antiforgery = GetAntiforgery(httpContext, options);
// Act & Assert // Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>( var exception = await Assert.ThrowsAsync<InvalidOperationException>(
@ -60,13 +62,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
public void ChecksSSL_GetAndStoreTokens_Throws() public void ChecksSSL_GetAndStoreTokens_Throws()
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = GetHttpContext();
var options = new AntiforgeryOptions() var options = new AntiforgeryOptions()
{ {
RequireSsl = true RequireSsl = true
}; };
var antiforgery = GetAntiforgery(options); var antiforgery = GetAntiforgery(httpContext, options);
// Act & Assert // Act & Assert
var exception = Assert.Throws<InvalidOperationException>( var exception = Assert.Throws<InvalidOperationException>(
@ -81,13 +83,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
public void ChecksSSL_GetTokens_Throws() public void ChecksSSL_GetTokens_Throws()
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = GetHttpContext();
var options = new AntiforgeryOptions() var options = new AntiforgeryOptions()
{ {
RequireSsl = true RequireSsl = true
}; };
var antiforgery = GetAntiforgery(options); var antiforgery = GetAntiforgery(httpContext, options);
// Act & Assert // Act & Assert
var exception = Assert.Throws<InvalidOperationException>( var exception = Assert.Throws<InvalidOperationException>(
@ -102,13 +104,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
public void ChecksSSL_SetCookieTokenAndHeader_Throws() public void ChecksSSL_SetCookieTokenAndHeader_Throws()
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = GetHttpContext();
var options = new AntiforgeryOptions() var options = new AntiforgeryOptions()
{ {
RequireSsl = true RequireSsl = true
}; };
var antiforgery = GetAntiforgery(options); var antiforgery = GetAntiforgery(httpContext, options);
// Act & Assert // Act & Assert
var exception = Assert.Throws<InvalidOperationException>( var exception = Assert.Throws<InvalidOperationException>(
@ -290,12 +292,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
out message)) out message))
.Returns(false) .Returns(false)
.Verifiable(); .Verifiable();
var antiforgery = GetAntiforgery(context);
var antiforgery = new DefaultAntiforgery(
new TestOptionsManager(),
context.TokenGenerator.Object,
context.TokenSerializer.Object,
context.TokenStore.Object);
// Act & assert // Act & assert
var exception = await Assert.ThrowsAsync<AntiforgeryValidationException>( var exception = await Assert.ThrowsAsync<AntiforgeryValidationException>(
@ -458,6 +455,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
} }
private DefaultAntiforgery GetAntiforgery( private DefaultAntiforgery GetAntiforgery(
HttpContext httpContext,
AntiforgeryOptions options = null, AntiforgeryOptions options = null,
IAntiforgeryTokenGenerator tokenGenerator = null, IAntiforgeryTokenGenerator tokenGenerator = null,
IAntiforgeryTokenSerializer tokenSerializer = null, IAntiforgeryTokenSerializer tokenSerializer = null,
@ -469,16 +467,29 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
optionsManager.Value = options; optionsManager.Value = options;
} }
var loggerFactory = httpContext.RequestServices.GetRequiredService<ILoggerFactory>();
return new DefaultAntiforgery( return new DefaultAntiforgery(
antiforgeryOptionsAccessor: optionsManager, antiforgeryOptionsAccessor: optionsManager,
tokenGenerator: tokenGenerator, tokenGenerator: tokenGenerator,
tokenSerializer: tokenSerializer, tokenSerializer: tokenSerializer,
tokenStore: tokenStore); tokenStore: tokenStore,
loggerFactory: loggerFactory);
}
private IServiceProvider GetServices()
{
var builder = new ServiceCollection();
builder.AddSingleton<ILoggerFactory>(new LoggerFactory());
return builder.BuildServiceProvider();
} }
private HttpContext GetHttpContext() private HttpContext GetHttpContext()
{ {
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.RequestServices = GetServices();
httpContext.User = new ClaimsPrincipal(new ClaimsIdentity("some-auth")); httpContext.User = new ClaimsPrincipal(new ClaimsIdentity("some-auth"));
return httpContext; return httpContext;
} }
@ -486,6 +497,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
private DefaultAntiforgery GetAntiforgery(AntiforgeryMockContext context) private DefaultAntiforgery GetAntiforgery(AntiforgeryMockContext context)
{ {
return GetAntiforgery( return GetAntiforgery(
context.HttpContext,
context.Options, context.Options,
context.TokenGenerator?.Object, context.TokenGenerator?.Object,
context.TokenSerializer?.Object, context.TokenSerializer?.Object,
@ -630,4 +642,4 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
public AntiforgeryOptions Value { get; set; } = new AntiforgeryOptions(); public AntiforgeryOptions Value { get; set; } = new AntiforgeryOptions();
} }
} }
} }

View File

@ -8,6 +8,7 @@
"Microsoft.AspNetCore.Http": "1.0.0-*", "Microsoft.AspNetCore.Http": "1.0.0-*",
"Microsoft.AspNetCore.Testing": "1.0.0-*", "Microsoft.AspNetCore.Testing": "1.0.0-*",
"Microsoft.Extensions.DependencyInjection": "1.0.0-*", "Microsoft.Extensions.DependencyInjection": "1.0.0-*",
"Microsoft.Extensions.Logging": "1.0.0-*",
"Microsoft.Extensions.WebEncoders": "1.0.0-*", "Microsoft.Extensions.WebEncoders": "1.0.0-*",
"Microsoft.NETCore.Platforms": "1.0.1-*", "Microsoft.NETCore.Platforms": "1.0.1-*",
"xunit": "2.1.0-*" "xunit": "2.1.0-*"
@ -19,7 +20,7 @@
"frameworks": { "frameworks": {
"dnx451": { "dnx451": {
"frameworkAssemblies": { "frameworkAssemblies": {
"System.Threading.Tasks": "" "System.Threading.Tasks": ""
}, },
"dependencies": { "dependencies": {
"Moq": "4.2.1312.1622", "Moq": "4.2.1312.1622",