Use new antiforgery package

This commit is contained in:
Ryan Nowak 2015-06-24 16:13:13 -07:00
parent e2787b3b84
commit 2f554c4b29
76 changed files with 224 additions and 4232 deletions

30
Mvc.sln
View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.22823.1
VisualStudioVersion = 14.0.23017.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
EndProject
@ -58,8 +58,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ContentNegotiationWebSite", "test\WebSites\ContentNegotiationWebSite\ContentNegotiationWebSite.xproj", "{C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AntiForgeryWebSite", "test\WebSites\AntiForgeryWebSite\AntiForgeryWebSite.xproj", "{A353B17E-A940-4CE8-8BF9-179E24A9041F}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AddServicesWebSite", "test\WebSites\AddServicesWebSite\AddServicesWebSite.xproj", "{6A0B65CE-6B01-40D0-840D-EFF3680D1547}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "FiltersWebSite", "test\WebSites\FiltersWebSite\FiltersWebSite.xproj", "{1976AC4A-FEA4-4587-A158-D9F79736D2B6}"
@ -174,6 +172,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Extens
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Extensions.Test", "test\Microsoft.AspNet.Mvc.Extensions.Test\Microsoft.AspNet.Mvc.Extensions.Test.xproj", "{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AntiforgeryTokenWebSite", "test\WebSites\AntiforgeryTokenWebSite\AntiforgeryTokenWebSite.xproj", "{A353B17E-A940-4CE8-8BF9-179E24A9041F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -394,16 +394,6 @@ Global
{C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7}.Release|x86.ActiveCfg = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|x86.ActiveCfg = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Any CPU.Build.0 = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|x86.ActiveCfg = Release|Any CPU
{6A0B65CE-6B01-40D0-840D-EFF3680D1547}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6A0B65CE-6B01-40D0-840D-EFF3680D1547}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A0B65CE-6B01-40D0-840D-EFF3680D1547}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -1052,6 +1042,18 @@ Global
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C}.Release|x86.ActiveCfg = Release|Any CPU
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C}.Release|x86.Build.0 = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|x86.ActiveCfg = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Debug|x86.Build.0 = Debug|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Any CPU.Build.0 = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|x86.ActiveCfg = Release|Any CPU
{A353B17E-A940-4CE8-8BF9-179E24A9041F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1079,7 +1081,6 @@ Global
{62735776-46FF-4170-9392-02E128A69B89} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{EE1AB716-F102-4CA3-AD2C-214A44B459A0} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{C6E5AFFA-890A-448F-8DE3-878B1D3C9FC7} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{A353B17E-A940-4CE8-8BF9-179E24A9041F} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{6A0B65CE-6B01-40D0-840D-EFF3680D1547} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{1976AC4A-FEA4-4587-A158-D9F79736D2B6} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{A192E504-2881-41DC-90D1-B7F1DD1134E8} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
@ -1137,5 +1138,6 @@ Global
{FCFE6024-2720-49B4-8257-9DBC6114F0F1} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{B2CA101A-87E6-4DD2-9BB2-28DA68EF1A94} = {32285FA4-6B46-4D6B-A840-2B13E4C8B58E}
{5DF6EFA5-865E-450B-BF83-DE9CE88EB77C} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{A353B17E-A940-4CE8-8BF9-179E24A9041F} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
EndGlobalSection
EndGlobal

View File

@ -8,9 +8,9 @@
<h2>Create</h2>
@* anti-forgery is on by default *@
@* <form/> tag helper will special-case only elements with an "asp-action" or "asp-anti-forgery" attribute. *@
<form asp-anti-forgery="false" asp-action="Create">
@* antiforgery is on by default *@
@* <form/> tag helper will special-case only elements with an "asp-action" or "asp-antiforgery" attribute. *@
<form asp-antiforgery="false" asp-action="Create">
<div class="form-horizontal">
@* validation summary tag helper will target just <div/> elements and append the list of errors *@
@* - i.e. this helper, like <select/> helper appends content. *@

View File

@ -23,12 +23,12 @@
<h3>Watch the greatest movies right here!</h3>
Submit your movie rankings:
<form asp-anti-forgery="false" asp-action="UpdateMovieRatings">
<form asp-antiforgery="false" asp-action="UpdateMovieRatings">
Movies + ratings go here
<button type="submit">Update ratings</button>
</form>
<form asp-anti-forgery="false" asp-action="UpdateCriticsQuotes">
<form asp-antiforgery="false" asp-action="UpdateCriticsQuotes">
Movies + ratings go here
<button type="submit">Update quotes</button>
</form>

View File

@ -42,150 +42,6 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("ObjectResult_MatchAllContentType"), 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.
/// </summary>
internal static string AntiForgeryToken_DeserializationFailed
{
get { return GetString("AntiForgeryToken_DeserializationFailed"); }
}
/// <summary>
/// The anti-forgery token could not be decrypted.
/// </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 AntiForgeryOptions.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 AntiForgeryOptions.RequireSsl = true, but the current request is not an SSL request.
/// </summary>
internal static string FormatAntiForgeryWorker_RequireSSL()
{
return GetString("AntiForgeryWorker_RequireSSL");
}
/// <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>
@ -234,22 +90,6 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("ClaimUidExtractor_ClaimNotPresent"), p0);
}
/// <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>
/// The class ReflectedActionFilterEndPoint only supports ReflectedActionDescriptors.
/// </summary>

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@ -123,33 +123,6 @@
<data name="ObjectResult_MatchAllContentType" xml:space="preserve">
<value>The content-type '{0}' added in the '{1}' property is invalid. Media types which match all types or match all subtypes are not supported.</value>
</data>
<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.</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 AntiForgeryOptions.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>
@ -159,9 +132,6 @@
<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

@ -1,138 +0,0 @@
// 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.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.Internal;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.WebEncoders;
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,
[NotNull] IOptions<AntiForgeryOptions> antiforgeryOptions,
[NotNull] IHtmlEncoder htmlEncoder,
[NotNull] IOptions<DataProtectionOptions> dataProtectionOptions)
{
var config = antiforgeryOptions.Options;
var applicationId = dataProtectionOptions.Options.ApplicationDiscriminator ?? string.Empty;
config.CookieName = config.CookieName ?? ComputeCookieName(applicationId);
var serializer = new AntiForgeryTokenSerializer(dataProtectionProvider.CreateProtector(_purpose));
var tokenStore = new AntiForgeryTokenStore(config, serializer);
var tokenProvider = new AntiForgeryTokenProvider(config, claimUidExtractor, additionalDataProvider);
_worker = new AntiForgeryWorker(serializer, config, tokenStore, tokenProvider, tokenProvider, htmlEncoder);
}
/// <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 TagBuilder GetHtml([NotNull] HttpContext context)
{
var builder = _worker.GetFormInputElement(context);
return builder;
}
/// <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);
}
/// <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="antiForgeryTokenSet">The anti-forgery token pair (cookie and form token) for this request.
/// </param>
public void Validate([NotNull] HttpContext context, AntiForgeryTokenSet antiForgeryTokenSet)
{
Validate(context, antiForgeryTokenSet.CookieToken, antiForgeryTokenSet.FormToken);
}
/// <summary>
/// Generates and sets an anti-forgery cookie if one is not available or not valid. Also sets response headers.
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
public void SetCookieTokenAndHeader([NotNull] HttpContext context)
{
_worker.SetCookieTokenAndHeader(context);
}
private string ComputeCookieName(string applicationId)
{
using (var sha256 = SHA256.Create())
{
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(applicationId));
var subHash = hash.Take(8).ToArray();
return WebEncoders.Base64UrlEncode(subHash);
}
}
}
}

View File

@ -1,13 +0,0 @@
// 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.
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Used as a per request state.
/// </summary>
internal class AntiForgeryContext
{
public AntiForgeryToken CookieToken { get; set; }
}
}

View File

@ -1,96 +0,0 @@
// 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.AspNet.Mvc.Extensions;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Provides programmatic configuration for the anti-forgery token system.
/// </summary>
public class AntiForgeryOptions
{
private const string AntiForgeryTokenFieldName = "__RequestVerificationToken";
private string _cookieName;
private string _formFieldName = AntiForgeryTokenFieldName;
public AntiForgeryOptions()
{
}
/// <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 string CookieName
{
get
{
return _cookieName;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value),
Resources.FormatPropertyOfTypeCannotBeNull(
nameof(CookieName), typeof(AntiForgeryOptions)));
}
_cookieName = value;
}
}
/// <summary>
/// Specifies the name of the anti-forgery token field that is used by the anti-forgery system.
/// </summary>
public string FormFieldName
{
get
{
return _formFieldName;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value),
Resources.FormatPropertyOfTypeCannotBeNull(
nameof(FormFieldName), typeof(AntiForgeryOptions)));
}
_formFieldName = 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 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 bool SuppressXFrameOptionsHeader
{
get;
set;
}
}
}

View File

@ -1,53 +0,0 @@
// 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.
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

@ -1,168 +0,0 @@
// 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;
using System.Security.Claims;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Extensions;
namespace Microsoft.AspNet.Mvc
{
internal sealed class AntiForgeryTokenProvider : IAntiForgeryTokenValidator, IAntiForgeryTokenGenerator
{
private readonly IClaimUidExtractor _claimUidExtractor;
private readonly AntiForgeryOptions _config;
private readonly IAntiForgeryAdditionalDataProvider _additionalDataProvider;
internal AntiForgeryTokenProvider(AntiForgeryOptions 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)
{
Debug.Assert(IsCookieTokenValid(cookieToken));
var formToken = new AntiForgeryToken()
{
SecurityToken = cookieToken.SecurityToken,
IsSessionToken = false
};
var 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?
var 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.
var 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

@ -1,136 +0,0 @@
// 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.IO;
using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.Mvc.Extensions;
using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.Internal;
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
{
var tokenBytes = WebEncoders.Base64UrlDecode(serializedToken);
using (var stream = new MemoryStream(_cryptoSystem.Unprotect(tokenBytes)))
{
using (var reader = new BinaryReader(stream))
{
var 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)
{
var 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 WebEncoders.Base64UrlEncode(_cryptoSystem.Protect(stream.ToArray()));
}
}
}
}
}

View File

@ -1,43 +0,0 @@
// 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.AspNet.Mvc.Extensions;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// The anti-forgery token pair (cookie and form token) for a request.
/// </summary>
public class AntiForgeryTokenSet
{
/// <summary>
/// Creates the anti-forgery token pair (cookie and form token) for a request.
/// </summary>
/// <param name="formToken">The token that is supplied in the request form body.</param>
/// <param name="cookieToken">The token that is supplied in the request cookie.</param>
public AntiForgeryTokenSet(string formToken, string cookieToken)
{
if (string.IsNullOrEmpty(formToken))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(formToken));
}
FormToken = formToken;
// Cookie Token is allowed to be null in the case when the old cookie is valid
// and there is no new cookieToken generated.
CookieToken = cookieToken;
}
/// <summary>
/// The token that is supplied in the request form body.
/// </summary>
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

@ -1,79 +0,0 @@
// 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.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
{
// Saves anti-XSRF tokens split between HttpRequest.Cookies and HttpRequest.Form
internal sealed class AntiForgeryTokenStore : IAntiForgeryTokenStore
{
private readonly AntiForgeryOptions _config;
private readonly IAntiForgeryTokenSerializer _serializer;
internal AntiForgeryTokenStore([NotNull] AntiForgeryOptions config,
[NotNull] IAntiForgeryTokenSerializer serializer)
{
_config = config;
_serializer = serializer;
}
public AntiForgeryToken GetCookieToken(HttpContext httpContext)
{
var contextAccessor =
httpContext.RequestServices.GetRequiredService<IScopedInstance<AntiForgeryContext>>();
if (contextAccessor.Value != null)
{
return contextAccessor.Value.CookieToken;
}
var requestCookie = httpContext.Request.Cookies[_config.CookieName];
if (string.IsNullOrEmpty(requestCookie))
{
// unable to find the cookie.
return null;
}
return _serializer.Deserialize(requestCookie);
}
public async Task<AntiForgeryToken> GetFormTokenAsync(HttpContext httpContext)
{
var form = await httpContext.Request.ReadFormAsync();
var value = form[_config.FormFieldName];
if (string.IsNullOrEmpty(value))
{
// did not exist
return null;
}
return _serializer.Deserialize(value);
}
public void SaveCookieToken(HttpContext httpContext, AntiForgeryToken token)
{
// Add the cookie to the request based context.
// This is useful if the cookie needs to be reloaded in the context of the same request.
var contextAccessor =
httpContext.RequestServices.GetRequiredService<IScopedInstance<AntiForgeryContext>>();
Debug.Assert(contextAccessor.Value == null, "AntiForgeryContext should be set only once per request.");
contextAccessor.Value = new AntiForgeryContext() { CookieToken = token };
var 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

@ -1,257 +0,0 @@
// 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;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Extensions;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders;
namespace Microsoft.AspNet.Mvc
{
internal sealed class AntiForgeryWorker
{
private readonly AntiForgeryOptions _config;
private readonly IAntiForgeryTokenSerializer _serializer;
private readonly IAntiForgeryTokenStore _tokenStore;
private readonly IAntiForgeryTokenValidator _validator;
private readonly IAntiForgeryTokenGenerator _generator;
private readonly IHtmlEncoder _htmlEncoder;
internal AntiForgeryWorker([NotNull] IAntiForgeryTokenSerializer serializer,
[NotNull] AntiForgeryOptions config,
[NotNull] IAntiForgeryTokenStore tokenStore,
[NotNull] IAntiForgeryTokenGenerator generator,
[NotNull] IAntiForgeryTokenValidator validator,
[NotNull] IHtmlEncoder htmlEncoder)
{
_serializer = serializer;
_config = config;
_tokenStore = tokenStore;
_generator = generator;
_validator = validator;
_htmlEncoder = htmlEncoder;
}
private void CheckSSLConfig(HttpContext httpContext)
{
if (_config.RequireSSL && !httpContext.Request.IsHttps)
{
throw new InvalidOperationException(Resources.AntiForgeryWorker_RequireSSL);
}
}
private AntiForgeryToken DeserializeToken(string serializedToken)
{
return (!string.IsNullOrEmpty(serializedToken))
? _serializer.Deserialize(serializedToken)
: null;
}
private AntiForgeryToken DeserializeTokenDoesNotThrow(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)
{
var user = httpContext.User;
if (user != null)
{
// We only support ClaimsIdentity.
return user.Identity as ClaimsIdentity;
}
}
return null;
}
private AntiForgeryToken GetCookieTokenDoesNotThrow(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 cookieToken = GetCookieTokenDoesNotThrow(httpContext);
var tokenSet = GetTokens(httpContext, cookieToken);
cookieToken = tokenSet.CookieToken;
var formToken = tokenSet.FormToken;
SaveCookieTokenAndHeader(httpContext, cookieToken);
// <input type="hidden" name="__AntiForgeryToken" value="..." />
var inputTag = new TagBuilder("input", _htmlEncoder)
{
Attributes =
{
{ "type", "hidden" },
{ "name", _config.FormFieldName },
{ "value", _serializer.Serialize(formToken) }
}
};
return inputTag;
}
// [ 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 cookieToken)
{
CheckSSLConfig(httpContext);
var deSerializedcookieToken = DeserializeTokenDoesNotThrow(cookieToken);
var tokenSet = GetTokens(httpContext, deSerializedcookieToken);
var serializedCookieToken = Serialize(tokenSet.CookieToken);
var serializedFormToken = Serialize(tokenSet.FormToken);
return new AntiForgeryTokenSet(serializedFormToken, serializedCookieToken);
}
private AntiForgeryTokenSetInternal GetTokens(HttpContext httpContext, AntiForgeryToken cookieToken)
{
var newCookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
if (newCookieToken != null)
{
cookieToken = newCookieToken;
}
var formToken = _generator.GenerateFormToken(
httpContext,
ExtractIdentity(httpContext),
cookieToken);
return new AntiForgeryTokenSetInternal()
{
// Note : The new cookie would be null if the old cookie is valid.
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);
}
/// <summary>
/// Generates and sets an anti-forgery cookie if one is not available or not valid. Also sets response headers.
/// </summary>
/// <param name="context">The HTTP context associated with the current call.</param>
public void SetCookieTokenAndHeader([NotNull] HttpContext httpContext)
{
CheckSSLConfig(httpContext);
var cookieToken = GetCookieTokenDoesNotThrow(httpContext);
cookieToken = ValidateAndGenerateNewCookieToken(cookieToken);
SaveCookieTokenAndHeader(httpContext, cookieToken);
}
// This method returns null if oldCookieToken is valid.
private AntiForgeryToken ValidateAndGenerateNewCookieToken(AntiForgeryToken cookieToken)
{
if (!_validator.IsCookieTokenValid(cookieToken))
{
// Need to make sure we're always operating with a good cookie token.
var newCookieToken = _generator.GenerateCookieToken();
Debug.Assert(_validator.IsCookieTokenValid(newCookieToken));
return newCookieToken;
}
return null;
}
private void SaveCookieTokenAndHeader(
[NotNull] HttpContext httpContext,
AntiForgeryToken cookieToken)
{
if (cookieToken != null)
{
// Persist the new cookie if it is not null.
_tokenStore.SaveCookieToken(httpContext, cookieToken);
}
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.Set("X-Frame-Options", "SAMEORIGIN");
}
}
private class AntiForgeryTokenSetInternal
{
public AntiForgeryToken FormToken { get; set; }
public AntiForgeryToken CookieToken { get; set; }
}
}
}

View File

@ -1,117 +0,0 @@
// 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;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
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 static readonly RandomNumberGenerator _randomNumberGenerator = RandomNumberGenerator.Create();
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(nameof(bitLength));
}
if (data == null || data.Length != bitLength / 8)
{
throw new ArgumentOutOfRangeException(nameof(data));
}
_data = data;
}
public int BitLength
{
get
{
return checked(_data.Length * 8);
}
}
private string DebuggerString
{
get
{
var sb = new StringBuilder("0x", 2 + (_data.Length * 2));
for (var 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;
}
Debug.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.
Debug.Assert(_data != null && _data.Length >= (32 / 8));
return BitConverter.ToInt32(_data, 0);
}
private static byte[] GenerateNewToken(int bitLength)
{
var data = new byte[bitLength / 8];
_randomNumberGenerator.GetBytes(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;
}
var areEqual = true;
for (var i = 0; i < a.Length; i++)
{
areEqual &= (a[i] == b[i]);
}
return areEqual;
}
}
}

