#263 Consume ITlsTokenBindingFeature in CookieAuthMiddleware.
This commit is contained in:
parent
42cba79e01
commit
c14119b612
|
|
@ -8,6 +8,7 @@ using System.Security.Claims;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Http.Features;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
|
@ -45,7 +46,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
return null;
|
||||
}
|
||||
|
||||
var ticket = Options.TicketDataFormat.Unprotect(cookie);
|
||||
var ticket = Options.TicketDataFormat.Unprotect(cookie, GetTlsTokenBinding());
|
||||
if (ticket == null)
|
||||
{
|
||||
Logger.LogWarning(@"Unprotect ticket failed");
|
||||
|
|
@ -175,7 +176,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
ticket = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
var cookieValue = Options.TicketDataFormat.Protect(ticket);
|
||||
var cookieValue = Options.TicketDataFormat.Protect(ticket, GetTlsTokenBinding());
|
||||
|
||||
var cookieOptions = BuildCookieOptions();
|
||||
if (ticket.Properties.IsPersistent && _renewExpiresUtc.HasValue)
|
||||
|
|
@ -244,7 +245,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
Options.ClaimsIssuer));
|
||||
ticket = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
||||
}
|
||||
var cookieValue = Options.TicketDataFormat.Protect(ticket);
|
||||
var cookieValue = Options.TicketDataFormat.Protect(ticket, GetTlsTokenBinding());
|
||||
|
||||
Options.CookieManager.AppendResponseCookie(
|
||||
Context,
|
||||
|
|
@ -357,5 +358,11 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
return true;
|
||||
|
||||
}
|
||||
|
||||
private string GetTlsTokenBinding()
|
||||
{
|
||||
var binding = Context.Features.Get<ITlsTokenBindingFeature>()?.GetProvidedTokenBindingId();
|
||||
return binding == null ? null : Convert.ToBase64String(binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ namespace Microsoft.AspNet.Authentication
|
|||
public interface ISecureDataFormat<TData>
|
||||
{
|
||||
string Protect(TData data);
|
||||
string Protect(TData data, string purpose);
|
||||
TData Unprotect(string protectedText);
|
||||
TData Unprotect(string protectedText, string purpose);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// 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 System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.AspNet.DataProtection;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication
|
||||
|
|
@ -20,14 +18,29 @@ namespace Microsoft.AspNet.Authentication
|
|||
|
||||
public string Protect(TData data)
|
||||
{
|
||||
byte[] userData = _serializer.Serialize(data);
|
||||
byte[] protectedData = _protector.Protect(userData);
|
||||
string protectedText = Base64UrlTextEncoder.Encode(protectedData);
|
||||
return protectedText;
|
||||
return Protect(data, purpose: null);
|
||||
}
|
||||
|
||||
public string Protect(TData data, string purpose)
|
||||
{
|
||||
var userData = _serializer.Serialize(data);
|
||||
|
||||
var protector = _protector;
|
||||
if (!string.IsNullOrEmpty(purpose))
|
||||
{
|
||||
protector = protector.CreateProtector(purpose);
|
||||
}
|
||||
|
||||
var protectedData = protector.Protect(userData);
|
||||
return Base64UrlTextEncoder.Encode(protectedData);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception will be traced")]
|
||||
public TData Unprotect(string protectedText)
|
||||
{
|
||||
return Unprotect(protectedText, purpose: null);
|
||||
}
|
||||
|
||||
public TData Unprotect(string protectedText, string purpose)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -36,20 +49,25 @@ namespace Microsoft.AspNet.Authentication
|
|||
return default(TData);
|
||||
}
|
||||
|
||||
byte[] protectedData = Base64UrlTextEncoder.Decode(protectedText);
|
||||
var protectedData = Base64UrlTextEncoder.Decode(protectedText);
|
||||
if (protectedData == null)
|
||||
{
|
||||
return default(TData);
|
||||
}
|
||||
|
||||
byte[] userData = _protector.Unprotect(protectedData);
|
||||
var protector = _protector;
|
||||
if (!string.IsNullOrEmpty(purpose))
|
||||
{
|
||||
protector = protector.CreateProtector(purpose);
|
||||
}
|
||||
|
||||
var userData = protector.Unprotect(protectedData);
|
||||
if (userData == null)
|
||||
{
|
||||
return default(TData);
|
||||
}
|
||||
|
||||
TData model = _serializer.Deserialize(userData);
|
||||
return model;
|
||||
return _serializer.Deserialize(userData);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
// 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 System.Text;
|
||||
using Microsoft.AspNet.DataProtection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.DataHandler
|
||||
{
|
||||
public class SecureDataFormatTests
|
||||
{
|
||||
public SecureDataFormatTests()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddDataProtection();
|
||||
ServiceProvider = serviceCollection.BuildServiceProvider();
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
|
||||
[Fact]
|
||||
public void ProtectDataRoundTrips()
|
||||
{
|
||||
var provider = ServiceProvider.GetRequiredService<IDataProtectionProvider>();
|
||||
var prototector = provider.CreateProtector("test");
|
||||
var secureDataFormat = new SecureDataFormat<string>(new StringSerializer(), prototector);
|
||||
|
||||
string input = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var protectedData = secureDataFormat.Protect(input);
|
||||
var result = secureDataFormat.Unprotect(protectedData);
|
||||
Assert.Equal(input, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProtectWithPurposeRoundTrips()
|
||||
{
|
||||
var provider = ServiceProvider.GetRequiredService<IDataProtectionProvider>();
|
||||
var prototector = provider.CreateProtector("test");
|
||||
var secureDataFormat = new SecureDataFormat<string>(new StringSerializer(), prototector);
|
||||
|
||||
string input = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
string purpose = "purpose1";
|
||||
var protectedData = secureDataFormat.Protect(input, purpose);
|
||||
var result = secureDataFormat.Unprotect(protectedData, purpose);
|
||||
Assert.Equal(input, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnprotectWithDifferentPurposeFails()
|
||||
{
|
||||
var provider = ServiceProvider.GetRequiredService<IDataProtectionProvider>();
|
||||
var prototector = provider.CreateProtector("test");
|
||||
var secureDataFormat = new SecureDataFormat<string>(new StringSerializer(), prototector);
|
||||
|
||||
string input = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
string purpose = "purpose1";
|
||||
var protectedData = secureDataFormat.Protect(input, purpose);
|
||||
var result = secureDataFormat.Unprotect(protectedData); // Null other purpose
|
||||
Assert.Null(result);
|
||||
|
||||
result = secureDataFormat.Unprotect(protectedData, "purpose2");
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
private class StringSerializer : IDataSerializer<string>
|
||||
{
|
||||
public byte[] Serialize(string model)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(model);
|
||||
}
|
||||
|
||||
public string Deserialize(byte[] data)
|
||||
{
|
||||
return Encoding.UTF8.GetString(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,8 +31,12 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
|
||||
return sb.ToString();
|
||||
}
|
||||
public string Protect(AuthenticationProperties data, string purpose)
|
||||
{
|
||||
return Protect(data);
|
||||
}
|
||||
|
||||
AuthenticationProperties ISecureDataFormat<AuthenticationProperties>.Unprotect(string protectedText)
|
||||
public AuthenticationProperties Unprotect(string protectedText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(protectedText))
|
||||
{
|
||||
|
|
@ -58,5 +62,10 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
|
||||
return propeties;
|
||||
}
|
||||
|
||||
public AuthenticationProperties Unprotect(string protectedText, string purpose)
|
||||
{
|
||||
return Unprotect(protectedText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue