Introducing AntiForgery System :

- Config is cleaned up to not have IAdditionalDataProvider and SuppressIdentityChecks.

- Added a DefaultClaimUidExtractor which looks for NameIdentifier and if not present serializes entire claims.

- Added HtmlHelper.

- AntiForgery now returns an AntiForgeryTokenSet which represents a tuple of cookie and form tokens.
This commit is contained in:
harshgMSFT 2014-03-31 12:58:40 -07:00
parent d61915149c
commit f26cc51e2e
28 changed files with 1491 additions and 6 deletions

View File

@ -0,0 +1,98 @@
using System.ComponentModel;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Security.DataProtection;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Provides access to the anti-forgery system, which provides protection against
/// Cross-site Request Forgery (XSRF, also called CSRF) attacks.
/// </summary>
public sealed class AntiForgery
{
private static readonly string _purpose = "Microsoft.AspNet.Mvc.AntiXsrf.AntiForgeryToken.v1";
private readonly AntiForgeryWorker _worker;
public AntiForgery([NotNull] IClaimUidExtractor claimUidExtractor,
[NotNull] IDataProtectionProvider dataProtectionProvider,
[NotNull] IAntiForgeryAdditionalDataProvider additionalDataProvider)
{
// TODO: This is temporary till we figure out how to flow configs using DI.
var config = new AntiForgeryConfigWrapper();
var serializer = new AntiForgeryTokenSerializer(dataProtectionProvider.CreateProtector(_purpose));
var tokenStore = new AntiForgeryTokenStore(config, serializer);
var tokenProvider = new TokenProvider(config, claimUidExtractor, additionalDataProvider);
_worker = new AntiForgeryWorker(serializer, config, tokenStore, tokenProvider, tokenProvider);
}
/// <summary>
/// Generates an anti-forgery token for this request. This token can
/// be validated by calling the Validate() method.
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
/// <returns>An HTML string corresponding to an &lt;input type="hidden"&gt;
/// element. This element should be put inside a &lt;form&gt;.</returns>
/// <remarks>
/// This method has a side effect:
/// A response cookie is set if there is no valid cookie associated with the request.
/// </remarks>
public HtmlString GetHtml([NotNull] HttpContext context)
{
TagBuilder builder = _worker.GetFormInputElement(context);
return builder.ToHtmlString(TagRenderMode.SelfClosing);
}
/// <summary>
/// Generates an anti-forgery token pair (cookie and form token) for this request.
/// This method is similar to GetHtml(HttpContext context), but this method gives the caller control
/// over how to persist the returned values. To validate these tokens, call the
/// appropriate overload of Validate.
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
/// <param name="oldCookieToken">The anti-forgery token - if any - that already existed
/// for this request. May be null. The anti-forgery system will try to reuse this cookie
/// value when generating a matching form token.</param>
/// <remarks>
/// Unlike the GetHtml(HttpContext context) method, this method has no side effect. The caller
/// is responsible for setting the response cookie and injecting the returned
/// form token as appropriate.
/// </remarks>
public AntiForgeryTokenSet GetTokens([NotNull] HttpContext context, string oldCookieToken)
{
// Will contain a new cookie value if the old cookie token
// was null or invalid. If this value is non-null when the method completes, the caller
// must persist this value in the form of a response cookie, and the existing cookie value
// should be discarded. If this value is null when the method completes, the existing
// cookie value was valid and needn't be modified.
return _worker.GetTokens(context, oldCookieToken);
}
/// <summary>
/// Validates an anti-forgery token that was supplied for this request.
/// The anti-forgery token may be generated by calling GetHtml(HttpContext context).
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
public async Task ValidateAsync([NotNull] HttpContext context)
{
await _worker.ValidateAsync(context);
}
/// <summary>
/// Validates an anti-forgery token pair that was generated by the GetTokens method.
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
/// <param name="cookieToken">The token that was supplied in the request cookie.</param>
/// <param name="formToken">The token that was supplied in the request form body.</param>
public void Validate([NotNull] HttpContext context, string cookieToken, string formToken)
{
_worker.Validate(context, cookieToken, formToken);
}
public void Validate([NotNull] HttpContext context, AntiForgeryTokenSet antiForgeryTokenSet)
{
Validate(context, antiForgeryTokenSet.CookieToken, antiForgeryTokenSet.FormToken);
}
}
}

View File

@ -0,0 +1,64 @@
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Provides programmatic configuration for the anti-forgery token system.
/// </summary>
public static class AntiForgeryConfig
{
internal const string AntiForgeryTokenFieldName = "__RequestVerificationToken";
private static string _cookieName;
/// <summary>
/// Specifies the name of the cookie that is used by the anti-forgery
/// system.
/// </summary>
/// <remarks>
/// If an explicit name is not provided, the system will automatically
/// generate a name.
/// </remarks>
public static string CookieName
{
get
{
if (_cookieName == null)
{
_cookieName = GetAntiForgeryCookieName();
}
return _cookieName;
}
set
{
_cookieName = value;
}
}
/// <summary>
/// Specifies whether SSL is required for the anti-forgery system
/// to operate. If this setting is 'true' and a non-SSL request
/// comes into the system, all anti-forgery APIs will fail.
/// </summary>
public static bool RequireSsl
{
get;
set;
}
/// <summary>
/// Specifies whether to suppress the generation of X-Frame-Options header
/// which is used to prevent ClickJacking. By default, the X-Frame-Options
/// header is generated with the value SAMEORIGIN. If this setting is 'true',
/// the X-Frame-Options header will not be generated for the response.
/// </summary>
public static bool SuppressXFrameOptionsHeader
{
get;
set;
}
// TODO: Replace the stub.
private static string GetAntiForgeryCookieName()
{
return AntiForgeryTokenFieldName;
}
}
}

View File

@ -0,0 +1,25 @@
namespace Microsoft.AspNet.Mvc
{
public sealed class AntiForgeryConfigWrapper : IAntiForgeryConfig
{
public string CookieName
{
get { return AntiForgeryConfig.CookieName; }
}
public string FormFieldName
{
get { return AntiForgeryConfig.AntiForgeryTokenFieldName; }
}
public bool RequireSSL
{
get { return AntiForgeryConfig.RequireSsl; }
}
public bool SuppressXFrameOptionsHeader
{
get { return AntiForgeryConfig.SuppressXFrameOptionsHeader; }
}
}
}