View File

@ -1,26 +0,0 @@
// 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 Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A default <see cref="IAntiForgeryAdditionalDataProvider"/> implementation.
/// </summary>
public class DefaultAntiForgeryAdditionalDataProvider : IAntiForgeryAdditionalDataProvider
{
/// <inheritdoc />
public virtual string GetAdditionalData(HttpContext context)
{
return string.Empty;
}
/// <inheritdoc />
public virtual bool ValidateAdditionalData(HttpContext context, string additionalData)
{
// Default implementation does not understand anything but empty data.
return string.IsNullOrEmpty(additionalData);
}
}
}

View File

@ -1,80 +0,0 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Default implementation of <see cref="IClaimUidExtractor"/>.
/// </summary>
public class DefaultClaimUidExtractor : IClaimUidExtractor
{
/// <inheritdoc />
public string ExtractClaimUid(ClaimsIdentity claimsIdentity)
{
if (claimsIdentity == null || !claimsIdentity.IsAuthenticated)
{
// Skip anonymous users
return null;
}
var uniqueIdentifierParameters = GetUniqueIdentifierParameters(claimsIdentity);
var claimUidBytes = ComputeSHA256(uniqueIdentifierParameters);
return Convert.ToBase64String(claimUidBytes);
}
internal static IEnumerable<string> GetUniqueIdentifierParameters(ClaimsIdentity claimsIdentity)
{
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())
{
var retVal = sha256.ComputeHash(ms.ToArray(), 0, checked((int)ms.Length));
return retVal;
}
}
}
}
}
}

View File

@ -1,39 +0,0 @@
// 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 Microsoft.AspNet.Http;
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

@ -1,22 +0,0 @@
// 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.Security.Claims;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Mvc
{
// Provides configuration information about the anti-forgery system.
internal interface IAntiForgeryTokenGenerator
{
// 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

@ -1,12 +0,0 @@
// 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.
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

@ -1,16 +0,0 @@
// 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.Threading.Tasks;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Mvc
{
// Provides an abstraction around how tokens are persisted and retrieved for a request
internal interface IAntiForgeryTokenStore
{
AntiForgeryToken GetCookieToken(HttpContext httpContext);
Task<AntiForgeryToken> GetFormTokenAsync(HttpContext httpContext);
void SaveCookieToken(HttpContext httpContext, AntiForgeryToken token);
}
}

View File

@ -1,23 +0,0 @@
// 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.Security.Claims;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Mvc
{
// Provides an abstraction around something that can validate anti-XSRF tokens
internal interface IAntiForgeryTokenValidator
{
// 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

@ -1,20 +0,0 @@
// 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.Security.Claims;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// This interface can extract unique identifers for a claims-based identity.
/// </summary>
public interface IClaimUidExtractor
{
/// <summary>
/// Extracts claims identifier.
/// </summary>
/// <param name="identity">The <see cref="ClaimsIdentity"/>.</param>
/// <returns>The claims identifier.</returns>
string ExtractClaimUid(ClaimsIdentity identity);
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Antiforgery;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc
@ -13,8 +14,8 @@ namespace Microsoft.AspNet.Mvc
public IFilter CreateInstance(IServiceProvider serviceProvider)
{
var antiForgery = serviceProvider.GetRequiredService<AntiForgery>();
return new ValidateAntiForgeryTokenAuthorizationFilter(antiForgery);
var antiforgery = serviceProvider.GetRequiredService<IAntiforgery>();
return new ValidateAntiforgeryTokenAuthorizationFilter(antiforgery);
}
}
}

View File

@ -2,22 +2,23 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.AspNet.Antiforgery;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
{
public class ValidateAntiForgeryTokenAuthorizationFilter : IAsyncAuthorizationFilter
public class ValidateAntiforgeryTokenAuthorizationFilter : IAsyncAuthorizationFilter
{
private readonly AntiForgery _antiForgery;
private readonly IAntiforgery _antiforgery;
public ValidateAntiForgeryTokenAuthorizationFilter([NotNull] AntiForgery antiForgery)
public ValidateAntiforgeryTokenAuthorizationFilter([NotNull] IAntiforgery antiforgery)
{
_antiForgery = antiForgery;
_antiforgery = antiforgery;
}
public async Task OnAuthorizationAsync([NotNull] AuthorizationContext context)
{
await _antiForgery.ValidateAsync(context.HttpContext);
await _antiforgery.ValidateRequestAsync(context.HttpContext);
}
}
}

View File

@ -42,150 +42,6 @@ namespace Microsoft.AspNet.Mvc.Extensions
return string.Format(CultureInfo.CurrentCulture, GetString("ObjectResult_MatchAllContentType"), 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.
/// </summary>
internal static string AntiForgeryToken_DeserializationFailed
{
get { return GetString("AntiForgeryToken_DeserializationFailed"); }
}
/// <summary>
/// The anti-forgery token could not be decrypted.
/// </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 AntiForgeryOptions.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 AntiForgeryOptions.RequireSsl = true, but the current request is not an SSL request.
/// </summary>
internal static string FormatAntiForgeryWorker_RequireSSL()
{
return GetString("AntiForgeryWorker_RequireSSL");
}
/// <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>
@ -234,22 +90,6 @@ namespace Microsoft.AspNet.Mvc.Extensions
return string.Format(CultureInfo.CurrentCulture, GetString("ClaimUidExtractor_ClaimNotPresent"), p0);
}
/// <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>
/// The class ReflectedActionFilterEndPoint only supports ReflectedActionDescriptors.
/// </summary>

View File

@ -9,6 +9,7 @@ using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.AspNet.Antiforgery;
using Microsoft.AspNet.Mvc.Extensions;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
@ -25,7 +26,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
private static readonly MethodInfo ConvertEnumFromStringMethod =
typeof(DefaultHtmlGenerator).GetTypeInfo().GetDeclaredMethod(nameof(ConvertEnumFromString));
private readonly AntiForgery _antiForgery;
private readonly IAntiforgery _antiforgery;
private readonly IClientModelValidatorProvider _clientModelValidatorProvider;
private readonly IModelMetadataProvider _metadataProvider;
private readonly IUrlHelper _urlHelper;
@ -34,20 +35,20 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <summary>
/// Initializes a new instance of the <see cref="DefaultHtmlGenerator"/> class.
/// </summary>
/// <param name="antiForgery">The <see cref="AntiForgery"/> instance which is used to generate anti-forgery
/// <param name="antiforgery">The <see cref="IAntiforgery"/> instance which is used to generate antiforgery
/// tokens.</param>
/// <param name="optionsAccessor">The accessor for <see cref="MvcOptions"/>.</param>
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="urlHelper">The <see cref="IUrlHelper"/>.</param>
/// <param name="htmlEncoder">The <see cref="IHtmlEncoder"/>.</param>
public DefaultHtmlGenerator(
[NotNull] AntiForgery antiForgery,
[NotNull] IAntiforgery antiforgery,
[NotNull] IOptions<MvcViewOptions> optionsAccessor,
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] IUrlHelper urlHelper,
[NotNull] IHtmlEncoder htmlEncoder)
{
_antiForgery = antiForgery;
_antiforgery = antiforgery;
var clientValidatorProviders = optionsAccessor.Options.ClientModelValidatorProviders;
_clientModelValidatorProvider = new CompositeClientModelValidatorProvider(clientValidatorProviders);
_metadataProvider = metadataProvider;
@ -95,10 +96,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
/// <inheritdoc />
public virtual TagBuilder GenerateAntiForgery([NotNull] ViewContext viewContext)
public virtual HtmlString GenerateAntiforgery([NotNull] ViewContext viewContext)
{
var tagBuilder = _antiForgery.GetHtml(viewContext.HttpContext);
return tagBuilder;
var tag = _antiforgery.GetHtml(viewContext.HttpContext);
return new HtmlString(tag);
}
/// <inheritdoc />

View File

@ -221,13 +221,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// <inheritdoc />
public HtmlString AntiForgeryToken()
{
var tagBuilder = _htmlGenerator.GenerateAntiForgery(ViewContext);
if (tagBuilder == null)
{
return HtmlString.Empty;
}
return tagBuilder.ToHtmlString(TagRenderMode.SelfClosing);
var html = _htmlGenerator.GenerateAntiforgery(ViewContext);
return html ?? HtmlString.Empty;
}
/// <inheritdoc />

View File

@ -31,7 +31,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
object routeValues,
object htmlAttributes);
TagBuilder GenerateAntiForgery([NotNull] ViewContext viewContext);
/// <summary>
/// Genrate an &lt;input type="hidden".../&gt; element containing an antiforgery token.
/// </summary>
/// <param name="viewContext">The <see cref="ViewContext"/> instance for the current scope.</param>
/// <returns>An <see cref="HtmlString"/> instance for the &lt;input type="hidden".../&gt; element.</returns>
HtmlString GenerateAntiforgery([NotNull] ViewContext viewContext);
/// <summary>
/// Generate a &lt;input type="checkbox".../&gt; element.

View File

@ -99,7 +99,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
object htmlAttributes);
/// <summary>
/// Returns a &lt;hidden&gt; element (anti-forgery token) that will be validated when the containing
/// Returns a &lt;hidden&gt; element (antiforgery token) that will be validated when the containing
/// &lt;form&gt; is submitted.
/// </summary>
/// <returns>A new <see cref="HtmlString"/> containing the &lt;hidden&gt; element.</returns>

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@ -123,33 +123,6 @@
<data name="ObjectResult_MatchAllContentType" xml:space="preserve">
<value>The content-type '{0}' added in the '{1}' property is invalid. Media types which match all types or match all subtypes are not supported.</value>
</data>
<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.</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 AntiForgeryOptions.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>
@ -159,9 +132,6 @@
<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

@ -5,6 +5,7 @@
"warningsAsErrors": true
},
"dependencies": {
"Microsoft.AspNet.Antiforgery": "1.0.0-*",
"Microsoft.AspNet.Authentication": "1.0.0-*",
"Microsoft.AspNet.Authorization": "1.0.0-*",
"Microsoft.AspNet.Cors.Core": "1.0.0-*",

View File

@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Antiforgery;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Razor.Internal;
using Microsoft.AspNet.Mvc.Rendering;
@ -781,8 +782,8 @@ namespace Microsoft.AspNet.Mvc.Razor
/// <remarks>The value returned is a token value that allows FlushAsync to work directly in an HTML
/// section. However the value does not represent the rendered content.
/// This method also writes out headers, so any modifications to headers must be done before
/// <see cref="FlushAsync"/> is called. For example, call <see cref="SetAntiForgeryCookieAndHeader"/> to send
/// anti-forgery cookie token and X-Frame-Options header to client before this method flushes headers out.
/// <see cref="FlushAsync"/> is called. For example, call <see cref="SetAntiforgeryCookieAndHeader"/> to send
/// antiforgery cookie token and X-Frame-Options header to client before this method flushes headers out.
/// </remarks>
public async Task<HtmlString> FlushAsync()
{
@ -843,15 +844,15 @@ namespace Microsoft.AspNet.Mvc.Razor
}
/// <summary>
/// Sets anti-forgery cookie and X-Frame-Options header on the response.
/// Sets antiforgery cookie and X-Frame-Options header on the response.
/// </summary>
/// <returns>A <see cref="HtmlString"/> that returns a <see cref="HtmlString.Empty"/>.</returns>
/// <remarks> Call this method to send anti-forgery cookie token and X-Frame-Options header to client
/// <remarks> Call this method to send antiforgery cookie token and X-Frame-Options header to client
/// before <see cref="FlushAsync"/> flushes the headers. </remarks>
public virtual HtmlString SetAntiForgeryCookieAndHeader()
public virtual HtmlString SetAntiforgeryCookieAndHeader()
{
var antiForgery = Context.RequestServices.GetRequiredService<AntiForgery>();
antiForgery.SetCookieTokenAndHeader(Context);
var antiforgery = Context.RequestServices.GetRequiredService<IAntiforgery>();
antiforgery.SetCookieTokenAndHeader(Context);
return HtmlString.Empty;
}

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <see cref="ITagHelper"/> implementation targeting &lt;form&gt; elements.
/// </summary>
[TargetElement("form", Attributes = ActionAttributeName)]
[TargetElement("form", Attributes = AntiForgeryAttributeName)]
[TargetElement("form", Attributes = AntiforgeryAttributeName)]
[TargetElement("form", Attributes = ControllerAttributeName)]
[TargetElement("form", Attributes = RouteAttributeName)]
[TargetElement("form", Attributes = RouteValuesDictionaryName)]
@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
public class FormTagHelper : TagHelper
{
private const string ActionAttributeName = "asp-action";
private const string AntiForgeryAttributeName = "asp-anti-forgery";
private const string AntiforgeryAttributeName = "asp-antiforgery";
private const string ControllerAttributeName = "asp-controller";
private const string RouteAttributeName = "asp-route";
private const string RouteValuesDictionaryName = "asp-all-route-data";
@ -56,11 +56,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
public string Controller { get; set; }
/// <summary>
/// Whether the anti-forgery token should be generated.
/// Whether the antiforgery token should be generated.
/// </summary>
/// <value>Defaults to <c>false</c> if user provides an <c>action</c> attribute; <c>true</c> otherwise.</value>
[HtmlAttributeName(AntiForgeryAttributeName)]
public bool? AntiForgery { get; set; }
[HtmlAttributeName(AntiforgeryAttributeName)]
public bool? Antiforgery { get; set; }
/// <summary>
/// Name of the route.
@ -80,7 +80,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <inheritdoc />
/// <remarks>
/// Does nothing if user provides an <c>action</c> attribute and <see cref="AntiForgery"/> is <c>null</c> or
/// Does nothing if user provides an <c>action</c> attribute and <see cref="Antiforgery"/> is <c>null</c> or
/// <c>false</c>.
/// </remarks>
/// <exception cref="InvalidOperationException">
@ -89,7 +89,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// </exception>
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var antiForgeryDefault = true;
var antiforgeryDefault = true;
// If "action" is already set, it means the user is attempting to use a normal <form>.
if (output.Attributes.ContainsName(HtmlActionAttributeName))
@ -107,9 +107,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
RouteValuesPrefix));
}
// User is using the FormTagHelper like a normal <form> tag. Anti-forgery default should be false to
// not force the anti-forgery token on the user.
antiForgeryDefault = false;
// User is using the FormTagHelper like a normal <form> tag. Antiforgery default should be false to
// not force the antiforgery token on the user.
antiforgeryDefault = false;
}
else
{
@ -158,12 +158,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
}
}
if (AntiForgery ?? antiForgeryDefault)
if (Antiforgery ?? antiforgeryDefault)
{
var antiForgeryTagBuilder = Generator.GenerateAntiForgery(ViewContext);
if (antiForgeryTagBuilder != null)
var antiforgeryTag = Generator.GenerateAntiforgery(ViewContext);
if (antiforgeryTag != null)
{
output.PostContent.Append(antiForgeryTagBuilder.ToString(TagRenderMode.SelfClosing));
output.PostContent.Append(antiforgeryTag.ToString());
}
}
}

