aspnetcore/src/Microsoft.AspNet.Mvc.Extens.../AntiForgery/AntiForgery.cs

138 lines
6.8 KiB
C#

// 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);
}
}
}
}