View File

@ -0,0 +1,52 @@
using System;
namespace Microsoft.AspNet.Mvc
{
internal sealed class AntiForgeryToken
{
internal const int SecurityTokenBitLength = 128;
internal const int ClaimUidBitLength = 256;
private string _additionalData = string.Empty;
private string _username = string.Empty;
private BinaryBlob _securityToken;
public string AdditionalData
{
get { return _additionalData; }
set
{
_additionalData = value ?? string.Empty;
}
}
public BinaryBlob ClaimUid { get; set; }
public bool IsSessionToken { get; set; }
public BinaryBlob SecurityToken
{
get
{
if (_securityToken == null)
{
_securityToken = new BinaryBlob(SecurityTokenBitLength);
}
return _securityToken;
}
set
{
_securityToken = value;
}
}
public string Username
{
get { return _username; }
set
{
_username = value ?? string.Empty;
}
}
}
}

View File

@ -0,0 +1,187 @@

using System;
using System.IO;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Security.DataProtection;
using System.Text;
namespace Microsoft.AspNet.Mvc
{
internal sealed class AntiForgeryTokenSerializer : IAntiForgeryTokenSerializer
{
private readonly IDataProtector _cryptoSystem;
private const byte TokenVersion = 0x01;
internal AntiForgeryTokenSerializer([NotNull] IDataProtector cryptoSystem)
{
_cryptoSystem = cryptoSystem;
}
public AntiForgeryToken Deserialize(string serializedToken)
{
Exception innerException = null;
try
{
using (MemoryStream stream = new MemoryStream(UrlTokenDecode(serializedToken)))
{
using (BinaryReader reader = new BinaryReader(stream))
{
AntiForgeryToken token = DeserializeImpl(reader);
if (token != null)
{
return token;
}
}
}
}
catch(Exception ex)
{
// swallow all exceptions - homogenize error if something went wrong
innerException = ex;
}
// if we reached this point, something went wrong deserializing
throw new InvalidOperationException(Resources.AntiForgeryToken_DeserializationFailed, innerException);
}
/* The serialized format of the anti-XSRF token is as follows:
* Version: 1 byte integer
* SecurityToken: 16 byte binary blob
* IsSessionToken: 1 byte Boolean
* [if IsSessionToken = true]
* +- IsClaimsBased: 1 byte Boolean
* | [if IsClaimsBased = true]
* | `- ClaimUid: 32 byte binary blob
* | [if IsClaimsBased = false]
* | `- Username: UTF-8 string with 7-bit integer length prefix
* `- AdditionalData: UTF-8 string with 7-bit integer length prefix
*/
private static AntiForgeryToken DeserializeImpl(BinaryReader reader)
{
// we can only consume tokens of the same serialized version that we generate
var embeddedVersion = reader.ReadByte();
if (embeddedVersion != TokenVersion)
{
return null;
}
var deserializedToken = new AntiForgeryToken();
var securityTokenBytes = reader.ReadBytes(AntiForgeryToken.SecurityTokenBitLength / 8);
deserializedToken.SecurityToken = new BinaryBlob(AntiForgeryToken.SecurityTokenBitLength, securityTokenBytes);
deserializedToken.IsSessionToken = reader.ReadBoolean();
if (!deserializedToken.IsSessionToken)
{
bool isClaimsBased = reader.ReadBoolean();
if (isClaimsBased)
{
var claimUidBytes = reader.ReadBytes(AntiForgeryToken.ClaimUidBitLength / 8);
deserializedToken.ClaimUid = new BinaryBlob(AntiForgeryToken.ClaimUidBitLength, claimUidBytes);
}
else
{
deserializedToken.Username = reader.ReadString();
}
deserializedToken.AdditionalData = reader.ReadString();
}
// if there's still unconsumed data in the stream, fail
if (reader.BaseStream.ReadByte() != -1)
{
return null;
}
// success
return deserializedToken;
}
public string Serialize([NotNull] AntiForgeryToken token)
{
using (var stream = new MemoryStream())
{
using (var writer = new BinaryWriter(stream))
{
writer.Write(TokenVersion);
writer.Write(token.SecurityToken.GetData());
writer.Write(token.IsSessionToken);
if (!token.IsSessionToken)
{
if (token.ClaimUid != null)
{
writer.Write(true /* isClaimsBased */);
writer.Write(token.ClaimUid.GetData());
}
else
{
writer.Write(false /* isClaimsBased */);
writer.Write(token.Username);
}
writer.Write(token.AdditionalData);
}
writer.Flush();
return UrlTokenEncode(_cryptoSystem.Protect(stream.ToArray()));
}
}
}
private string UrlTokenEncode(byte[] input)
{
var base64String = Convert.ToBase64String(input);
if (string.IsNullOrEmpty(base64String))
{
return string.Empty;
}
var sb = new StringBuilder();
for (int i = 0; i < base64String.Length; i++)
{
switch (base64String[i])
{
case '+':
sb.Append('-');
break;
case '/':
sb.Append('_');
break;
case '=':
sb.Append('.');
break;
default:
sb.Append(base64String[i]);
break;
}
}
return sb.ToString();
}
private byte[] UrlTokenDecode(string input)
{
var sb = new StringBuilder();
for (int i = 0; i < input.Length; i++)
{
switch (input[i])
{
case '-':
sb.Append('+');
break;
case '_':
sb.Append('/');
break;
case '.':
sb.Append('=');
break;
default:
sb.Append(input[i]);
break;
}
}
return Convert.FromBase64String(sb.ToString());
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using Microsoft.AspNet.Mvc.Core;
namespace Microsoft.AspNet.Mvc
{
public class AntiForgeryTokenSet
{
public AntiForgeryTokenSet(string formToken, string cookieToken)
{
if (string.IsNullOrEmpty(formToken))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, formToken);
}
FormToken = formToken;
CookieToken = cookieToken;
}
public string FormToken { get; private set; }
// The cookie token is allowed to be null.
// This would be the case when the old cookie token is still valid.
// In such cases a call to GetTokens would return a token set with null cookie token.
public string CookieToken { get; private set; }
}
}

View File

@ -0,0 +1,61 @@

using System;
using Microsoft.AspNet.Abstractions;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc
{
// Saves anti-XSRF tokens split between HttpRequest.Cookies and HttpRequest.Form
internal sealed class AntiForgeryTokenStore : ITokenStore
{
private readonly IAntiForgeryConfig _config;
private readonly IAntiForgeryTokenSerializer _serializer;
internal AntiForgeryTokenStore([NotNull] IAntiForgeryConfig config,
[NotNull] IAntiForgeryTokenSerializer serializer)
{
_config = config;
_serializer = serializer;
}
public AntiForgeryToken GetCookieToken(HttpContext httpContext)
{
var cookie = httpContext.Request.Cookies[_config.CookieName];
if (String.IsNullOrEmpty(cookie))
{
// did not exist
return null;
}
return _serializer.Deserialize(cookie);
}
public async Task<AntiForgeryToken> GetFormTokenAsync(HttpContext httpContext)
{
var form = await httpContext.Request.GetFormAsync();
string value = form[_config.FormFieldName];
if (string.IsNullOrEmpty(value))
{
// did not exist
return null;
}
return _serializer.Deserialize(value);
}
public void SaveCookieToken(HttpContext httpContext, AntiForgeryToken token)
{
string serializedToken = _serializer.Serialize(token);
var options = new CookieOptions() { HttpOnly = true };
// Note: don't use "newCookie.Secure = _config.RequireSSL;" since the default
// value of newCookie.Secure is poulated out of band.
if (_config.RequireSSL)
{
options.Secure = true;
}
httpContext.Response.Cookies.Append(_config.CookieName, serializedToken, options);
}
}
}

View File

@ -0,0 +1,211 @@

using System;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.Rendering;
namespace Microsoft.AspNet.Mvc
{
internal sealed class AntiForgeryWorker
{
private readonly IAntiForgeryConfig _config;
private readonly IAntiForgeryTokenSerializer _serializer;
private readonly ITokenStore _tokenStore;
private readonly ITokenValidator _validator;
private readonly ITokenGenerator _generator;
internal AntiForgeryWorker([NotNull] IAntiForgeryTokenSerializer serializer,
[NotNull] IAntiForgeryConfig config,
[NotNull] ITokenStore tokenStore,
[NotNull] ITokenGenerator generator,
[NotNull] ITokenValidator validator)
{
_serializer = serializer;
_config = config;
_tokenStore = tokenStore;
_generator = generator;
_validator = validator;
}
private void CheckSSLConfig(HttpContext httpContext)
{
if (_config.RequireSSL && !httpContext.Request.IsSecure)
{
throw new InvalidOperationException(Resources.AntiForgeryWorker_RequireSSL);
}
}
private AntiForgeryToken DeserializeToken(string serializedToken)
{
return (!string.IsNullOrEmpty(serializedToken))
? _serializer.Deserialize(serializedToken)
: null;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Caller will just regenerate token in case of failure.")]
private AntiForgeryToken DeserializeTokenNoThrow(string serializedToken)
{
try
{
return DeserializeToken(serializedToken);
}
catch
{
// ignore failures since we'll just generate a new token
return null;
}
}
private static ClaimsIdentity ExtractIdentity(HttpContext httpContext)
{
if (httpContext != null)
{
ClaimsPrincipal user = httpContext.User;
if (user != null)
{
// We only support ClaimsIdentity.
// Todo remove this once httpContext.User moves to ClaimsIdentity.
return user.Identity as ClaimsIdentity;
}
}
return null;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Caller will just regenerate token in case of failure.")]
private AntiForgeryToken GetCookieTokenNoThrow(HttpContext httpContext)
{
try
{
return _tokenStore.GetCookieToken(httpContext);
}
catch
{
// ignore failures since we'll just generate a new token
return null;
}
}
// [ ENTRY POINT ]
// Generates an anti-XSRF token pair for the current user. The return
// value is the hidden input form element that should be rendered in
// the <form>. This method has a side effect: it may set a response
// cookie.
public TagBuilder GetFormInputElement([NotNull] HttpContext httpContext)
{
CheckSSLConfig(httpContext);
var oldCookieToken = GetCookieTokenNoThrow(httpContext);
var tokenSet = GetTokens(httpContext, oldCookieToken);
var newCookieToken = tokenSet.CookieToken;
var formToken = tokenSet.FormToken;
if (newCookieToken != null)
{
// If a new cookie was generated, persist it.
_tokenStore.SaveCookieToken(httpContext, newCookieToken);
}
if (!_config.SuppressXFrameOptionsHeader)
{
// Adding X-Frame-Options header to prevent ClickJacking. See
// http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10
// for more information.
httpContext.Response.Headers.Add("X-Frame-Options", new[] { "SAMEORIGIN" });
}
// <input type="hidden" name="__AntiForgeryToken" value="..." />
var retVal = new TagBuilder("input");
retVal.Attributes["type"] = "hidden";
retVal.Attributes["name"] = _config.FormFieldName;
retVal.Attributes["value"] = _serializer.Serialize(formToken);
return retVal;
}
// [ ENTRY POINT ]
// Generates a (cookie, form) serialized token pair for the current user.
// The caller may specify an existing cookie value if one exists. If the
// 'new cookie value' out param is non-null, the caller *must* persist
// the new value to cookie storage since the original value was null or
// invalid. This method is side-effect free.
public AntiForgeryTokenSet GetTokens([NotNull] HttpContext httpContext, string serializedOldCookieToken)
{
CheckSSLConfig(httpContext);
AntiForgeryToken oldCookieToken = DeserializeTokenNoThrow(serializedOldCookieToken);
var tokenSet = GetTokens(httpContext, oldCookieToken);
var serializedNewCookieToken = Serialize(tokenSet.CookieToken);
var serializedFormToken = Serialize(tokenSet.FormToken);
return new AntiForgeryTokenSet(serializedFormToken, serializedNewCookieToken);
}
private AntiForgeryTokenSetInternal GetTokens(HttpContext httpContext, AntiForgeryToken oldCookieToken)
{
AntiForgeryToken newCookieToken = null;
if (!_validator.IsCookieTokenValid(oldCookieToken))
{
// Need to make sure we're always operating with a good cookie token.
oldCookieToken = newCookieToken = _generator.GenerateCookieToken();
}
Contract.Assert(_validator.IsCookieTokenValid(oldCookieToken));
AntiForgeryToken formToken = _generator.
GenerateFormToken(httpContext,
ExtractIdentity(httpContext),
oldCookieToken);
return new AntiForgeryTokenSetInternal()
{
CookieToken = newCookieToken,
FormToken = formToken
};
}
private string Serialize(AntiForgeryToken token)
{
return (token != null) ? _serializer.Serialize(token) : null;
}
// [ ENTRY POINT ]
// Given an HttpContext, validates that the anti-XSRF tokens contained
// in the cookies & form are OK for this request.
public async Task ValidateAsync([NotNull] HttpContext httpContext)
{
CheckSSLConfig(httpContext);
// Extract cookie & form tokens
var cookieToken = _tokenStore.GetCookieToken(httpContext);
var formToken = await _tokenStore.GetFormTokenAsync(httpContext);
// Validate
_validator.ValidateTokens(httpContext, ExtractIdentity(httpContext), cookieToken, formToken);
}
// [ ENTRY POINT ]
// Given the serialized string representations of a cookie & form token,
// validates that the pair is OK for this request.
public void Validate([NotNull] HttpContext httpContext, string cookieToken, string formToken)
{
CheckSSLConfig(httpContext);
// Extract cookie & form tokens
var deserializedCookieToken = DeserializeToken(cookieToken);
var deserializedFormToken = DeserializeToken(formToken);
// Validate
_validator.ValidateTokens(httpContext, ExtractIdentity(httpContext), deserializedCookieToken, deserializedFormToken);
}
private class AntiForgeryTokenSetInternal
{
public AntiForgeryToken FormToken { get; set; }
public AntiForgeryToken CookieToken { get; set; }
}
}
}

View File

@ -0,0 +1,116 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using Microsoft.AspNet.Security.DataProtection;
using System.Runtime.CompilerServices;
namespace Microsoft.AspNet.Mvc
{
// Represents a binary blob (token) that contains random data.
// Useful for binary data inside a serialized stream.
[DebuggerDisplay("{DebuggerString}")]
internal sealed class BinaryBlob : IEquatable<BinaryBlob>
{
private readonly byte[] _data;
// Generates a new token using a specified bit length.
public BinaryBlob(int bitLength)
: this(bitLength, GenerateNewToken(bitLength))
{
}
// Generates a token using an existing binary value.
public BinaryBlob(int bitLength, byte[] data)
{
if (bitLength < 32 || bitLength % 8 != 0)
{
throw new ArgumentOutOfRangeException("bitLength");
}
if (data == null || data.Length != bitLength / 8)
{
throw new ArgumentOutOfRangeException("data");
}
_data = data;
}
public int BitLength
{
get
{
return checked(_data.Length * 8);
}
}
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by debugger.")]
private string DebuggerString
{
get
{
StringBuilder sb = new StringBuilder("0x", 2 + (_data.Length * 2));
for (int i = 0; i < _data.Length; i++)
{
sb.AppendFormat(CultureInfo.InvariantCulture, "{0:x2}", _data[i]);
}
return sb.ToString();
}
}
public override bool Equals(object obj)
{
return Equals(obj as BinaryBlob);
}
public bool Equals(BinaryBlob other)
{
if (other == null)
{
return false;
}
Contract.Assert(this._data.Length == other._data.Length);
return AreByteArraysEqual(this._data, other._data);
}
public byte[] GetData()
{
return _data;
}
public override int GetHashCode()
{
// Since data should contain uniformly-distributed entropy, the
// first 32 bits can serve as the hash code.
Contract.Assert(_data != null && _data.Length >= (32 / 8));
return BitConverter.ToInt32(_data, 0);
}
private static byte[] GenerateNewToken(int bitLength)
{
byte[] data = new byte[bitLength / 8];
CryptRand.FillBuffer(new ArraySegment<byte>(data));
return data;
}
// Need to mark it with NoInlining and NoOptimization attributes to ensure that the
// operation runs in constant time.
[MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static bool AreByteArraysEqual(byte[] a, byte[] b)
{
if (a == null || b == null || a.Length != b.Length)
{
return false;
}
bool areEqual = true;
for (int i = 0; i < a.Length; i++)
{
areEqual &= (a[i] == b[i]);
}
return areEqual;
}
}
}

View File

@ -0,0 +1,18 @@
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Mvc
{
public class DefaultAntiForgeryAdditionalDataProvider : IAntiForgeryAdditionalDataProvider
{
public virtual string GetAdditionalData(HttpContext context)
{
return string.Empty;
}
public virtual bool ValidateAdditionalData(HttpContext context, string additionalData)
{
// Default implementation does not understand anything but empty data.
return string.IsNullOrEmpty(additionalData);
}
}
}

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
namespace Microsoft.AspNet.Mvc
{
// Can extract unique identifers for a claims-based identity
public class DefaultClaimUidExtractor : IClaimUidExtractor
{
public string ExtractClaimUid(ClaimsIdentity claimsIdentity)
{
if (claimsIdentity == null || !claimsIdentity.IsAuthenticated)
{
// Skip anonymous users
return null;
}
var uniqueIdentifierParameters = GetUniqueIdentifierParameters(claimsIdentity);
byte[] claimUidBytes = ComputeSHA256(uniqueIdentifierParameters);
return Convert.ToBase64String(claimUidBytes);
}
private static IEnumerable<string> GetUniqueIdentifierParameters(ClaimsIdentity claimsIdentity)
{
// TODO: Need to enable support for special casing acs identities.
var nameIdentifierClaim = claimsIdentity.FindFirst(claim =>
String.Equals(ClaimTypes.NameIdentifier,
claim.Type, StringComparison.Ordinal));
if (nameIdentifierClaim != null && !string.IsNullOrEmpty(nameIdentifierClaim.Value))
{
return new string[]
{
ClaimTypes.NameIdentifier,
nameIdentifierClaim.Value
};
}
// We Do not understand this claimsIdentity, fallback on serializing the entire claims Identity.
var claims = claimsIdentity.Claims.ToList();
claims.Sort((a, b) => string.Compare(a.Type, b.Type, StringComparison.Ordinal));
var identifierParameters = new List<string>();
foreach (var claim in claims)
{
identifierParameters.Add(claim.Type);
identifierParameters.Add(claim.Value);
}
return identifierParameters;
}
private static byte[] ComputeSHA256(IEnumerable<string> parameters)
{
using (var ms = new MemoryStream())
{
using (var bw = new BinaryWriter(ms))
{
foreach (string parameter in parameters)
{
bw.Write(parameter); // also writes the length as a prefix; unambiguous
}
bw.Flush();
using (var sha256 = SHA256.Create())
{
byte[] retVal = sha256.ComputeHash(ms.ToArray(), 0, checked((int)ms.Length));
return retVal;
}
}
}
}
}
}

View File

@ -0,0 +1,36 @@
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Allows providing or validating additional custom data for anti-forgery tokens.
/// For example, the developer could use this to supply a nonce when the token is
/// generated, then he could validate the nonce when the token is validated.
/// </summary>
/// <remarks>
/// The anti-forgery system already embeds the client's username within the
/// generated tokens. This interface provides and consumes <em>supplemental</em>
/// data. If an incoming anti-forgery token contains supplemental data but no
/// additional data provider is configured, the supplemental data will not be
/// validated.
/// </remarks>
public interface IAntiForgeryAdditionalDataProvider
{
/// <summary>
/// Provides additional data to be stored for the anti-forgery tokens generated
/// during this request.
/// </summary>
/// <param name="context">Information about the current request.</param>
/// <returns>Supplemental data to embed within the anti-forgery token.</returns>
string GetAdditionalData(HttpContext context);
/// <summary>
/// Validates additional data that was embedded inside an incoming anti-forgery
/// token.
/// </summary>
/// <param name="context">Information about the current request.</param>
/// <param name="additionalData">Supplemental data that was embedded within the token.</param>
/// <returns>True if the data is valid; false if the data is invalid.</returns>
bool ValidateAdditionalData(HttpContext context, string additionalData);
}
}

View File

@ -0,0 +1,18 @@
namespace Microsoft.AspNet.Mvc
{
// Provides configuration information about the anti-forgery system.
public interface IAntiForgeryConfig
{
// Name of the cookie to use.
string CookieName { get; }
// Name of the form field to use.
string FormFieldName { get; }
// Whether SSL is mandatory for this request.
bool RequireSSL { get; }
// Skip X-FRAME-OPTIONS header.
bool SuppressXFrameOptionsHeader { get; }
}
}

View File

@ -0,0 +1,9 @@
namespace Microsoft.AspNet.Mvc
{
// Abstracts out the serialization process for an anti-forgery token
internal interface IAntiForgeryTokenSerializer
{
AntiForgeryToken Deserialize(string serializedToken);
string Serialize(AntiForgeryToken token);
}
}

View File

@ -0,0 +1,11 @@
using System.Security.Claims;
using System.Security.Principal;
namespace Microsoft.AspNet.Mvc
{
// Can extract unique identifers for a claims-based identity
public interface IClaimUidExtractor
{
string ExtractClaimUid(ClaimsIdentity identity);
}
}

View File

@ -0,0 +1,17 @@
using System.Security.Principal;
using Microsoft.AspNet.Abstractions;
using System.Security.Claims;
namespace Microsoft.AspNet.Mvc
{
// Provides configuration information about the anti-forgery system.
internal interface ITokenGenerator
{
// Generates a new random cookie token.
AntiForgeryToken GenerateCookieToken();
// Given a cookie token, generates a corresponding form token.
// The incoming cookie token must be valid.
AntiForgeryToken GenerateFormToken(HttpContext httpContext, ClaimsIdentity identity, AntiForgeryToken cookieToken);
}
}

View File

@ -0,0 +1,13 @@
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Mvc
{
// Provides an abstraction around how tokens are persisted and retrieved for a request
internal interface ITokenStore
{
AntiForgeryToken GetCookieToken(HttpContext httpContext);
Task<AntiForgeryToken> GetFormTokenAsync(HttpContext httpContext);
void SaveCookieToken(HttpContext httpContext, AntiForgeryToken token);
}
}

View File

@ -0,0 +1,17 @@
using System.Security.Principal;
using Microsoft.AspNet.Abstractions;
using System.Security.Claims;
namespace Microsoft.AspNet.Mvc
{
// Provides an abstraction around something that can validate anti-XSRF tokens
internal interface ITokenValidator
{
// Determines whether an existing cookie token is valid (well-formed).
// If it is not, the caller must call GenerateCookieToken() before calling GenerateFormToken().
bool IsCookieTokenValid(AntiForgeryToken cookieToken);
// Validates a (cookie, form) token pair.
void ValidateTokens(HttpContext httpContext, ClaimsIdentity identity, AntiForgeryToken cookieToken, AntiForgeryToken formToken);
}
}

View File

@ -0,0 +1,159 @@
using System;
using System.Diagnostics.Contracts;
using System.Security.Principal;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Mvc.Core;
using System.Security.Claims;
namespace Microsoft.AspNet.Mvc
{
internal sealed class TokenProvider : ITokenValidator, ITokenGenerator
{
private readonly IClaimUidExtractor _claimUidExtractor;
private readonly IAntiForgeryConfig _config;
private readonly IAntiForgeryAdditionalDataProvider _additionalDataProvider;
internal TokenProvider(IAntiForgeryConfig config,
IClaimUidExtractor claimUidExtractor,
IAntiForgeryAdditionalDataProvider additionalDataProvider)
{
_config = config;
_claimUidExtractor = claimUidExtractor;
_additionalDataProvider = additionalDataProvider;
}
public AntiForgeryToken GenerateCookieToken()
{
return new AntiForgeryToken()
{
// SecurityToken will be populated automatically.
IsSessionToken = true
};
}
public AntiForgeryToken GenerateFormToken(HttpContext httpContext,
ClaimsIdentity identity,
AntiForgeryToken cookieToken)
{
Contract.Assert(IsCookieTokenValid(cookieToken));
var formToken = new AntiForgeryToken()
{
SecurityToken = cookieToken.SecurityToken,
IsSessionToken = false
};
bool isIdentityAuthenticated = false;
// populate Username and ClaimUid
if (identity != null && identity.IsAuthenticated)
{
isIdentityAuthenticated = true;
formToken.ClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(identity));
if (formToken.ClaimUid == null)
{
formToken.Username = identity.Name;
}
}
// populate AdditionalData
if (_additionalDataProvider != null)
{
formToken.AdditionalData = _additionalDataProvider.GetAdditionalData(httpContext);
}
if (isIdentityAuthenticated
&& string.IsNullOrEmpty(formToken.Username)
&& formToken.ClaimUid == null
&& string.IsNullOrEmpty(formToken.AdditionalData))
{
// Application says user is authenticated, but we have no identifier for the user.
throw new InvalidOperationException(
Resources.
FormatTokenValidator_AuthenticatedUserWithoutUsername(identity.GetType()));
}
return formToken;
}
public bool IsCookieTokenValid(AntiForgeryToken cookieToken)
{
return (cookieToken != null && cookieToken.IsSessionToken);
}
public void ValidateTokens(HttpContext httpContext, ClaimsIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken)
{
// Were the tokens even present at all?
if (sessionToken == null)
{
throw new InvalidOperationException(Resources.FormatAntiForgeryToken_CookieMissing(_config.CookieName));
}
if (fieldToken == null)
{
throw new InvalidOperationException(Resources.FormatAntiForgeryToken_FormFieldMissing(_config.FormFieldName));
}
// Do the tokens have the correct format?
if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken)
{
throw new InvalidOperationException(Resources.FormatAntiForgeryToken_TokensSwapped(_config.CookieName, _config.FormFieldName));
}
// Are the security tokens embedded in each incoming token identical?
if (!Equals(sessionToken.SecurityToken, fieldToken.SecurityToken))
{
throw new InvalidOperationException(Resources.AntiForgeryToken_SecurityTokenMismatch);
}
// Is the incoming token meant for the current user?
string currentUsername = string.Empty;
BinaryBlob currentClaimUid = null;
if (identity != null && identity.IsAuthenticated)
{
currentClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(identity));
if (currentClaimUid == null)
{
currentUsername = identity.Name ?? string.Empty;
}
}
// OpenID and other similar authentication schemes use URIs for the username.
// These should be treated as case-sensitive.
bool useCaseSensitiveUsernameComparison = currentUsername.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
|| currentUsername.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
if (!String.Equals(fieldToken.Username,
currentUsername,
(useCaseSensitiveUsernameComparison) ?
StringComparison.Ordinal :
StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(Resources.
FormatAntiForgeryToken_UsernameMismatch(fieldToken.Username,
currentUsername));
}
if (!Equals(fieldToken.ClaimUid, currentClaimUid))
{
throw new InvalidOperationException(Resources.AntiForgeryToken_ClaimUidMismatch);
}
// Is the AdditionalData valid?
if (_additionalDataProvider != null && !_additionalDataProvider.ValidateAdditionalData(httpContext, fieldToken.AdditionalData))
{
throw new InvalidOperationException(Resources.AntiForgeryToken_AdditionalDataCheckFailed);
}
}
private static BinaryBlob GetClaimUidBlob(string base64ClaimUid)
{
if (base64ClaimUid == null)
{
return null;
}
return new BinaryBlob(256, Convert.FromBase64String(base64ClaimUid));
}
}
}

View File

@ -42,6 +42,25 @@
<Compile Include="ActionResults\RedirectToActionResult.cs" />
<Compile Include="ActionResults\RedirectToRouteResult.cs" />
<Compile Include="ActionResults\ViewResult.cs" />
<Compile Include="AntiForgery\AntiForgery.cs" />
<Compile Include="AntiForgery\AntiForgeryConfig.cs" />
<Compile Include="AntiForgery\AntiForgeryConfigWrapper.cs" />
<Compile Include="AntiForgery\AntiForgeryToken.cs" />
<Compile Include="AntiForgery\AntiForgeryTokenSerializer.cs" />
<Compile Include="AntiForgery\AntiForgeryTokenSet.cs" />
<Compile Include="AntiForgery\AntiForgeryTokenStore.cs" />
<Compile Include="AntiForgery\AntiForgeryWorker.cs" />
<Compile Include="AntiForgery\BinaryBlob.cs" />
<Compile Include="AntiForgery\DefaultClaimUidExtractor.cs" />
<Compile Include="AntiForgery\DefaultAntiForgeryAdditionalDataProvider.cs" />
<Compile Include="AntiForgery\IAntiForgeryAdditionalDataProvider.cs" />
<Compile Include="AntiForgery\IAntiForgeryConfig.cs" />
<Compile Include="AntiForgery\IAntiForgeryTokenSerializer.cs" />
<Compile Include="AntiForgery\IClaimUidExtractor.cs" />
<Compile Include="AntiForgery\ITokenGenerator.cs" />
<Compile Include="AntiForgery\ITokenStore.cs" />
<Compile Include="AntiForgery\ITokenValidator.cs" />
<Compile Include="AntiForgery\TokenValidator.cs" />
<Compile Include="Areas\AreaAttribute.cs" />
<Compile Include="Areas\ReflectedRouteConstraintsActionDescriptorProvider.cs" />
<Compile Include="BodyParameterInfo.cs" />

View File

@ -10,6 +10,54 @@ namespace Microsoft.AspNet.Mvc.Core
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNet.Mvc.Core.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// The provided identity of type '{0}' is marked IsAuthenticated = true but does not have a value for Name. By default, the anti-forgery system requires that all authenticated identities have a unique Name. If it is not possible to provide a unique Name for this identity, consider extending IAdditionalDataProvider by overriding the DefaultAdditionalDataProvider or a custom type that can provide some form of unique identifier for the current user.
/// </summary>
internal static string TokenValidator_AuthenticatedUserWithoutUsername
{
get { return GetString("TokenValidator_AuthenticatedUserWithoutUsername"); }
}
/// <summary>
/// The provided identity of type '{0}' is marked IsAuthenticated = true but does not have a value for Name. By default, the anti-forgery system requires that all authenticated identities have a unique Name. If it is not possible to provide a unique Name for this identity, consider extending IAdditionalDataProvider by overriding the DefaultAdditionalDataProvider or a custom type that can provide some form of unique identifier for the current user.
/// </summary>
internal static string FormatTokenValidator_AuthenticatedUserWithoutUsername(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TokenValidator_AuthenticatedUserWithoutUsername"), p0);
}
/// <summary>
/// A claim of type '{0}' was not present on the provided ClaimsIdentity.
/// </summary>
internal static string ClaimUidExtractor_ClaimNotPresent
{
get { return GetString("ClaimUidExtractor_ClaimNotPresent"); }
}
/// <summary>
/// A claim of type '{0}' was not present on the provided ClaimsIdentity.
/// </summary>
internal static string FormatClaimUidExtractor_ClaimNotPresent(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("ClaimUidExtractor_ClaimNotPresent"), p0);
}
/// <summary>
/// A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.
/// </summary>
internal static string ClaimUidExtractor_DefaultClaimsNotPresent
{
get { return GetString("ClaimUidExtractor_DefaultClaimsNotPresent"); }
}
/// <summary>
/// A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' or 'http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.
/// </summary>
internal static string FormatClaimUidExtractor_DefaultClaimsNotPresent()
{
return GetString("ClaimUidExtractor_DefaultClaimsNotPresent");
}
/// <summary>
/// The method '{0}' on type '{1}' returned an instance of '{2}'. Make sure to call Unwrap on the returned value to avoid unobserved faulted Task.
/// </summary>
@ -42,6 +90,150 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("ActionExecutor_UnexpectedTaskInstance"), p0, p1);
}
/// <summary>
/// The provided anti-forgery token failed a custom data check.
/// </summary>
internal static string AntiForgeryToken_AdditionalDataCheckFailed
{
get { return GetString("AntiForgeryToken_AdditionalDataCheckFailed"); }
}
/// <summary>
/// The provided anti-forgery token failed a custom data check.
/// </summary>
internal static string FormatAntiForgeryToken_AdditionalDataCheckFailed()
{
return GetString("AntiForgeryToken_AdditionalDataCheckFailed");
}
/// <summary>
/// The provided anti-forgery token was meant for a different claims-based user than the current user.
/// </summary>
internal static string AntiForgeryToken_ClaimUidMismatch
{
get { return GetString("AntiForgeryToken_ClaimUidMismatch"); }
}
/// <summary>
/// The provided anti-forgery token was meant for a different claims-based user than the current user.
/// </summary>
internal static string FormatAntiForgeryToken_ClaimUidMismatch()
{
return GetString("AntiForgeryToken_ClaimUidMismatch");
}
/// <summary>
/// The required anti-forgery cookie "{0}" is not present.
/// </summary>
internal static string AntiForgeryToken_CookieMissing
{
get { return GetString("AntiForgeryToken_CookieMissing"); }
}
/// <summary>
/// The required anti-forgery cookie "{0}" is not present.
/// </summary>
internal static string FormatAntiForgeryToken_CookieMissing(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiForgeryToken_CookieMissing"), p0);
}
/// <summary>
/// The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the &lt;machineKey&gt; configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.
/// </summary>
internal static string AntiForgeryToken_DeserializationFailed
{
get { return GetString("AntiForgeryToken_DeserializationFailed"); }
}
/// <summary>
/// The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the &lt;machineKey&gt; configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.
/// </summary>
internal static string FormatAntiForgeryToken_DeserializationFailed()
{
return GetString("AntiForgeryToken_DeserializationFailed");
}
/// <summary>
/// The required anti-forgery form field "{0}" is not present.
/// </summary>
internal static string AntiForgeryToken_FormFieldMissing
{
get { return GetString("AntiForgeryToken_FormFieldMissing"); }
}
/// <summary>
/// The required anti-forgery form field "{0}" is not present.
/// </summary>
internal static string FormatAntiForgeryToken_FormFieldMissing(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiForgeryToken_FormFieldMissing"), p0);
}
/// <summary>
/// The anti-forgery cookie token and form field token do not match.
/// </summary>
internal static string AntiForgeryToken_SecurityTokenMismatch
{
get { return GetString("AntiForgeryToken_SecurityTokenMismatch"); }
}
/// <summary>
/// The anti-forgery cookie token and form field token do not match.
/// </summary>
internal static string FormatAntiForgeryToken_SecurityTokenMismatch()
{
return GetString("AntiForgeryToken_SecurityTokenMismatch");
}
/// <summary>
/// Validation of the provided anti-forgery token failed. The cookie "{0}" and the form field "{1}" were swapped.
/// </summary>
internal static string AntiForgeryToken_TokensSwapped
{
get { return GetString("AntiForgeryToken_TokensSwapped"); }
}
/// <summary>
/// Validation of the provided anti-forgery token failed. The cookie "{0}" and the form field "{1}" were swapped.
/// </summary>
internal static string FormatAntiForgeryToken_TokensSwapped(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiForgeryToken_TokensSwapped"), p0, p1);
}
/// <summary>
/// The provided anti-forgery token was meant for user "{0}", but the current user is "{1}".
/// </summary>
internal static string AntiForgeryToken_UsernameMismatch
{
get { return GetString("AntiForgeryToken_UsernameMismatch"); }
}
/// <summary>
/// The provided anti-forgery token was meant for user "{0}", but the current user is "{1}".
/// </summary>
internal static string FormatAntiForgeryToken_UsernameMismatch(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("AntiForgeryToken_UsernameMismatch"), p0, p1);
}
/// <summary>
/// The anti-forgery system has the configuration value AntiForgeryConfig.RequireSsl = true, but the current request is not an SSL request.
/// </summary>
internal static string AntiForgeryWorker_RequireSSL
{
get { return GetString("AntiForgeryWorker_RequireSSL"); }
}
/// <summary>
/// The anti-forgery system has the configuration value AntiForgeryConfig.RequireSsl = true, but the current request is not an SSL request.
/// </summary>
internal static string FormatAntiForgeryWorker_RequireSSL()
{
return GetString("AntiForgeryWorker_RequireSSL");
}
/// <summary>
/// The class ReflectedActionFilterEndPoint only supports ReflectedActionDescriptors.
/// </summary>