View File

@ -33,18 +33,6 @@ namespace Microsoft.Framework.DependencyInjection
return services;
}
/// <summary>
/// Configures a set of <see cref="AntiForgeryOptions"/> for the application.
/// </summary>
/// <param name="services">The services available in the application.</param>
/// <param name="setupAction">The <see cref="AntiForgeryOptions"/> which need to be configured.</param>
public static void ConfigureAntiforgery(
[NotNull] this IServiceCollection services,
[NotNull] Action<AntiForgeryOptions> setupAction)
{
services.Configure(setupAction);
}
/// <summary>
/// Configures a set of <see cref="MvcFormatterMappingOptions"/> for the application.
/// </summary>
@ -245,11 +233,6 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAddSingleton<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>();
services.TryAddTransient<IViewComponentHelper, DefaultViewComponentHelper>();
// Security and Authorization
services.TryAddSingleton<IClaimUidExtractor, DefaultClaimUidExtractor>();
services.TryAddSingleton<AntiForgery, AntiForgery>();
services.TryAddSingleton<IAntiForgeryAdditionalDataProvider, DefaultAntiForgeryAdditionalDataProvider>();
// Api Description
services.TryAddSingleton<IApiDescriptionGroupCollectionProvider, ApiDescriptionGroupCollectionProvider>();
services.TryAddEnumerable(
@ -287,6 +270,7 @@ namespace Microsoft.Framework.DependencyInjection
private static void ConfigureDefaultServices(IServiceCollection services)
{
services.AddDataProtection();
services.AddAntiforgery();
services.AddCors();
services.AddAuthorization();
services.AddWebEncoders();

View File

@ -1,35 +0,0 @@
// 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 Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class AntiForgeryOptionsTests
{
[Fact]
public void CookieName_SettingNullValue_Throws()
{
// Arrange
var options = new AntiForgeryOptions();
// Act & Assert
var ex = Assert.Throws<ArgumentNullException>(() => options.CookieName = null);
Assert.Equal("The 'CookieName' property of 'Microsoft.AspNet.Mvc.AntiForgeryOptions' must not be null." +
Environment.NewLine + "Parameter name: value", ex.Message);
}
[Fact]
public void FormFieldName_SettingNullValue_Throws()
{
// Arrange
var options = new AntiForgeryOptions();
// Act & Assert
var ex = Assert.Throws<ArgumentNullException>(() => options.FormFieldName = null);
Assert.Equal("The 'FormFieldName' property of 'Microsoft.AspNet.Mvc.AntiForgeryOptions' must not be null." +
Environment.NewLine + "Parameter name: value", ex.Message);
}
}
}

View File

@ -1,181 +0,0 @@
// 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.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.DataProtection;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class AntiForgeryTokenSerializerTest
{
private static readonly Mock<IDataProtector> _dataProtector = GetDataProtector();
private static readonly BinaryBlob _claimUid = new BinaryBlob(256, new byte[] { 0x6F, 0x16, 0x48, 0xE9, 0x72, 0x49, 0xAA, 0x58, 0x75, 0x40, 0x36, 0xA6, 0x7E, 0x24, 0x8C, 0xF0, 0x44, 0xF0, 0x7E, 0xCF, 0xB0, 0xED, 0x38, 0x75, 0x56, 0xCE, 0x02, 0x9A, 0x4F, 0x9A, 0x40, 0xE0 });
private static readonly BinaryBlob _securityToken = new BinaryBlob(128, new byte[] { 0x70, 0x5E, 0xED, 0xCC, 0x7D, 0x42, 0xF1, 0xD6, 0xB3, 0xB9, 0x8A, 0x59, 0x36, 0x25, 0xBB, 0x4C });
private const byte _salt = 0x05;
[Theory]
[InlineData(
"01" // Version
+ "705EEDCC7D42F1D6B3B9" // SecurityToken
// (WRONG!) Stream ends too early
)]
[InlineData(
"01" // Version
+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
+ "01" // IsSessionToken
+ "00" // (WRONG!) Too much data in stream
)]
[InlineData(
"02" // (WRONG! - must be 0x01) Version
+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
+ "01" // IsSessionToken
)]
[InlineData(
"01" // Version
+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
+ "00" // IsSessionToken
+ "00" // IsClaimsBased
+ "05" // Username length header
+ "0000" // (WRONG!) Too little data in stream
)]
public void Deserialize_BadToken_Throws(string serializedToken)
{
// Arrange
var testSerializer = new AntiForgeryTokenSerializer(_dataProtector.Object);
// Act & assert
var ex = Assert.Throws<InvalidOperationException>(() => testSerializer.Deserialize(serializedToken));
Assert.Equal(@"The anti-forgery token could not be decrypted.", ex.Message);
}
[Fact]
public void Serialize_FieldToken_WithClaimUid_TokenRoundTripSuccessful()
{
// Arrange
var testSerializer = new AntiForgeryTokenSerializer(_dataProtector.Object);
//"01" // Version
//+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
//+ "00" // IsSessionToken
//+ "01" // IsClaimsBased
//+ "6F1648E97249AA58754036A67E248CF044F07ECFB0ED387556CE029A4F9A40E0" // ClaimUid
//+ "05" // AdditionalData length header
//+ "E282AC3437"; // AdditionalData ("€47") as UTF8
var token = new AntiForgeryToken()
{
SecurityToken = _securityToken,
IsSessionToken = false,
ClaimUid = _claimUid,
AdditionalData = "€47"
};
// Act
var actualSerializedData = testSerializer.Serialize(token);
var deserializedToken = testSerializer.Deserialize(actualSerializedData);
// Assert
AssertTokensEqual(token, deserializedToken);
_dataProtector.Verify();
}
[Fact]
public void Serialize_FieldToken_WithUsername_TokenRoundTripSuccessful()
{
// Arrange
var testSerializer = new AntiForgeryTokenSerializer(_dataProtector.Object);
//"01" // Version
//+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
//+ "00" // IsSessionToken
//+ "00" // IsClaimsBased
//+ "08" // Username length header
//+ "4AC3A972C3B46D65" // Username ("Jérôme") as UTF8
//+ "05" // AdditionalData length header
//+ "E282AC3437"; // AdditionalData ("€47") as UTF8
var token = new AntiForgeryToken()
{
SecurityToken = _securityToken,
IsSessionToken = false,
Username = "Jérôme",
AdditionalData = "€47"
};
// Act
var actualSerializedData = testSerializer.Serialize(token);
var deserializedToken = testSerializer.Deserialize(actualSerializedData);
// Assert
AssertTokensEqual(token, deserializedToken);
_dataProtector.Verify();
}
[Fact]
public void Serialize_SessionToken_TokenRoundTripSuccessful()
{
// Arrange
var testSerializer = new AntiForgeryTokenSerializer(_dataProtector.Object);
//"01" // Version
//+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken
//+ "01"; // IsSessionToken
var token = new AntiForgeryToken()
{
SecurityToken = _securityToken,
IsSessionToken = true
};
// Act
string actualSerializedData = testSerializer.Serialize(token);
var deserializedToken = testSerializer.Deserialize(actualSerializedData);
// Assert
AssertTokensEqual(token, deserializedToken);
_dataProtector.Verify();
}
private static Mock<IDataProtector> GetDataProtector()
{
var mockCryptoSystem = new Mock<IDataProtector>();
mockCryptoSystem.Setup(o => o.Protect(It.IsAny<byte[]>()))
.Returns<byte[]>(Protect)
.Verifiable();
mockCryptoSystem.Setup(o => o.Unprotect(It.IsAny<byte[]>()))
.Returns<byte[]>(UnProtect)
.Verifiable();
return mockCryptoSystem;
}
private static byte[] Protect(byte[] data)
{
var input = new List<byte>(data);
input.Add(_salt);
return input.ToArray();
}
private static byte[] UnProtect(byte[] data)
{
var salt = data[data.Length - 1];
if (salt != _salt)
{
throw new ArgumentException("Invalid salt value in data");
}
return data.Take(data.Length - 1).ToArray();
}
private static void AssertTokensEqual(AntiForgeryToken expected, AntiForgeryToken actual)
{
Assert.NotNull(expected);
Assert.NotNull(actual);
Assert.Equal(expected.AdditionalData, actual.AdditionalData);
Assert.Equal(expected.ClaimUid, actual.ClaimUid);
Assert.Equal(expected.IsSessionToken, actual.IsSessionToken);
Assert.Equal(expected.SecurityToken, actual.SecurityToken);
Assert.Equal(expected.Username, actual.Username);
}
}
}

View File

@ -1,425 +0,0 @@
// 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.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class AntiForgeryTokenStoreTest
{
private readonly string _cookieName = "cookie-name";
[Fact]
public void GetCookieToken_CookieDoesNotExist_ReturnsNull()
{
// Arrange
var requestCookies = new Mock<IReadableStringCollection>();
requestCookies
.Setup(o => o.Get(It.IsAny<string>()))
.Returns(string.Empty);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext
.Setup(o => o.Request.Cookies)
.Returns(requestCookies.Object);
var contextAccessor = new ScopedInstance<AntiForgeryContext>();
mockHttpContext.SetupGet(o => o.RequestServices)
.Returns(GetServiceProvider(contextAccessor));
var config = new AntiForgeryOptions()
{
CookieName = _cookieName
};
var tokenStore = new AntiForgeryTokenStore(
config: config,
serializer: null);
// Act
var token = tokenStore.GetCookieToken(mockHttpContext.Object);
// Assert
Assert.Null(token);
}
[Fact]
public void GetCookieToken_CookieIsMissingInRequest_LooksUpCookieInAntiForgeryContext()
{
// Arrange
var requestCookies = new Mock<IReadableStringCollection>();
requestCookies
.Setup(o => o.Get(It.IsAny<string>()))
.Returns(string.Empty);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext
.Setup(o => o.Request.Cookies)
.Returns(requestCookies.Object);
var contextAccessor = new ScopedInstance<AntiForgeryContext>();
mockHttpContext.SetupGet(o => o.RequestServices)
.Returns(GetServiceProvider(contextAccessor));
// add a cookie explicitly.
var cookie = new AntiForgeryToken();
contextAccessor.Value = new AntiForgeryContext() { CookieToken = cookie };
var config = new AntiForgeryOptions()
{
CookieName = _cookieName
};
var tokenStore = new AntiForgeryTokenStore(
config: config,
serializer: null);
// Act
var token = tokenStore.GetCookieToken(mockHttpContext.Object);
// Assert
Assert.Equal(cookie, token);
}
[Fact]
public void GetCookieToken_CookieIsEmpty_ReturnsNull()
{
// Arrange
var mockHttpContext = GetMockHttpContext(_cookieName, string.Empty);
var config = new AntiForgeryOptions()
{
CookieName = _cookieName
};
var tokenStore = new AntiForgeryTokenStore(
config: config,
serializer: null);
// Act
var token = tokenStore.GetCookieToken(mockHttpContext);
// Assert
Assert.Null(token);
}
[Fact]
public void GetCookieToken_CookieIsInvalid_PropagatesException()
{
// Arrange
var mockHttpContext = GetMockHttpContext(_cookieName, "invalid-value");
var config = new AntiForgeryOptions()
{
CookieName = _cookieName
};
var expectedException = new InvalidOperationException("some exception");
var mockSerializer = new Mock<IAntiForgeryTokenSerializer>();
mockSerializer
.Setup(o => o.Deserialize("invalid-value"))
.Throws(expectedException);
var tokenStore = new AntiForgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act & assert
var ex = Assert.Throws<InvalidOperationException>(() => tokenStore.GetCookieToken(mockHttpContext));
Assert.Same(expectedException, ex);
}
[Fact]
public void GetCookieToken_CookieIsValid_ReturnsToken()
{
// Arrange
var expectedToken = new AntiForgeryToken();
var mockHttpContext = GetMockHttpContext(_cookieName, "valid-value");
var config = new AntiForgeryOptions()
{
CookieName = _cookieName
};
var mockSerializer = new Mock<IAntiForgeryTokenSerializer>();
mockSerializer
.Setup(o => o.Deserialize("valid-value"))
.Returns(expectedToken);
var tokenStore = new AntiForgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act
AntiForgeryToken retVal = tokenStore.GetCookieToken(mockHttpContext);
// Assert
Assert.Same(expectedToken, retVal);
}
[Fact]
public async Task GetFormToken_FormFieldIsEmpty_ReturnsNull()
{
// Arrange
var mockHttpContext = new Mock<HttpContext>();
var requestContext = new Mock<HttpRequest>();
var formCollection = new Mock<IFormCollection>();
formCollection.Setup(f => f["form-field-name"]).Returns(string.Empty);
requestContext.Setup(o => o.ReadFormAsync(CancellationToken.None))
.Returns(Task.FromResult(formCollection.Object));
mockHttpContext.Setup(o => o.Request)
.Returns(requestContext.Object);
var config = new AntiForgeryOptions()
{
FormFieldName = "form-field-name"
};
var tokenStore = new AntiForgeryTokenStore(
config: config,
serializer: null);
// Act
var token = await tokenStore.GetFormTokenAsync(mockHttpContext.Object);
// Assert
Assert.Null(token);
}
[Fact]
public async Task GetFormToken_FormFieldIsInvalid_PropagatesException()
{
// Arrange
var formCollection = new Mock<IFormCollection>();
formCollection.Setup(f => f["form-field-name"]).Returns("invalid-value");
var requestContext = new Mock<HttpRequest>();
requestContext.Setup(o => o.ReadFormAsync(CancellationToken.None))
.Returns(Task.FromResult(formCollection.Object));
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Request)
.Returns(requestContext.Object);
var config = new AntiForgeryOptions()
{
FormFieldName = "form-field-name"
};
var expectedException = new InvalidOperationException("some exception");
var mockSerializer = new Mock<IAntiForgeryTokenSerializer>();
mockSerializer.Setup(o => o.Deserialize("invalid-value"))
.Throws(expectedException);
var tokenStore = new AntiForgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act & assert
var ex =
await
Assert.ThrowsAsync<InvalidOperationException>(
async () => await tokenStore.GetFormTokenAsync(mockHttpContext.Object));
Assert.Same(expectedException, ex);
}
[Fact]
public async Task GetFormToken_FormFieldIsValid_ReturnsToken()
{
// Arrange
var expectedToken = new AntiForgeryToken();
// Arrange
var mockHttpContext = new Mock<HttpContext>();
var requestContext = new Mock<HttpRequest>();
var formCollection = new Mock<IFormCollection>();
formCollection.Setup(f => f["form-field-name"]).Returns("valid-value");
requestContext.Setup(o => o.ReadFormAsync(CancellationToken.None))
.Returns(Task.FromResult(formCollection.Object));
mockHttpContext.Setup(o => o.Request)
.Returns(requestContext.Object);
var config = new AntiForgeryOptions()
{
FormFieldName = "form-field-name"
};
var mockSerializer = new Mock<IAntiForgeryTokenSerializer>();
mockSerializer.Setup(o => o.Deserialize("valid-value"))
.Returns(expectedToken);
var tokenStore = new AntiForgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act
var retVal = await tokenStore.GetFormTokenAsync(mockHttpContext.Object);
// Assert
Assert.Same(expectedToken, retVal);
}
[Theory]
[InlineData(true, true)]
[InlineData(false, null)]
public void SaveCookieToken(bool requireSsl, bool? expectedCookieSecureFlag)
{
// Arrange
var token = new AntiForgeryToken();
var mockCookies = new Mock<IResponseCookies>();
bool defaultCookieSecureValue = expectedCookieSecureFlag ?? false; // pulled from config; set by ctor
var cookies = new MockResponseCookieCollection();
cookies.Count = 0;
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Response.Cookies)
.Returns(cookies);
var contextAccessor = new ScopedInstance<AntiForgeryContext>();
mockHttpContext.SetupGet(o => o.RequestServices)
.Returns(GetServiceProvider(contextAccessor));
var mockSerializer = new Mock<IAntiForgeryTokenSerializer>();
mockSerializer.Setup(o => o.Serialize(token))
.Returns("serialized-value");
var config = new AntiForgeryOptions()
{
CookieName = _cookieName,
RequireSSL = requireSsl
};
var tokenStore = new AntiForgeryTokenStore(
config: config,
serializer: mockSerializer.Object);
// Act
tokenStore.SaveCookieToken(mockHttpContext.Object, token);
// Assert
Assert.Equal(1, cookies.Count);
Assert.NotNull(contextAccessor.Value.CookieToken);
Assert.NotNull(cookies);
Assert.Equal(_cookieName, cookies.Key);
Assert.Equal("serialized-value", cookies.Value);
Assert.True(cookies.Options.HttpOnly);
Assert.Equal(defaultCookieSecureValue, cookies.Options.Secure);
}
private HttpContext GetMockHttpContext(string cookieName, string cookieValue)
{
var requestCookies = new MockCookieCollection(new Dictionary<string, string>() { { cookieName, cookieValue } });
var request = new Mock<HttpRequest>();
request.Setup(o => o.Cookies)
.Returns(requestCookies);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Request)
.Returns(request.Object);
var contextAccessor = new ScopedInstance<AntiForgeryContext>();
mockHttpContext.SetupGet(o => o.RequestServices)
.Returns(GetServiceProvider(contextAccessor));
return mockHttpContext.Object;
}
private static IServiceProvider GetServiceProvider(IScopedInstance<AntiForgeryContext> contextAccessor)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddInstance<IScopedInstance<AntiForgeryContext>>(contextAccessor);
return serviceCollection.BuildServiceProvider();
}
private class MockResponseCookieCollection : IResponseCookies
{
public string Key { get; set; }
public string Value { get; set; }
public CookieOptions Options { get; set; }
public int Count { get; set; }
public void Append(string key, string value, CookieOptions options)
{
this.Key = key;
this.Value = value;
this.Options = options;
this.Count++;
}
public void Append(string key, string value)
{
throw new NotImplementedException();
}
public void Delete(string key, CookieOptions options)
{
throw new NotImplementedException();
}
public void Delete(string key)
{
throw new NotImplementedException();
}
}
private class MockCookieCollection : IReadableStringCollection
{
private Dictionary<string, string> _dictionary;
public int Count
{
get
{
return _dictionary.Count;
}
}
public ICollection<string> Keys
{
get
{
return _dictionary.Keys;
}
}
public MockCookieCollection(Dictionary<string, string> dictionary)
{
_dictionary = dictionary;
}
public static MockCookieCollection GetDummyInstance(string key, string value)
{
return new MockCookieCollection(new Dictionary<string, string>() { { key, value } });
}
public string Get(string key)
{
return this[key];
}
public IList<string> GetValues(string key)
{
throw new NotImplementedException();
}
public bool ContainsKey(string key)
{
return _dictionary.ContainsKey(key);
}
public string this[string key]
{
get { return _dictionary[key]; }
}
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
}
}

View File

@ -1,132 +0,0 @@
// 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 Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class AntiForgeryTokenTest
{
[Fact]
public void AdditionalDataProperty()
{
// Arrange
var token = new AntiForgeryToken();
// Act & assert - 1
Assert.Equal("", token.AdditionalData);
// Act & assert - 2
token.AdditionalData = "additional data";
Assert.Equal("additional data", token.AdditionalData);
// Act & assert - 3
token.AdditionalData = null;
Assert.Equal("", token.AdditionalData);
}
[Fact]
public void ClaimUidProperty()
{
// Arrange
var token = new AntiForgeryToken();
// Act & assert - 1
Assert.Null(token.ClaimUid);
// Act & assert - 2
BinaryBlob blob = new BinaryBlob(32);
token.ClaimUid = blob;
Assert.Equal(blob, token.ClaimUid);
// Act & assert - 3
token.ClaimUid = null;
Assert.Null(token.ClaimUid);
}
[Fact]
public void IsSessionTokenProperty()
{
// Arrange
var token = new AntiForgeryToken();
// Act & assert - 1
Assert.False(token.IsSessionToken);
// Act & assert - 2
token.IsSessionToken = true;
Assert.True(token.IsSessionToken);
// Act & assert - 3
token.IsSessionToken = false;
Assert.False(token.IsSessionToken);
}
[Fact]
public void UsernameProperty()
{
// Arrange
var token = new AntiForgeryToken();
// Act & assert - 1
Assert.Equal("", token.Username);
// Act & assert - 2
token.Username = "my username";
Assert.Equal("my username", token.Username);
// Act & assert - 3
token.Username = null;
Assert.Equal("", token.Username);
}
[Fact]
public void SecurityTokenProperty_GetsAutopopulated()
{
// Arrange
var token = new AntiForgeryToken();
// Act
var securityToken = token.SecurityToken;
// Assert
Assert.NotNull(securityToken);
Assert.Equal(AntiForgeryToken.SecurityTokenBitLength, securityToken.BitLength);
// check that we're not making a new one each property call
Assert.Equal(securityToken, token.SecurityToken);
}
[Fact]
public void SecurityTokenProperty_PropertySetter_DoesNotUseDefaults()
{
// Arrange
var token = new AntiForgeryToken();
// Act
var securityToken = new BinaryBlob(64);
token.SecurityToken = securityToken;
// Assert
Assert.Equal(securityToken, token.SecurityToken);
}
[Fact]
public void SecurityTokenProperty_PropertySetter_DoesNotAllowNulls()
{
// Arrange
var token = new AntiForgeryToken();
// Act
token.SecurityToken = null;
var securityToken = token.SecurityToken;
// Assert
Assert.NotNull(securityToken);
Assert.Equal(AntiForgeryToken.SecurityTokenBitLength, securityToken.BitLength);
// check that we're not making a new one each property call
Assert.Equal(securityToken, token.SecurityToken);
}
}
}

View File