View File

@ -30,6 +30,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
private readonly IUrlHelper _urlHelper;
private readonly IViewEngine _viewEngine;
private readonly AntiForgery _antiForgeryInstance;
private ViewContext _viewContext;
@ -39,11 +40,13 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlHelper(
[NotNull] IViewEngine viewEngine,
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] IUrlHelper urlHelper)
[NotNull] IUrlHelper urlHelper,
[NotNull] AntiForgery antiForgeryInstance)
{
_viewEngine = viewEngine;
MetadataProvider = metadataProvider;
_urlHelper = urlHelper;
_antiForgeryInstance = antiForgeryInstance;
// Underscores are fine characters in id's.
IdAttributeDotReplacement = "_";
@ -158,6 +161,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
ViewContext = viewContext;
}
public HtmlString AntiForgeryToken()
{
return _antiForgeryInstance.GetHtml(ViewContext.HttpContext);
}
public MvcForm BeginForm(string actionName, string controllerName, object routeValues, FormMethod method,
object htmlAttributes)
{

View File

@ -15,8 +15,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlHelper(
[NotNull] IViewEngine viewEngine,
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] IUrlHelper urlHelper)
: base(viewEngine, metadataProvider, urlHelper)
[NotNull] IUrlHelper urlHelper,
[NotNull] AntiForgery antiForgeryInstance)
: base(viewEngine, metadataProvider, urlHelper, antiForgeryInstance)
{
}