@ -1,584 +0,0 @@
// 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.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Framework.WebEncoders.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class AntiForgeryWorkerTest
{
[Fact]
public async Task ChecksSSL_ValidateAsync_Throws()
{
// Arrange
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Request.IsHttps)
.Returns(false);
var config = new AntiForgeryOptions()
{
RequireSSL = true
};
var worker = new AntiForgeryWorker(
config: config,
serializer: null,
tokenStore: null,
generator: null,
validator: null,
htmlEncoder: new CommonTestEncoder());
// Act & assert
var ex =
await
Assert.ThrowsAsync<InvalidOperationException>(
async () => await worker.ValidateAsync(mockHttpContext.Object));
Assert.Equal(
@"The anti-forgery system has the configuration value AntiForgeryOptions.RequireSsl = true, " +
"but the current request is not an SSL request.",
ex.Message);
}
[Fact]
public void ChecksSSL_Validate_Throws()
{
// Arrange
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Request.IsHttps)
.Returns(false);
var config = new AntiForgeryOptions()
{
RequireSSL = true
};
var worker = new AntiForgeryWorker(
config: config,
serializer: null,
tokenStore: null,
generator: null,
validator: null,
htmlEncoder: new CommonTestEncoder());
// Act & assert
var ex = Assert.Throws<InvalidOperationException>(
() => worker.Validate(mockHttpContext.Object, cookieToken: null, formToken: null));
Assert.Equal(
@"The anti-forgery system has the configuration value AntiForgeryOptions.RequireSsl = true, " +
"but the current request is not an SSL request.",
ex.Message);
}
[Fact]
public void ChecksSSL_GetFormInputElement_Throws()
{
// Arrange
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Request.IsHttps)
.Returns(false);
var config = new AntiForgeryOptions()
{
RequireSSL = true
};
var worker = new AntiForgeryWorker(
config: config,
serializer: null,
tokenStore: null,
generator: null,
validator: null,
htmlEncoder: new CommonTestEncoder());
// Act & assert
var ex = Assert.Throws<InvalidOperationException>(() => worker.GetFormInputElement(mockHttpContext.Object));
Assert.Equal(
@"The anti-forgery system has the configuration value AntiForgeryOptions.RequireSsl = true, " +
"but the current request is not an SSL request.",
ex.Message);
}
[Fact]
public void ChecksSSL_GetTokens_Throws()
{
// Arrange
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.Request.IsHttps)
.Returns(false);
var config = new AntiForgeryOptions()
{
RequireSSL = true
};
var worker = new AntiForgeryWorker(
config: config,
serializer: null,
tokenStore: null,
generator: null,
validator: null,
htmlEncoder: new CommonTestEncoder());
// Act & assert
var ex = Assert.Throws<InvalidOperationException>(() =>
worker.GetTokens(mockHttpContext.Object, "cookie-token"));
Assert.Equal(
@"The anti-forgery system has the configuration value AntiForgeryOptions.RequireSsl = true, " +
"but the current request is not an SSL request.",
ex.Message);
}
[Fact]
public void GetFormInputElement_ExistingInvalidCookieToken_GeneratesANewCookieAndAnAntiForgeryToken()
{
// Arrange
var config = new AntiForgeryOptions()
{
FormFieldName = "form-field-name"
};
// Make sure the existing cookie is invalid.
var context = GetAntiForgeryWorkerContext(config, isOldCookieValid: false);
var worker = GetAntiForgeryWorker(context);
// Act
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
// Assert
Assert.Equal(@"<input name=""HtmlEncode[[form-field-name]]"" type=""HtmlEncode[[hidden]]"" " +
@"value=""HtmlEncode[[serialized-form-token]]"" />",
inputElement.ToString(TagRenderMode.SelfClosing));
context.TokenStore.Verify();
}
[Fact]
public void GetFormInputElement_ExistingInvalidCookieToken_SwallowsExceptions()
{
// Arrange
var config = new AntiForgeryOptions()
{
FormFieldName = "form-field-name"
};
// Make sure the existing cookie is invalid.
var context = GetAntiForgeryWorkerContext(config, isOldCookieValid: false);
var worker = GetAntiForgeryWorker(context);
// This will cause the cookieToken to be null.
context.TokenStore.Setup(o => o.GetCookieToken(context.HttpContext.Object))
.Throws(new Exception("should be swallowed"));
// Setup so that the null cookie token returned is treated as invalid.
context.TokenProvider.Setup(o => o.IsCookieTokenValid(null))
.Returns(false);
// Act
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
// Assert
Assert.Equal(@"<input name=""HtmlEncode[[form-field-name]]"" type=""HtmlEncode[[hidden]]"" " +
@"value=""HtmlEncode[[serialized-form-token]]"" />",
inputElement.ToString(TagRenderMode.SelfClosing));
context.TokenStore.Verify();
}
[Fact]
public void GetFormInputElement_ExistingValidCookieToken_GeneratesAnAntiForgeryToken()
{
// Arrange
var options = new AntiForgeryOptions()
{
FormFieldName = "form-field-name"
};
// Make sure the existing cookie is valid and use the same cookie for the mock Token Provider.
var context = GetAntiForgeryWorkerContext(options, useOldCookie: true, isOldCookieValid: true);
var worker = GetAntiForgeryWorker(context);
// Act
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
// Assert
Assert.Equal(@"<input name=""HtmlEncode[[form-field-name]]"" type=""HtmlEncode[[hidden]]"" " +
@"value=""HtmlEncode[[serialized-form-token]]"" />",
inputElement.ToString(TagRenderMode.SelfClosing));
}
[Theory]
[InlineData(false, "SAMEORIGIN")]
[InlineData(true, null)]
public void GetFormInputElement_AddsXFrameOptionsHeader(bool suppressXFrameOptions, string expectedHeaderValue)
{
// Arrange
var options = new AntiForgeryOptions()
{
SuppressXFrameOptionsHeader = suppressXFrameOptions
};
// Genreate a new cookie.
var context = GetAntiForgeryWorkerContext(options, useOldCookie: false, isOldCookieValid: false);
var worker = GetAntiForgeryWorker(context);
// Act
var inputElement = worker.GetFormInputElement(context.HttpContext.Object);
// Assert
string xFrameOptions = context.HttpContext.Object.Response.Headers["X-Frame-Options"];
Assert.Equal(expectedHeaderValue, xFrameOptions);
}
[Fact]
public void GetTokens_ExistingInvalidCookieToken_GeneratesANewCookieTokenAndANewFormToken()
{
// Arrange
// Genreate a new cookie.
var context = GetAntiForgeryWorkerContext(
new AntiForgeryOptions(),
useOldCookie: false,
isOldCookieValid: false);
var worker = GetAntiForgeryWorker(context);
// Act
var tokenset = worker.GetTokens(context.HttpContext.Object, "serialized-old-cookie-token");
// Assert
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
Assert.Equal("serialized-form-token", tokenset.FormToken);
}
[Fact]
public void GetTokens_ExistingInvalidCookieToken_SwallowsExceptions()
{
// Arrange
// Make sure the existing cookie is invalid.
var context = GetAntiForgeryWorkerContext(
new AntiForgeryOptions(),
useOldCookie: false,
isOldCookieValid: false);
// This will cause the cookieToken to be null.
context.TokenSerializer.Setup(o => o.Deserialize("serialized-old-cookie-token"))
.Throws(new Exception("should be swallowed"));
// Setup so that the null cookie token returned is treated as invalid.
context.TokenProvider.Setup(o => o.IsCookieTokenValid(null))
.Returns(false);
var worker = GetAntiForgeryWorker(context);
// Act
var tokenset = worker.GetTokens(context.HttpContext.Object, "serialized-old-cookie-token");
// Assert
Assert.Equal("serialized-new-cookie-token", tokenset.CookieToken);
Assert.Equal("serialized-form-token", tokenset.FormToken);
}
[Fact]
public void GetTokens_ExistingValidCookieToken_GeneratesANewFormToken()
{
// Arrange
var context = GetAntiForgeryWorkerContext(
new AntiForgeryOptions(),
useOldCookie: true,
isOldCookieValid: true);
context.TokenStore = null;
var worker = GetAntiForgeryWorker(context);
// Act
var tokenset = worker.GetTokens(context.HttpContext.Object, "serialized-old-cookie-token");
// Assert
Assert.Null(tokenset.CookieToken);
Assert.Equal("serialized-form-token", tokenset.FormToken);
}
[Fact]
public void Validate_FromInvalidStrings_Throws()
{
// Arrange
var context = GetAntiForgeryWorkerContext(new AntiForgeryOptions());
context.TokenSerializer.Setup(o => o.Deserialize("cookie-token"))
.Returns(context.TestTokenSet.OldCookieToken);
context.TokenSerializer.Setup(o => o.Deserialize("form-token"))
.Returns(context.TestTokenSet.FormToken);
context.TokenProvider.Setup(o => o.ValidateTokens(
context.HttpContext.Object,
context.HttpContext.Object.User.Identity as ClaimsIdentity,
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Throws(new InvalidOperationException("my-message"));
context.TokenStore = null;
var worker = GetAntiForgeryWorker(context);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => worker.Validate(context.HttpContext.Object, "cookie-token", "form-token"));
Assert.Equal("my-message", ex.Message);
}
[Fact]
public void Validate_FromValidStrings_TokensValidatedSuccessfully()
{
// Arrange
var context = GetAntiForgeryWorkerContext(new AntiForgeryOptions());
context.TokenSerializer.Setup(o => o.Deserialize("cookie-token"))
.Returns(context.TestTokenSet.OldCookieToken);
context.TokenSerializer.Setup(o => o.Deserialize("form-token"))
.Returns(context.TestTokenSet.FormToken);
context.TokenProvider.Setup(o => o.ValidateTokens(
context.HttpContext.Object,
context.HttpContext.Object.User.Identity as ClaimsIdentity,
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Verifiable();
context.TokenStore = null;
var worker = GetAntiForgeryWorker(context);
// Act
worker.Validate(context.HttpContext.Object, "cookie-token", "form-token");
// Assert
context.TokenProvider.Verify();
}
[Fact]
public async Task Validate_FromStore_Failure()
{
// Arrange
var context = GetAntiForgeryWorkerContext(new AntiForgeryOptions());
context.TokenProvider.Setup(o => o.ValidateTokens(
context.HttpContext.Object,
context.HttpContext.Object.User.Identity as ClaimsIdentity,
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Throws(new InvalidOperationException("my-message"));
context.TokenSerializer = null;
var worker = GetAntiForgeryWorker(context);
// Act & assert
var ex =
await
Assert.ThrowsAsync<InvalidOperationException>(
async () => await worker.ValidateAsync(context.HttpContext.Object));
Assert.Equal("my-message", ex.Message);
}
[Fact]
public async Task Validate_FromStore_Success()
{
// Arrange
var context = GetAntiForgeryWorkerContext(new AntiForgeryOptions());
context.TokenProvider.Setup(o => o.ValidateTokens(
context.HttpContext.Object,
context.HttpContext.Object.User.Identity as ClaimsIdentity,
context.TestTokenSet.OldCookieToken, context.TestTokenSet.FormToken))
.Verifiable();
context.TokenSerializer = null;
var worker = GetAntiForgeryWorker(context);
// Act
await worker.ValidateAsync(context.HttpContext.Object);
// Assert
context.TokenProvider.Verify();
}
[Theory]
[InlineData(false, "SAMEORIGIN")]
[InlineData(true, null)]
public void SetCookieTokenAndHeader_AddsXFrameOptionsHeader(
bool suppressXFrameOptions,
string expectedHeaderValue)
{
// Arrange
var options = new AntiForgeryOptions()
{
SuppressXFrameOptionsHeader = suppressXFrameOptions
};
// Genreate a new cookie.
var context = GetAntiForgeryWorkerContext(options, useOldCookie: false, isOldCookieValid: false);
var worker = GetAntiForgeryWorker(context);
// Act
worker.SetCookieTokenAndHeader(context.HttpContext.Object);
// Assert
var xFrameOptions = context.HttpContext.Object.Response.Headers["X-Frame-Options"];
Assert.Equal(expectedHeaderValue, xFrameOptions);
}
private AntiForgeryWorker GetAntiForgeryWorker(AntiForgeryWorkerContext context)
{
return new AntiForgeryWorker(
config: context.Options,
serializer: context.TokenSerializer != null ? context.TokenSerializer.Object : null,
tokenStore: context.TokenStore != null ? context.TokenStore.Object : null,
generator: context.TokenProvider != null ? context.TokenProvider.Object : null,
validator: context.TokenProvider != null ? context.TokenProvider.Object : null,
htmlEncoder: new CommonTestEncoder());
}
private Mock<HttpContext> GetHttpContext(bool setupResponse = true)
{
var identity = new ClaimsIdentity("some-auth");
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(o => o.User)
.Returns(new ClaimsPrincipal(identity));
if (setupResponse)
{
var mockResponse = new Mock<HttpResponse>();
mockResponse.Setup(r => r.Headers)
.Returns(new HeaderDictionary(new Dictionary<string, string[]>()));
mockHttpContext.Setup(o => o.Response)
.Returns(mockResponse.Object);
}
return mockHttpContext;
}
private Mock<IAntiForgeryTokenProvider> GetTokenProvider(
HttpContext context,
TestTokenSet testTokenSet,
bool useOldCookie,
bool isOldCookieValid = true,
bool isNewCookieValid = true)
{
var oldCookieToken = testTokenSet.OldCookieToken;
var newCookieToken = testTokenSet.NewCookieToken;
var formToken = testTokenSet.FormToken;
var mockValidator = new Mock<IAntiForgeryTokenProvider>(MockBehavior.Strict);
mockValidator.Setup(o => o.GenerateFormToken(
context,
context.User.Identity as ClaimsIdentity,
useOldCookie ? oldCookieToken : newCookieToken))
.Returns(formToken);
mockValidator.Setup(o => o.IsCookieTokenValid(oldCookieToken))
.Returns(isOldCookieValid);
mockValidator.Setup(o => o.IsCookieTokenValid(newCookieToken))
.Returns(isNewCookieValid);
mockValidator.Setup(o => o.GenerateCookieToken())
.Returns(useOldCookie ? oldCookieToken : newCookieToken);
return mockValidator;
}
private Mock<IAntiForgeryTokenStore> GetTokenStore(
HttpContext context,
TestTokenSet testTokenSet,
bool saveNewCookie = true)
{
var oldCookieToken = testTokenSet.OldCookieToken;
var formToken = testTokenSet.FormToken;
var mockTokenStore = new Mock<IAntiForgeryTokenStore>(MockBehavior.Strict);
mockTokenStore.Setup(o => o.GetCookieToken(context))
.Returns(oldCookieToken);
mockTokenStore.Setup(o => o.GetFormTokenAsync(context))
.Returns(Task.FromResult(formToken));
if (saveNewCookie)
{
var newCookieToken = testTokenSet.NewCookieToken;
mockTokenStore.Setup(o => o.SaveCookieToken(context, newCookieToken))
.Verifiable();
}
return mockTokenStore;
}
private Mock<IAntiForgeryTokenSerializer> GetTokenSerializer(TestTokenSet testTokenSet)
{
var oldCookieToken = testTokenSet.OldCookieToken;
var newCookieToken = testTokenSet.NewCookieToken;
var formToken = testTokenSet.FormToken;
var mockSerializer = new Mock<IAntiForgeryTokenSerializer>(MockBehavior.Strict);
mockSerializer.Setup(o => o.Serialize(formToken))
.Returns("serialized-form-token");
mockSerializer.Setup(o => o.Deserialize("serialized-old-cookie-token"))
.Returns(oldCookieToken);
mockSerializer.Setup(o => o.Serialize(newCookieToken))
.Returns("serialized-new-cookie-token");
return mockSerializer;
}
private TestTokenSet GetTokenSet(bool isOldCookieTokenSessionToken = true, bool isNewCookieSessionToken = true)
{
return new TestTokenSet()
{
FormToken = new AntiForgeryToken() { IsSessionToken = false },
OldCookieToken = new AntiForgeryToken() { IsSessionToken = isOldCookieTokenSessionToken },
NewCookieToken = new AntiForgeryToken() { IsSessionToken = isNewCookieSessionToken },
};
}
private AntiForgeryWorkerContext GetAntiForgeryWorkerContext(
AntiForgeryOptions config,
bool useOldCookie = false,
bool isOldCookieValid = true)
{
// Arrange
var mockHttpContext = GetHttpContext();
var testTokenSet = GetTokenSet(isOldCookieTokenSessionToken: true, isNewCookieSessionToken: true);
var mockSerializer = GetTokenSerializer(testTokenSet);
var mockTokenStore = GetTokenStore(mockHttpContext.Object, testTokenSet);
var mockTokenProvider = GetTokenProvider(
mockHttpContext.Object,
testTokenSet,
useOldCookie: useOldCookie,
isOldCookieValid: isOldCookieValid);
return new AntiForgeryWorkerContext()
{
Options = config,
HttpContext = mockHttpContext,
TokenProvider = mockTokenProvider,
TokenSerializer = mockSerializer,
TokenStore = mockTokenStore,
TestTokenSet = testTokenSet
};
}
private class TestTokenSet
{
public AntiForgeryToken FormToken { get; set; }
public string FormTokenString { get; set; }
public AntiForgeryToken OldCookieToken { get; set; }
public string OldCookieTokenString { get; set; }
public AntiForgeryToken NewCookieToken { get; set; }
public string NewCookieTokenString { get; set; }
}
private class AntiForgeryWorkerContext
{
public AntiForgeryOptions Options { get; set; }
public TestTokenSet TestTokenSet { get; set; }
public Mock<HttpContext> HttpContext { get; set; }
public Mock<IAntiForgeryTokenProvider> TokenProvider { get; set; }
public Mock<IAntiForgeryTokenStore> TokenStore { get; set; }
public Mock<IAntiForgeryTokenSerializer> TokenSerializer { get; set; }
}
}
}

View File

@ -1,129 +0,0 @@
// 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 Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class BinaryBlobTest
{
[Fact]
public void Ctor_BitLength()
{
// Act
var blob = new BinaryBlob(bitLength: 64);
var data = blob.GetData();
// Assert
Assert.Equal(64, blob.BitLength);
Assert.Equal(64 / 8, data.Length);
Assert.NotEqual(new byte[64 / 8], data); // should not be a zero-filled array
}
[Theory]
[InlineData(24)]
[InlineData(33)]
public void Ctor_BitLength_Bad(int bitLength)
{
// Act & assert
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new BinaryBlob(bitLength));
Assert.Equal("bitLength", ex.ParamName);
}
[Fact]
public void Ctor_BitLength_ProducesDifferentValues()
{
// Act
var blobA = new BinaryBlob(bitLength: 64);
var blobB = new BinaryBlob(bitLength: 64);
// Assert
Assert.NotEqual(blobA.GetData(), blobB.GetData());
}
[Fact]
public void Ctor_Data()
{
// Arrange
var expectedData = new byte[] { 0x01, 0x02, 0x03, 0x04 };
// Act
var blob = new BinaryBlob(32, expectedData);
// Assert
Assert.Equal(32, blob.BitLength);
Assert.Equal(expectedData, blob.GetData());
}
[Theory]
[InlineData((object[])null)]
[InlineData(new byte[] { 0x01, 0x02, 0x03 })]
public void Ctor_Data_Bad(byte[] data)
{
// Act & assert
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new BinaryBlob(32, data));
Assert.Equal("data", ex.ParamName);
}
[Fact]
public void Equals_DifferentData_ReturnsFalse()
{
// Arrange
object blobA = new BinaryBlob(32, new byte[] { 0x01, 0x02, 0x03, 0x04 });
object blobB = new BinaryBlob(32, new byte[] { 0x04, 0x03, 0x02, 0x01 });
// Act & assert
Assert.NotEqual(blobA, blobB);
}
[Fact]
public void Equals_NotABlob_ReturnsFalse()
{
// Arrange
object blobA = new BinaryBlob(32);
object blobB = "hello";
// Act & assert
Assert.NotEqual(blobA, blobB);
}
[Fact]
public void Equals_Null_ReturnsFalse()
{
// Arrange
object blobA = new BinaryBlob(32);
object blobB = null;
// Act & assert
Assert.NotEqual(blobA, blobB);
}
[Fact]
public void Equals_SameData_ReturnsTrue()
{
// Arrange
object blobA = new BinaryBlob(32, new byte[] { 0x01, 0x02, 0x03, 0x04 });
object blobB = new BinaryBlob(32, new byte[] { 0x01, 0x02, 0x03, 0x04 });
// Act & assert
Assert.Equal(blobA, blobB);
}
[Fact]
public void GetHashCodeTest()
{
// Arrange
var blobData = new byte[] { 0x01, 0x02, 0x03, 0x04 };
var expectedHashCode = BitConverter.ToInt32(blobData, 0);
var blob = new BinaryBlob(32, blobData);
// Act
var actualHashCode = blob.GetHashCode();
// Assert
Assert.Equal(expectedHashCode, actualHashCode);
}
}
}

View File

@ -1,108 +0,0 @@
// 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.Linq;
using System.Security.Claims;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class ClaimUidExtractorTest
{
[Fact]
public void ExtractClaimUid_NullIdentity()
{
// Arrange
IClaimUidExtractor extractor = new DefaultClaimUidExtractor();
// Act
var claimUid = extractor.ExtractClaimUid(null);
// Assert
Assert.Null(claimUid);
}
[Fact]
public void ExtractClaimUid_Unauthenticated()
{
// Arrange
IClaimUidExtractor extractor = new DefaultClaimUidExtractor();
var mockIdentity = new Mock<ClaimsIdentity>();
mockIdentity.Setup(o => o.IsAuthenticated)
.Returns(false);
// Act
var claimUid = extractor.ExtractClaimUid(mockIdentity.Object);
// Assert
Assert.Null(claimUid);
}
[Fact]
public void ExtractClaimUid_ClaimsIdentity()
{
// Arrange
var mockIdentity = new Mock<ClaimsIdentity>();
mockIdentity.Setup(o => o.IsAuthenticated)
.Returns(true);
IClaimUidExtractor extractor = new DefaultClaimUidExtractor();
// Act
var claimUid = extractor.ExtractClaimUid(mockIdentity.Object);
// Assert
Assert.NotNull(claimUid);
Assert.Equal("47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=", claimUid);
}
[Fact]
public void DefaultUniqueClaimTypes_NotPresent_SerializesAllClaimTypes()
{
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim(ClaimTypes.Email, "someone@antifrogery.com"));
identity.AddClaim(new Claim(ClaimTypes.GivenName, "some"));
identity.AddClaim(new Claim(ClaimTypes.Surname, "one"));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, String.Empty));
// Arrange
var claimsIdentity = (ClaimsIdentity)identity;
// Act
var identiferParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(claimsIdentity)
.ToArray();
var claims = claimsIdentity.Claims.ToList();
claims.Sort((a, b) => string.Compare(a.Type, b.Type, StringComparison.Ordinal));
// Assert
int index = 0;
foreach (var claim in claims)
{
Assert.True(String.Equals(identiferParameters[index++], claim.Type, StringComparison.Ordinal));
Assert.True(String.Equals(identiferParameters[index++], claim.Value, StringComparison.Ordinal));
}
}
[Fact]
public void DefaultUniqueClaimTypes_Present()
{
// Arrange
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim("fooClaim", "fooClaimValue"));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "nameIdentifierValue"));
// Act
var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(identity);
// Assert
Assert.Equal(new string[]
{
ClaimTypes.NameIdentifier,
"nameIdentifierValue",
}, uniqueIdentifierParameters);
}
}
}

View File

@ -1,10 +0,0 @@
// 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.
namespace Microsoft.AspNet.Mvc.Core.Test
{
// A TokenProvider that can be passed to MoQ
internal interface IAntiForgeryTokenProvider : IAntiForgeryTokenValidator, IAntiForgeryTokenGenerator
{
}
}

View File