View File

@ -61,7 +61,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
string fragment,
object routeValues,
object htmlAttributes);
/// <summary>
/// Generates a hidden form field (anti-forgery token) that is validated when the form is submitted.
/// </summary>
/// <returns>
/// The generated form field (anti-forgery token).
/// </returns>
HtmlString AntiForgeryToken();
/// <summary>
/// Writes an opening <form> tag to the response. When the user submits the form,

View File

@ -117,12 +117,48 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AntiForgeryToken_AdditionalDataCheckFailed" xml:space="preserve">
<value>The provided anti-forgery token failed a custom data check.</value>
</data>
<data name="AntiForgeryToken_ClaimUidMismatch" xml:space="preserve">
<value>The provided anti-forgery token was meant for a different claims-based user than the current user.</value>
</data>
<data name="AntiForgeryToken_CookieMissing" xml:space="preserve">
<value>The required anti-forgery cookie "{0}" is not present.</value>
</data>
<data name="AntiForgeryToken_DeserializationFailed" xml:space="preserve">
<value>The anti-forgery token could not be decrypted. If this application is hosted by a Web Farm or cluster, ensure that all machines are running the same version of ASP.NET Web Pages and that the &lt;machineKey&gt; configuration specifies explicit encryption and validation keys. AutoGenerate cannot be used in a cluster.</value>
</data>
<data name="AntiForgeryToken_FormFieldMissing" xml:space="preserve">
<value>The required anti-forgery form field "{0}" is not present.</value>
</data>
<data name="AntiForgeryToken_SecurityTokenMismatch" xml:space="preserve">
<value>The anti-forgery cookie token and form field token do not match.</value>
</data>
<data name="AntiForgeryToken_TokensSwapped" xml:space="preserve">
<value>Validation of the provided anti-forgery token failed. The cookie "{0}" and the form field "{1}" were swapped.</value>
</data>
<data name="AntiForgeryToken_UsernameMismatch" xml:space="preserve">
<value>The provided anti-forgery token was meant for user "{0}", but the current user is "{1}".</value>
</data>
<data name="AntiForgeryWorker_RequireSSL" xml:space="preserve">
<value>The anti-forgery system has the configuration value AntiForgeryConfig.RequireSsl = true, but the current request is not an SSL request.</value>
</data>
<data name="ActionExecutor_WrappedTaskInstance" xml:space="preserve">
<value>The method '{0}' on type '{1}' returned an instance of '{2}'. Make sure to call Unwrap on the returned value to avoid unobserved faulted Task.</value>
</data>
<data name="ActionExecutor_UnexpectedTaskInstance" xml:space="preserve">
<value>The method '{0}' on type '{1}' returned a Task instance even though it is not an asynchronous method.</value>
</data>
<!--<data name="ClaimUidExtractor_DefaultClaimsNotPresent" xml:space="preserve">
<value>A claim of type 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier' was not present on the provided ClaimsIdentity. To enable anti-forgery token support with claims-based authentication, please verify that the configured claims provider is providing both of these claims on the ClaimsIdentity instances it generates. If the configured claims provider instead uses a different claim type as a unique identifier, it can be configured by setting the static property AntiForgeryConfig.UniqueClaimTypeIdentifier.</value>
</data>-->
<data name="ClaimUidExtractor_ClaimNotPresent" xml:space="preserve">
<value>A claim of type '{0}' was not present on the provided ClaimsIdentity.</value>
</data>
<data name="TokenValidator_AuthenticatedUserWithoutUsername" xml:space="preserve">
<value>The provided identity of type '{0}' is marked IsAuthenticated = true but does not have a value for Name. By default, the anti-forgery system requires that all authenticated identities have a unique Name. If it is not possible to provide a unique Name for this identity, consider extending IAdditionalDataProvider by overriding the DefaultAdditionalDataProvider or a custom type that can provide some form of unique identifier for the current user.</value>
</data>
<data name="ReflectedActionFilterEndPoint_UnexpectedActionDescriptor" xml:space="preserve">
<value>The class ReflectedActionFilterEndPoint only supports ReflectedActionDescriptors.</value>
</data>