@ -1,582 +0,0 @@
// 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.Security.Claims;
using System.Security.Cryptography;
using Microsoft.AspNet.Http;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class TokenProviderTest
{
[Fact]
public void GenerateCookieToken()
{
// Arrange
var tokenProvider = new AntiForgeryTokenProvider(
config: null,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
var retVal = tokenProvider.GenerateCookieToken();
// Assert
Assert.NotNull(retVal);
}
[Fact]
public void GenerateFormToken_AnonymousUser()
{
// Arrange
var cookieToken = new AntiForgeryToken() { IsSessionToken = true };
var httpContext = new Mock<HttpContext>().Object;
var mockIdentity = new Mock<ClaimsIdentity>();
mockIdentity.Setup(o => o.IsAuthenticated)
.Returns(false);
var config = new AntiForgeryOptions();
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
var fieldToken = tokenProvider.GenerateFormToken(httpContext, mockIdentity.Object, cookieToken);
// Assert
Assert.NotNull(fieldToken);
Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken);
Assert.False(fieldToken.IsSessionToken);
Assert.Empty(fieldToken.Username);
Assert.Null(fieldToken.ClaimUid);
Assert.Empty(fieldToken.AdditionalData);
}
[Fact]
public void GenerateFormToken_AuthenticatedWithoutUsernameAndNoAdditionalData_NoAdditionalData()
{
// Arrange
var cookieToken = new AntiForgeryToken()
{
IsSessionToken = true
};
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new MyAuthenticatedIdentityWithoutUsername();
var config = new AntiForgeryOptions();
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: claimUidExtractor,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.GenerateFormToken(httpContext, identity, cookieToken));
Assert.Equal(
"The provided identity of type " +
"'Microsoft.AspNet.Mvc.Core.Test.TokenProviderTest+MyAuthenticatedIdentityWithoutUsername' " +
"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.",
ex.Message);
}
[Fact]
public void GenerateFormToken_AuthenticatedWithoutUsername_WithAdditionalData()
{
// Arrange
var cookieToken = new AntiForgeryToken() { IsSessionToken = true };
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new MyAuthenticatedIdentityWithoutUsername();
var mockAdditionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.GetAdditionalData(httpContext))
.Returns("additional-data");
var config = new AntiForgeryOptions();
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: claimUidExtractor,
additionalDataProvider: mockAdditionalDataProvider.Object);
// Act
var fieldToken = tokenProvider.GenerateFormToken(httpContext, identity, cookieToken);
// Assert
Assert.NotNull(fieldToken);
Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken);
Assert.False(fieldToken.IsSessionToken);
Assert.Empty(fieldToken.Username);
Assert.Null(fieldToken.ClaimUid);
Assert.Equal("additional-data", fieldToken.AdditionalData);
}
[Fact]
public void GenerateFormToken_ClaimsBasedIdentity()
{
// Arrange
var cookieToken = new AntiForgeryToken() { IsSessionToken = true };
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity("some-identity");
var config = new AntiForgeryOptions();
byte[] data = new byte[256 / 8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(data);
}
var base64ClaimUId = Convert.ToBase64String(data);
var expectedClaimUid = new BinaryBlob(256, data);
var mockClaimUidExtractor = new Mock<IClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
.Returns(base64ClaimUId);
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
// Act
var fieldToken = tokenProvider.GenerateFormToken(httpContext, identity, cookieToken);
// Assert
Assert.NotNull(fieldToken);
Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken);
Assert.False(fieldToken.IsSessionToken);
Assert.Equal("", fieldToken.Username);
Assert.Equal(expectedClaimUid, fieldToken.ClaimUid);
Assert.Equal("", fieldToken.AdditionalData);
}
[Fact]
public void GenerateFormToken_RegularUserWithUsername()
{
// Arrange
var cookieToken = new AntiForgeryToken() { IsSessionToken = true };
var httpContext = new Mock<HttpContext>().Object;
var mockIdentity = new Mock<ClaimsIdentity>();
mockIdentity.Setup(o => o.IsAuthenticated)
.Returns(true);
mockIdentity.Setup(o => o.Name)
.Returns("my-username");
var config = new AntiForgeryOptions();
IClaimUidExtractor claimUidExtractor = new Mock<IClaimUidExtractor>().Object;
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: claimUidExtractor,
additionalDataProvider: null);
// Act
var fieldToken = tokenProvider.GenerateFormToken(httpContext, mockIdentity.Object, cookieToken);
// Assert
Assert.NotNull(fieldToken);
Assert.Equal(cookieToken.SecurityToken, fieldToken.SecurityToken);
Assert.False(fieldToken.IsSessionToken);
Assert.Equal("my-username", fieldToken.Username);
Assert.Null(fieldToken.ClaimUid);
Assert.Empty(fieldToken.AdditionalData);
}
[Fact]
public void IsCookieTokenValid_FieldToken_ReturnsFalse()
{
// Arrange
var cookieToken = new AntiForgeryToken()
{
IsSessionToken = false
};
var tokenProvider = new AntiForgeryTokenProvider(
config: null,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
// Assert
Assert.False(retVal);
}
[Fact]
public void IsCookieTokenValid_NullToken_ReturnsFalse()
{
// Arrange
AntiForgeryToken cookieToken = null;
var tokenProvider = new AntiForgeryTokenProvider(
config: null,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
// Assert
Assert.False(retVal);
}
[Fact]
public void IsCookieTokenValid_ValidToken_ReturnsTrue()
{
// Arrange
var cookieToken = new AntiForgeryToken()
{
IsSessionToken = true
};
var tokenProvider = new AntiForgeryTokenProvider(
config: null,
claimUidExtractor: null,
additionalDataProvider: null);
// Act
bool retVal = tokenProvider.IsCookieTokenValid(cookieToken);
// Assert
Assert.True(retVal);
}
[Fact]
public void ValidateTokens_SessionTokenMissing()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
AntiForgeryToken sessionToken = null;
var fieldtoken = new AntiForgeryToken() { IsSessionToken = false };
var config = new AntiForgeryOptions()
{
CookieName = "my-cookie-name"
};
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(@"The required anti-forgery cookie ""my-cookie-name"" is not present.", ex.Message);
}
[Fact]
public void ValidateTokens_FieldTokenMissing()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
var sessionToken = new AntiForgeryToken() { IsSessionToken = true };
AntiForgeryToken fieldtoken = null;
var config = new AntiForgeryOptions()
{
FormFieldName = "my-form-field-name"
};
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(@"The required anti-forgery form field ""my-form-field-name"" is not present.", ex.Message);
}
[Fact]
public void ValidateTokens_FieldAndSessionTokensSwapped()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
var sessionToken = new AntiForgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiForgeryToken() { IsSessionToken = false };
var config = new AntiForgeryOptions()
{
CookieName = "my-cookie-name",
FormFieldName = "my-form-field-name"
};
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: null);
// Act & assert
var ex1 =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, fieldtoken, fieldtoken));
Assert.Equal(
"Validation of the provided anti-forgery token failed. " +
@"The cookie ""my-cookie-name"" and the form field ""my-form-field-name"" were swapped.",
ex1.Message);
var ex2 =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, sessionToken));
Assert.Equal(
"Validation of the provided anti-forgery token failed. " +
@"The cookie ""my-cookie-name"" and the form field ""my-form-field-name"" were swapped.",
ex2.Message);
}
[Fact]
public void ValidateTokens_FieldAndSessionTokensHaveDifferentSecurityKeys()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
ClaimsIdentity identity = new Mock<ClaimsIdentity>().Object;
var sessionToken = new AntiForgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiForgeryToken() { IsSessionToken = false };
var tokenProvider = new AntiForgeryTokenProvider(
config: null,
claimUidExtractor: null,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(@"The anti-forgery cookie token and form field token do not match.", ex.Message);
}
[Theory]
[InlineData("the-user", "the-other-user")]
[InlineData("http://example.com/uri-casing", "http://example.com/URI-casing")]
[InlineData("https://example.com/secure-uri-casing", "https://example.com/secure-URI-casing")]
public void ValidateTokens_UsernameMismatch(string identityUsername, string embeddedUsername)
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity(identityUsername);
var sessionToken = new AntiForgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiForgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
Username = embeddedUsername,
IsSessionToken = false
};
var mockClaimUidExtractor = new Mock<IClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
.Returns((string)null);
var tokenProvider = new AntiForgeryTokenProvider(
config: null,
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(
@"The provided anti-forgery token was meant for user """ + embeddedUsername +
@""", but the current user is """ + identityUsername + @""".", ex.Message);
}
[Fact]
public void ValidateTokens_ClaimUidMismatch()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity("the-user");
var sessionToken = new AntiForgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiForgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
IsSessionToken = false,
ClaimUid = new BinaryBlob(256)
};
var differentToken = new BinaryBlob(256);
var mockClaimUidExtractor = new Mock<IClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
.Returns(Convert.ToBase64String(differentToken.GetData()));
var tokenProvider = new AntiForgeryTokenProvider(
config: null,
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(
@"The provided anti-forgery token was meant for a different claims-based user than the current user.",
ex.Message);
}
[Fact]
public void ValidateTokens_AdditionalDataRejected()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = new ClaimsIdentity();
var sessionToken = new AntiForgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiForgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
Username = String.Empty,
IsSessionToken = false,
AdditionalData = "some-additional-data"
};
var mockAdditionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
.Returns(false);
var config = new AntiForgeryOptions();
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: mockAdditionalDataProvider.Object);
// Act & assert
var ex =
Assert.Throws<InvalidOperationException>(
() => tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken));
Assert.Equal(@"The provided anti-forgery token failed a custom data check.", ex.Message);
}
[Fact]
public void ValidateTokens_Success_AnonymousUser()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = new ClaimsIdentity();
var sessionToken = new AntiForgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiForgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
Username = String.Empty,
IsSessionToken = false,
AdditionalData = "some-additional-data"
};
var mockAdditionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
.Returns(true);
var config = new AntiForgeryOptions();
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: null,
additionalDataProvider: mockAdditionalDataProvider.Object);
// Act
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
// Assert
// Nothing to assert - if we got this far, success!
}
[Fact]
public void ValidateTokens_Success_AuthenticatedUserWithUsername()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity("the-user");
var sessionToken = new AntiForgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiForgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
Username = "THE-USER",
IsSessionToken = false,
AdditionalData = "some-additional-data"
};
var mockAdditionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
mockAdditionalDataProvider.Setup(o => o.ValidateAdditionalData(httpContext, "some-additional-data"))
.Returns(true);
var config = new AntiForgeryOptions();
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: new Mock<IClaimUidExtractor>().Object,
additionalDataProvider: mockAdditionalDataProvider.Object);
// Act
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
// Assert
// Nothing to assert - if we got this far, success!
}
[Fact]
public void ValidateTokens_Success_ClaimsBasedUser()
{
// Arrange
var httpContext = new Mock<HttpContext>().Object;
var identity = GetAuthenticatedIdentity("the-user");
var sessionToken = new AntiForgeryToken() { IsSessionToken = true };
var fieldtoken = new AntiForgeryToken()
{
SecurityToken = sessionToken.SecurityToken,
IsSessionToken = false,
ClaimUid = new BinaryBlob(256)
};
var mockClaimUidExtractor = new Mock<IClaimUidExtractor>();
mockClaimUidExtractor.Setup(o => o.ExtractClaimUid(identity))
.Returns(Convert.ToBase64String(fieldtoken.ClaimUid.GetData()));
var config = new AntiForgeryOptions();
var tokenProvider = new AntiForgeryTokenProvider(
config: config,
claimUidExtractor: mockClaimUidExtractor.Object,
additionalDataProvider: null);
// Act
tokenProvider.ValidateTokens(httpContext, identity, sessionToken, fieldtoken);
// Assert
// Nothing to assert - if we got this far, success!
}
private static ClaimsIdentity GetAuthenticatedIdentity(string identityUsername)
{
var claim = new Claim(ClaimsIdentity.DefaultNameClaimType, identityUsername);
return new ClaimsIdentity(new[] { claim }, "Some-Authentication");
}
private sealed class MyAuthenticatedIdentityWithoutUsername : ClaimsIdentity
{
public override bool IsAuthenticated
{
get { return true; }
}
public override string Name
{
get { return String.Empty; }
}
}
}
}

View File

@ -1,51 +0,0 @@
// 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 Microsoft.AspNet.DataProtection;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.WebEncoders.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test
{
public class ValidateAntiForgeryTokenAttributeTest
{
[Fact]
public void ValidationAttribute_ForwardsCallToValidateAntiForgeryTokenAuthorizationFilter()
{
// Arrange
var serviceCollection = new ServiceCollection();
serviceCollection.AddInstance<AntiForgery>(GetAntiForgeryInstance());
var serviceProvider = serviceCollection.BuildServiceProvider();
var attribute = new ValidateAntiForgeryTokenAttribute();
// Act
var filter = attribute.CreateInstance(serviceProvider);
// Assert
var validationFilter = filter as ValidateAntiForgeryTokenAuthorizationFilter;
Assert.NotNull(validationFilter);
}
private AntiForgery GetAntiForgeryInstance()
{
var claimExtractor = new Mock<IClaimUidExtractor>();
var dataProtectionProvider = new Mock<IDataProtectionProvider>();
var additionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
var optionsAccessor = new Mock<IOptions<AntiForgeryOptions>>();
var mockDataProtectionOptions = new Mock<IOptions<DataProtectionOptions>>();
mockDataProtectionOptions
.SetupGet(options => options.Options)
.Returns(Mock.Of<DataProtectionOptions>());
optionsAccessor.SetupGet(o => o.Options).Returns(new AntiForgeryOptions());
return new AntiForgery(claimExtractor.Object,
dataProtectionProvider.Object,
additionalDataProvider.Object,
optionsAccessor.Object,
new CommonTestEncoder(),
mockDataProtectionOptions.Object);
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.AspNet.Antiforgery;
using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.OptionsModel;
@ -544,20 +545,10 @@ namespace Microsoft.AspNet.Mvc.Rendering
// GetCurrentValues uses only the IModelMetadataProvider passed to the DefaultHtmlGenerator constructor.
private static IHtmlGenerator GetGenerator(IModelMetadataProvider metadataProvider)
{
var antiforgeryOptionsAccessor = new Mock<IOptions<AntiForgeryOptions>>();
antiforgeryOptionsAccessor.SetupGet(accessor => accessor.Options).Returns(new AntiForgeryOptions());
var mvcViewOptionsAccessor = new Mock<IOptions<MvcViewOptions>>();
mvcViewOptionsAccessor.SetupGet(accessor => accessor.Options).Returns(new MvcViewOptions());
var htmlEncoder = Mock.Of<IHtmlEncoder>();
var dataOptionsAccessor = new Mock<IOptions<DataProtectionOptions>>();
dataOptionsAccessor.SetupGet(accessor => accessor.Options).Returns(new DataProtectionOptions());
var antiForgery = new AntiForgery(
Mock.Of<IClaimUidExtractor>(),
Mock.Of<IDataProtectionProvider>(),
Mock.Of<IAntiForgeryAdditionalDataProvider>(),
antiforgeryOptionsAccessor.Object,
htmlEncoder,
dataOptionsAccessor.Object);
var antiforgery = Mock.Of<IAntiforgery>();
var optionsAccessor = new Mock<IOptions<MvcOptions>>();
optionsAccessor
@ -565,7 +556,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
.Returns(new MvcOptions());
return new DefaultHtmlGenerator(
antiForgery,
antiforgery,
mvcViewOptionsAccessor.Object,
metadataProvider,
Mock.Of<IUrlHelper>(),

View File

@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNet.Antiforgery;
using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
@ -236,7 +237,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
if (htmlGenerator == null)
{
htmlGenerator = new DefaultHtmlGenerator(
GetAntiForgeryInstance(),
Mock.Of<IAntiforgery>(),
optionsAccessor.Object,
provider,
urlHelper,
@ -307,26 +308,6 @@ namespace Microsoft.AspNet.Mvc.Rendering
return viewEngine.Object;
}
private static AntiForgery GetAntiForgeryInstance()
{
var claimExtractor = new Mock<IClaimUidExtractor>();
var dataProtectionProvider = new Mock<IDataProtectionProvider>();
var additionalDataProvider = new Mock<IAntiForgeryAdditionalDataProvider>();
var optionsAccessor = new Mock<IOptions<AntiForgeryOptions>>();
var mockDataProtectionOptions = new Mock<IOptions<DataProtectionOptions>>();
mockDataProtectionOptions
.SetupGet(options => options.Options)
.Returns(Mock.Of<DataProtectionOptions>());
optionsAccessor.SetupGet(o => o.Options).Returns(new AntiForgeryOptions());
return new AntiForgery(
claimExtractor.Object,
dataProtectionProvider.Object,
additionalDataProvider.Object,
optionsAccessor.Object,
new CommonTestEncoder(),
mockDataProtectionOptions.Object);
}
private static string FormatOutput(ModelExplorer modelExplorer)
{
var metadata = modelExplorer.Metadata;

View File

@ -10,18 +10,18 @@ using System.Xml.Linq;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public static class AntiForgeryTestHelper
public static class AntiforgeryTestHelper
{
public static string RetrieveAntiForgeryToken(string htmlContent, string actionUrl)
public static string RetrieveAntiforgeryToken(string htmlContent, string actionUrl)
{
return RetrieveAntiForgeryTokens(
return RetrieveAntiforgeryTokens(
htmlContent,
attribute => attribute.Value.EndsWith(actionUrl, StringComparison.OrdinalIgnoreCase) ||
attribute.Value.EndsWith($"HtmlEncode[[{ actionUrl }]]", StringComparison.OrdinalIgnoreCase))
.FirstOrDefault();
}
public static IEnumerable<string> RetrieveAntiForgeryTokens(
public static IEnumerable<string> RetrieveAntiforgeryTokens(
string htmlContent,
Func<XAttribute, bool> predicate = null)
{
@ -54,7 +54,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
}
}
public static CookieMetadata RetrieveAntiForgeryCookie(HttpResponseMessage response)
public static CookieMetadata RetrieveAntiforgeryCookie(HttpResponseMessage response)
{
var setCookieArray = response.Headers.GetValues("Set-Cookie").ToArray();
var cookie = setCookieArray[0].Split(';').First().Split('=');

View File

@ -13,11 +13,11 @@ using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class AntiForgeryTests
public class AntiforgeryTests
{
private const string SiteName = nameof(AntiForgeryWebSite);
private readonly Action<IApplicationBuilder> _app = new AntiForgeryWebSite.Startup().Configure;
private readonly Action<IServiceCollection> _configureServices = new AntiForgeryWebSite.Startup().ConfigureServices;
private const string SiteName = nameof(AntiforgeryTokenWebSite);
private readonly Action<IApplicationBuilder> _app = new AntiforgeryTokenWebSite.Startup().Configure;
private readonly Action<IServiceCollection> _configureServices = new AntiforgeryTokenWebSite.Startup().ConfigureServices;
[Fact]
public async Task MultipleAFTokensWithinTheSamePage_GeneratesASingleCookieToken()
@ -50,12 +50,12 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// do a get response.
var getResponse = await client.GetAsync("http://localhost/Account/Login");
var resposneBody = await getResponse.Content.ReadAsStringAsync();
var responseBody = await getResponse.Content.ReadAsStringAsync();
// Get the AF token for the second login. If the cookies are generated twice(i.e are different),
// this AF token will not work with the first cookie.
var formToken = AntiForgeryTestHelper.RetrieveAntiForgeryToken(resposneBody, "Account/UseFacebookLogin");
var cookieToken = AntiForgeryTestHelper.RetrieveAntiForgeryCookie(getResponse);
var formToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(responseBody, "Account/UseFacebookLogin");
var cookieToken = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse);
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login");
request.Headers.Add("Cookie", cookieToken.Key + "=" + cookieToken.Value);
@ -84,10 +84,10 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var client = server.CreateClient();
var getResponse = await client.GetAsync("http://localhost/Account/Login");
var resposneBody = await getResponse.Content.ReadAsStringAsync();
var formToken = AntiForgeryTestHelper.RetrieveAntiForgeryToken(resposneBody, "Account/Login");
var responseBody = await getResponse.Content.ReadAsStringAsync();
var formToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(responseBody, "Account/Login");
var cookieToken = AntiForgeryTestHelper.RetrieveAntiForgeryCookie(getResponse);
var cookieToken = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse);
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login");
request.Headers.Add("Cookie", cookieToken.Key + "=invalidCookie");
@ -105,7 +105,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Assert
var exception = response.GetServerException();
Assert.Equal("The anti-forgery token could not be decrypted.", exception.ExceptionMessage);
Assert.Equal("The antiforgery token could not be decrypted.", exception.ExceptionMessage);
}
[Fact]
@ -116,8 +116,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var client = server.CreateClient();
var getResponse = await client.GetAsync("http://localhost/Account/Login");
var resposneBody = await getResponse.Content.ReadAsStringAsync();
var cookieToken = AntiForgeryTestHelper.RetrieveAntiForgeryCookie(getResponse);
var responseBody = await getResponse.Content.ReadAsStringAsync();
var cookieToken = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse);
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login");
var formToken = "adsad";
request.Headers.Add("Cookie", cookieToken.Key + "=" + cookieToken.Value);
@ -135,7 +135,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Assert
var exception = response.GetServerException();
Assert.Equal("The anti-forgery token could not be decrypted.", exception.ExceptionMessage);
Assert.Equal("The antiforgery token could not be decrypted.", exception.ExceptionMessage);
}
[Fact]
@ -146,14 +146,14 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var client = server.CreateClient();
// do a get response.
// We do two requests to get two different sets of anti forgery cookie and token values.
// We do two requests to get two different sets of antiforgery cookie and token values.
var getResponse1 = await client.GetAsync("http://localhost/Account/Login");
var resposneBody1 = await getResponse1.Content.ReadAsStringAsync();
var formToken1 = AntiForgeryTestHelper.RetrieveAntiForgeryToken(resposneBody1, "Account/Login");
var responseBody1 = await getResponse1.Content.ReadAsStringAsync();
var formToken1 = AntiforgeryTestHelper.RetrieveAntiforgeryToken(responseBody1, "Account/Login");
var getResponse2 = await client.GetAsync("http://localhost/Account/Login");
var resposneBody2 = await getResponse2.Content.ReadAsStringAsync();
var cookieToken2 = AntiForgeryTestHelper.RetrieveAntiForgeryCookie(getResponse2);
var responseBody2 = await getResponse2.Content.ReadAsStringAsync();
var cookieToken2 = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse2);
var cookieToken = cookieToken2.Value;
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login");
@ -173,7 +173,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Assert
var exception = response.GetServerException();
Assert.Equal("The anti-forgery cookie token and form field token do not match.", exception.ExceptionMessage);
Assert.Equal("The antiforgery cookie token and form field token do not match.", exception.ExceptionMessage);
}
[Fact]
@ -185,9 +185,9 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// do a get response.
var getResponse = await client.GetAsync("http://localhost/Account/Login");
var resposneBody = await getResponse.Content.ReadAsStringAsync();
var formToken = AntiForgeryTestHelper.RetrieveAntiForgeryToken(resposneBody, "Account/Login");
var cookieTokenKey = AntiForgeryTestHelper.RetrieveAntiForgeryCookie(getResponse).Key;
var responseBody = await getResponse.Content.ReadAsStringAsync();
var formToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(responseBody, "Account/Login");
var cookieTokenKey = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse).Key;
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login");
var nameValueCollection = new List<KeyValuePair<string, string>>
@ -205,7 +205,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Assert
var exception = response.GetServerException();
Assert.Equal(
"The required anti-forgery cookie \"" + cookieTokenKey + "\" is not present.",
"The required antiforgery cookie \"" + cookieTokenKey + "\" is not present.",
exception.ExceptionMessage);
}
@ -216,8 +216,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
var client = server.CreateClient();
var getResponse = await client.GetAsync("http://localhost/Account/Login");
var resposneBody = await getResponse.Content.ReadAsStringAsync();
var cookieToken = AntiForgeryTestHelper.RetrieveAntiForgeryCookie(getResponse);
var responseBody = await getResponse.Content.ReadAsStringAsync();
var cookieToken = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse);
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/Login");
request.Headers.Add("Cookie", cookieToken.Key + "=" + cookieToken.Value);
@ -234,7 +234,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Assert
var exception = response.GetServerException();
Assert.Equal("The required anti-forgery form field \"__RequestVerificationToken\" is not present.",
Assert.Equal("The required antiforgery form field \"__RequestVerificationToken\" is not present.",
exception.ExceptionMessage);
}
@ -265,10 +265,10 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// do a get response.
var getResponse = await client.GetAsync("http://localhost/Account/FlushAsyncLogin");
var resposneBody = await getResponse.Content.ReadAsStringAsync();
var responseBody = await getResponse.Content.ReadAsStringAsync();
var formToken = AntiForgeryTestHelper.RetrieveAntiForgeryToken(resposneBody, "Account/FlushAsyncLogin");
var cookieToken = AntiForgeryTestHelper.RetrieveAntiForgeryCookie(getResponse);
var formToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(responseBody, "Account/FlushAsyncLogin");
var cookieToken = AntiforgeryTestHelper.RetrieveAntiforgeryCookie(getResponse);
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Account/FlushAsyncLogin");
request.Headers.Add("Cookie", cookieToken.Key + "=" + cookieToken.Value);

View File

@ -52,7 +52,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
[InlineData("Image", null)]
// Testing InputTagHelper with File
[InlineData("Input", null)]
public async Task HtmlGenerationWebSite_GeneratesExpectedResults(string action, string antiForgeryPath)
public async Task HtmlGenerationWebSite_GeneratesExpectedResults(string action, string antiforgeryPath)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, _configureServices);
@ -72,7 +72,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(expectedMediaType, response.Content.Headers.ContentType);
responseContent = responseContent.Trim();
if (antiForgeryPath == null)
if (antiforgeryPath == null)
{
#if GENERATE_BASELINES
ResourceFile.UpdateFile(_resourcesAssembly, outputFile, expectedContent, responseContent);
@ -82,7 +82,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
}
else
{
var forgeryToken = AntiForgeryTestHelper.RetrieveAntiForgeryToken(responseContent, antiForgeryPath);
var forgeryToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(responseContent, antiforgeryPath);
#if GENERATE_BASELINES
// Reverse usual substitution and insert a format item into the new file content.
responseContent = responseContent.Replace(forgeryToken, "{0}");
@ -102,7 +102,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
[InlineData("OrderUsingHtmlHelpers", "/HtmlGeneration_Order/Submit")]
[InlineData("Product", null)]
[InlineData("Script", null)]
public async Task HtmlGenerationWebSite_GenerateEncodedResults(string action, string antiForgeryPath)
public async Task HtmlGenerationWebSite_GenerateEncodedResults(string action, string antiforgeryPath)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName, services =>
@ -128,7 +128,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(expectedMediaType, response.Content.Headers.ContentType);
responseContent = responseContent.Trim();
if (antiForgeryPath == null)
if (antiforgeryPath == null)
{
#if GENERATE_BASELINES
ResourceFile.UpdateFile(_resourcesAssembly, outputFile, expectedContent, responseContent);
@ -138,7 +138,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
}
else
{
var forgeryToken = AntiForgeryTestHelper.RetrieveAntiForgeryToken(responseContent, antiForgeryPath);
var forgeryToken = AntiforgeryTestHelper.RetrieveAntiforgeryToken(responseContent, antiforgeryPath);
#if GENERATE_BASELINES
// Reverse usual substitution and insert a format item into the new file content.
responseContent = responseContent.Replace(forgeryToken, "{0}");
@ -180,7 +180,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
responseContent = responseContent.Trim();
var forgeryToken =
AntiForgeryTestHelper.RetrieveAntiForgeryToken(responseContent, "Customer/HtmlGeneration_Customer");
AntiforgeryTestHelper.RetrieveAntiforgeryToken(responseContent, "Customer/HtmlGeneration_Customer");
#if GENERATE_BASELINES
// Reverse usual substitution and insert a format item into the new file content.
@ -461,11 +461,11 @@ Products: Laptops (3)";
[InlineData(null)]
[InlineData(true)]
[InlineData(false)]
public async Task FormTagHelper_GeneratesExpectedContent(bool? optionsAntiForgery)
public async Task FormTagHelper_GeneratesExpectedContent(bool? optionsAntiforgery)
{
// Arrange
var newServices = new ServiceCollection();
newServices.InitializeTagHelper<FormTagHelper>((helper, _) => helper.AntiForgery = optionsAntiForgery);
newServices.InitializeTagHelper<FormTagHelper>((helper, _) => helper.Antiforgery = optionsAntiforgery);
var server = TestHelper.CreateServer(_app, SiteName,
services =>
{
@ -476,8 +476,8 @@ Products: Laptops (3)";
var expectedMediaType = MediaTypeHeaderValue.Parse("text/html; charset=utf-8");
var outputFile = string.Format(
"compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Form.Options.AntiForgery.{0}.html",
optionsAntiForgery?.ToString() ?? "null");
"compiler/resources/HtmlGenerationWebSite.HtmlGeneration_Home.Form.Options.Antiforgery.{0}.html",
optionsAntiforgery?.ToString() ?? "null");
var expectedContent =
await ResourceFile.ReadResourceAsync(_resourcesAssembly, outputFile, sourceFile: false);
@ -491,7 +491,7 @@ Products: Laptops (3)";
Assert.Equal(expectedMediaType, response.Content.Headers.ContentType);
responseContent = responseContent.Trim();
var forgeryTokens = AntiForgeryTestHelper.RetrieveAntiForgeryTokens(responseContent).ToArray();
var forgeryTokens = AntiforgeryTestHelper.RetrieveAntiforgeryTokens(responseContent).ToArray();
#if GENERATE_BASELINES
// Reverse usual substitutions and insert format items into the new file content.

View File

@ -11,7 +11,7 @@
"ActionResultsWebSite": "1.0.0",
"ActivatorWebSite": "1.0.0",
"AddServicesWebSite": "1.0.0",
"AntiForgeryWebSite": "1.0.0",
"AntiforgeryTokenWebSite": "1.0.0",
"ApiExplorerWebSite": "1.0.0",
"ApplicationModelWebSite": "1.0.0",
"BasicWebSite": "1.0.0",

View File

@ -33,7 +33,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{ "asp-action", "index" },
{ "asp-controller", "home" },
{ "method", "post" },
{ "asp-anti-forgery", true }
{ "asp-antiforgery", true }
},
items: new Dictionary<object, object>(),
uniqueId: "test",
@ -58,12 +58,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var viewContext = TestableHtmlGenerator.GetViewContext(model: null,
htmlGenerator: htmlGenerator,
metadataProvider: metadataProvider);
var expectedPostContent = "Something" + htmlGenerator.GenerateAntiForgery(viewContext)
.ToString(TagRenderMode.SelfClosing);
var expectedPostContent = "Something" + htmlGenerator.GenerateAntiforgery(viewContext);
var formTagHelper = new FormTagHelper(htmlGenerator)
{
Action = "index",
AntiForgery = true,
Antiforgery = true,
Controller = "home",
ViewContext = viewContext,
RouteValues =
@ -93,8 +92,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[InlineData(null, "<input />")]
[InlineData(true, "<input />")]
[InlineData(false, "")]
public async Task ProcessAsync_GeneratesAntiForgeryCorrectly(
bool? antiForgery,
public async Task ProcessAsync_GeneratesAntiforgeryCorrectly(
bool? antiforgery,
string expectedPostContent)
{
// Arrange
@ -124,12 +123,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
It.IsAny<object>()))
.Returns(new TagBuilder("form", new CommonTestEncoder()));
generator.Setup(mock => mock.GenerateAntiForgery(viewContext))
.Returns(new TagBuilder("input", new CommonTestEncoder()));
generator.Setup(mock => mock.GenerateAntiforgery(viewContext))
.Returns(new HtmlString("<input />"));
var formTagHelper = new FormTagHelper(generator.Object)
{
Action = "Index",
AntiForgery = antiForgery,
Antiforgery = antiforgery,
ViewContext = viewContext,
};
@ -195,7 +194,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var formTagHelper = new FormTagHelper(generator.Object)
{
Action = "Index",
AntiForgery = false,
Antiforgery = false,
ViewContext = testViewContext,
RouteValues =
{
@ -250,7 +249,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
var formTagHelper = new FormTagHelper(generator.Object)
{
Action = "Index",
AntiForgery = false,
Antiforgery = false,
Controller = "Home",
ViewContext = viewContext,
};
@ -299,7 +298,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
.Verifiable();
var formTagHelper = new FormTagHelper(generator.Object)
{
AntiForgery = false,
Antiforgery = false,
Route = "Default",
ViewContext = viewContext,
RouteValues =
@ -326,19 +325,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
[InlineData(true, "<input />")]
[InlineData(false, "")]
[InlineData(null, "")]
public async Task ProcessAsync_SupportsAntiForgeryIfActionIsSpecified(
bool? antiForgery,
public async Task ProcessAsync_SupportsAntiforgeryIfActionIsSpecified(
bool? antiforgery,
string expectedPostContent)
{
// Arrange
var viewContext = CreateViewContext();
var generator = new Mock<IHtmlGenerator>();
generator.Setup(mock => mock.GenerateAntiForgery(It.IsAny<ViewContext>()))
.Returns(new TagBuilder("input", new CommonTestEncoder()));
generator.Setup(mock => mock.GenerateAntiforgery(It.IsAny<ViewContext>()))
.Returns(new HtmlString("<input />"));
var formTagHelper = new FormTagHelper(generator.Object)
{
AntiForgery = antiForgery,
Antiforgery = antiforgery,
ViewContext = viewContext,
};

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNet.Antiforgery;
using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc.ModelBinding;
@ -38,7 +39,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
IOptions<MvcViewOptions> options,
IUrlHelper urlHelper,
IDictionary<string, object> validationAttributes)
: base(GetAntiForgery(), options, metadataProvider, urlHelper, new CommonTestEncoder())
: base(Mock.Of<IAntiforgery>(), options, metadataProvider, urlHelper, new CommonTestEncoder())
{
_validationAttributes = validationAttributes;
}
@ -69,9 +70,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return viewContext;
}
public override TagBuilder GenerateAntiForgery(ViewContext viewContext)
public override HtmlString GenerateAntiforgery(ViewContext viewContext)
{
return new TagBuilder("input", new CommonTestEncoder())
var tagBuilder = new TagBuilder("input", new CommonTestEncoder())
{
Attributes =
{
@ -80,6 +81,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{ "value", "olJlUDjrouRNWLen4tQJhauj1Z1rrvnb3QD65cmQU1Ykqi6S4" }, // 50 chars of a token.
},
};
return tagBuilder.ToHtmlString(TagRenderMode.SelfClosing);
}
protected override IDictionary<string, object> GetValidationAttributes(
@ -99,26 +102,5 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
return mockOptions.Object;
}
private static AntiForgery GetAntiForgery()
{
// AntiForgery must be passed to TestableHtmlGenerator constructor but will never be called.
var optionsAccessor = new Mock<IOptions<AntiForgeryOptions>>();
var mockDataProtectionOptions = new Mock<IOptions<DataProtectionOptions>>();
mockDataProtectionOptions
.SetupGet(options => options.Options)
.Returns(Mock.Of<DataProtectionOptions>());
optionsAccessor
.SetupGet(o => o.Options)
.Returns(new AntiForgeryOptions());
var antiForgery = new AntiForgery(
Mock.Of<IClaimUidExtractor>(),
Mock.Of<IDataProtectionProvider>(),
Mock.Of<IAntiForgeryAdditionalDataProvider>(),
optionsAccessor.Object,
new CommonTestEncoder(),
mockDataProtectionOptions.Object);
return antiForgery;
}
}
}

View File

@ -1,5 +0,0 @@
AntiForgeryWebSite
===
This web site illustrates how to use anti forgery system to prevent CSRF attacks. The web site has an
`AccountsController` which uses an anti forgery token to validate incoming form posts.

View File

@ -10,6 +10,9 @@
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<RootNamespace>AntiforgeryTokenWebSite</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>49637</DevelopmentServerPort>

View File

@ -3,7 +3,7 @@
using Microsoft.AspNet.Mvc;
namespace AntiForgeryWebSite
namespace AntiforgeryTokenWebSite
{
// This controller is reachable via traditional routing.
public class AccountController : Controller

View File

@ -3,7 +3,7 @@
using Microsoft.AspNet.Mvc;
namespace AntiForgeryWebSite
namespace AntiforgeryTokenWebSite
{
// This controller is reachable via traditional routing.
public class HomeController : Controller

View File

@ -3,7 +3,7 @@
using System.ComponentModel.DataAnnotations;
namespace AntiForgeryWebSite
namespace AntiforgeryTokenWebSite
{
public class LoginViewModel
{

View File

@ -4,7 +4,7 @@
using Microsoft.AspNet.Builder;
using Microsoft.Framework.DependencyInjection;
namespace AntiForgeryWebSite
namespace AntiforgeryTokenWebSite
{
public class Startup
{

View File

@ -1,4 +1,4 @@
@model AntiForgeryWebSite.LoginViewModel
@model AntiforgeryTokenWebSite.LoginViewModel
@{
ViewBag.Title = "Log in";

View File

@ -1,4 +1,4 @@
@model AntiForgeryWebSite.LoginViewModel
@model AntiforgeryTokenWebSite.LoginViewModel
@{
ViewBag.Title = "Log in";

View File

@ -1,8 +1,8 @@
<html>
<head>
<title>@ViewBag.Title AntiForgery Functional Tests</title>
<title>@ViewBag.Title Antiforgery Functional Tests</title>
</head>
@SetAntiForgeryCookieAndHeader()
@SetAntiforgeryCookieAndHeader()
@await FlushAsync()
<body>

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title AntiForgery Functional Tests</title>
<title>@ViewBag.Title Antiforgery Functional Tests</title>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
@ -11,7 +11,7 @@
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
</button>
@Html.ActionLink("ASP.NET Anti Forgery tests", "Index", "Home", null, new { @class = "navbar-brand" })
@Html.ActionLink("ASP.NET Antiforgery tests", "Index", "Home", null, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
@await Html.PartialAsync("_LoginPartial")

View File

@ -0,0 +1,5 @@
AntiforgeryTokenWebSite
===
This web site illustrates how to use the antiforgery system to prevent CSRF attacks. The web site has an
`AccountsController` which uses an antiforgery token to validate incoming form posts.

View File

@ -8,7 +8,7 @@
<html>
<body>
<form asp-controller="HtmlGeneration_Home" asp-action="EditWarehouse" asp-anti-forgery="false">
<form asp-controller="HtmlGeneration_Home" asp-action="EditWarehouse" asp-antiforgery="false">
<div>
@Html.DisplayFor(m => m.City)
<input asp-for="City" type="text" />

View File

@ -12,7 +12,7 @@
<form></form>
<form asp-controller="HtmlGeneration_Home" asp-action="Form"></form>
<form asp-controller="HtmlGeneration_Home" asp-action="Form" asp-anti-forgery="true"></form>
<form asp-controller="HtmlGeneration_Home" asp-action="Form" asp-anti-forgery="false"></form>
<form asp-controller="HtmlGeneration_Home" asp-action="Form" asp-antiforgery="true"></form>
<form asp-controller="HtmlGeneration_Home" asp-action="Form" asp-antiforgery="false"></form>
</body>
</html>

View File

@ -18,7 +18,7 @@
<title></title>
</head>
<body>
<form asp-controller="HtmlGeneration_Order" asp-action="Submit" asp-anti-forgery=" true">
<form asp-controller="HtmlGeneration_Order" asp-action="Submit" asp-antiforgery=" true">
<div>
<label asp-for="Shipping" class="order"></label>
<input asp-for="Shipping" type="text" asp-format="Your shipping method is {0}" size="50" />

View File

@ -12,7 +12,7 @@
<title></title>
</head>
<body>
<form asp-controller="HtmlGeneration_Home" asp-route-action="ProductSubmit" asp-anti-forgery="false" method="get">
<form asp-controller="HtmlGeneration_Home" asp-route-action="ProductSubmit" asp-antiforgery="false" method="get">
<div>
<label asp-for="HomePage" class="product"></label>
<input asp-for="HomePage" type="url" size="50" />

View File

@ -17,7 +17,7 @@
</head>
<body>
<form asp-anti-forgery="false" asp-action="Create">
<form asp-antiforgery="false" asp-action="Create">
<div class="form-horizontal">
<h4>Employee</h4>