View File

@ -11,7 +11,8 @@
"Microsoft.AspNet.Security": "0.1-alpha-*",
"Common": "",
"Microsoft.AspNet.Mvc.ModelBinding": "",
"Microsoft.Net.Runtime.Interfaces": "0.1-alpha-*"
"Microsoft.Net.Runtime.Interfaces": "0.1-alpha-*",
"Microsoft.AspNet.Security.DataProtection" : "0.1-alpha-*"
},
"configurations": {
"net45": {},
@ -37,6 +38,9 @@
"System.Runtime": "4.0.20.0",
"System.Runtime.Extensions": "4.0.10.0",
"System.Runtime.InteropServices": "4.0.20.0",
"System.Security.Cryptography": "4.0.0.0",
"System.Security.Cryptography.HashAlgorithms.SHA2": "4.0.0.0",
"System.Security.Principal": "4.0.0.0",
"System.Text.Encoding": "4.0.20.0",
"System.Threading": "4.0.0.0",
"System.Threading.Tasks": "4.0.10.0"

View File

@ -47,4 +47,4 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return false;
}
}
}
}

View File

@ -9,6 +9,7 @@ using Microsoft.AspNet.Mvc.Razor;
using Microsoft.AspNet.Mvc.Razor.Compilation;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Security.Authorization;
using Microsoft.AspNet.Security.DataProtection;
namespace Microsoft.AspNet.Mvc
{
@ -78,6 +79,9 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Transient<IViewComponentHelper, DefaultViewComponentHelper>();
yield return describe.Transient<IAuthorizationService, DefaultAuthorizationService>();
yield return describe.Singleton<IClaimUidExtractor, DefaultClaimUidExtractor>();
yield return describe.Singleton<AntiForgery, AntiForgery>();
yield return describe.Singleton<IAntiForgeryAdditionalDataProvider, DefaultAntiForgeryAdditionalDataProvider>();
yield return
describe.Describe(