源码看微软验证Token【CSRF】MVC5

都知道在MVC5中,在action方法前加入 [ValidateAntiForgeryToken],会验证是否来自于自己表单的用户,验证其cooktoken和来自表单中的token,是否一致
为方便更好的调试,直接调用其验证方法 AntiForgery.Validate();

 // [AcceptVerbs(HttpVerbs.Post)] netCore没有这个
 [HttpPost]
// [ValidateAntiForgeryToken]
 public JsonResult Index(int a=1/*IFormCollection collection*/)
 {
     HttpCookie antiForgeryCookie = Request.Cookies[AntiForgeryConfig.CookieName];
     string cookieValue = antiForgeryCookie != null ? antiForgeryCookie.Value : null;
     
     AntiForgery.Validate(cookieValue, Request["__RequestVerificationToken"]);//Validate,就是框架源码验证的核心
     ModelState.AddModelError("", "1111111111111");
     return Json("验证成功!");
 }

打开Validate方法

/// <summary>验证 HTML 表单字段中的输入数据是否来自已提交数据的用户。</summary>
/// <param name="cookieToken">Cookie 令牌值。</param>
/// <param name="formToken">令牌格式。</param>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static void Validate(string cookieToken, string formToken)
{
  if (HttpContext.Current == null)
    throw new ArgumentException(WebPageResources.HttpContextUnavailable);
  AntiForgery._worker.Validate((HttpContextBase) new HttpContextWrapper(HttpContext.Current), cookieToken, formToken);
}

打开 AntiForgery._worker.Validate方法

public void Validate(HttpContextBase httpContext, string cookieToken, string formToken)
{
  this.CheckSSLConfig(httpContext);
  AntiForgeryToken cookieToken1 = this.DeserializeToken(cookieToken);//来自Cookie的token
  AntiForgeryToken formToken1 = this.DeserializeToken(formToken);//来自form表单的token
  this._validator.ValidateTokens(httpContext, AntiForgeryWorker.ExtractIdentity(httpContext), cookieToken1, formToken1);
}

继续打开  this._validator.ValidateTokens(),方法

internal interface ITokenValidator
{
  AntiForgeryToken GenerateCookieToken();

  AntiForgeryToken GenerateFormToken(HttpContextBase httpContext,IIdentity identity,AntiForgeryToken cookieToken);

  bool IsCookieTokenValid(AntiForgeryToken cookieToken);

  void ValidateTokens(HttpContextBase httpContext,IIdentity identity,AntiForgeryToken cookieToken,AntiForgeryToken formToken);
}

由于 ValidateTokens 是需要实现的,找到实现方法,TokenValidator类的ValidateTokens,就是具体实现

public void ValidateTokens( HttpContextBase httpContext,  IIdentity identity,  AntiForgeryToken sessionToken,  AntiForgeryToken fieldToken)
{
  if (sessionToken == null)//1.来自cookies的token=null,直接验证失败
    throw HttpAntiForgeryException.CreateCookieMissingException(this._config.CookieName);
  if (fieldToken == null)  //2.来自表单的token=null,直接验证失败
    throw HttpAntiForgeryException.CreateFormFieldMissingException(this._config.FormFieldName);
  if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken) // 3.验证cookietoken的IsSessionToken 是否
    throw HttpAntiForgeryException.CreateTokensSwappedException(this._config.CookieName, this._config.FormFieldName);
  if (!object.Equals((object) sessionToken.SecurityToken, (object) fieldToken.SecurityToken))
    throw HttpAntiForgeryException.CreateSecurityTokenMismatchException();
  string str = string.Empty;
  BinaryBlob binaryBlob = (BinaryBlob) null;
  if (identity != null && identity.IsAuthenticated)
  {
    binaryBlob = this._claimUidExtractor.ExtractClaimUid(identity);
    if (binaryBlob == null)
      str = identity.Name ?? string.Empty;
  }
  bool flag = str.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || str.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
  if (!string.Equals(fieldToken.Username, str, flag ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase))
    throw HttpAntiForgeryException.CreateUsernameMismatchException(fieldToken.Username, str);
  if (!object.Equals((object) fieldToken.ClaimUid, (object) binaryBlob))
    throw HttpAntiForgeryException.CreateClaimUidMismatchException();
  if (this._config.AdditionalDataProvider != null && !this._config.AdditionalDataProvider.ValidateAdditionalData(httpContext, fieldToken.AdditionalData))
    throw HttpAntiForgeryException.CreateAdditionalDataCheckFailedException();
}

透过这个方法,可以看出 这是个完全验证式的方法,此方法就是令牌验证最为关键的逻辑代码
验证的核心,在于两个token(两个AntiForgeryToken的对象),一个是cooktoken,一个formtoken,验证各自的属性是否一致,IsSessionToken为true表示Cookie令牌,否则为表单令牌
1.在开启了防伪标记功能后,FormToken或CookieToken其中一个值为空白!也就是说只要缺少表单令牌或者Cookie令牌,验证一定是失败的。
2.防伪令牌中安全令牌的值不相等!
3.防伪令牌中的相关授权信息不一致!例如出现认证授权的用户名不一致!
4.防伪令牌自身的标记错误,IsSessionToken为true表示Cookie令牌,否则为表单令牌,如果这个属性设置错误,验证失败!
5.其他的还会判断AdditionalDataProvider这个属性值是否

友情链接:https://shiyousan.com/post/636402934261643641
可以发现,验证来验证去,核心还是在于 AntiForgeryToken 这个密封类,

internal sealed class AntiForgeryToken
{
  internal const int SecurityTokenBitLength = 128;
  internal const int ClaimUidBitLength = 256;
  private string _additionalData;
  private BinaryBlob _securityToken;
  private string _username;

  public string AdditionalData
  {
    get
    {
      return this._additionalData ?? string.Empty;
    }
    set
    {
      this._additionalData = value;
    }
  }

  public BinaryBlob ClaimUid { get; set; }

  //true表示Cookie令牌,否则为表单令牌
  public bool IsSessionToken { get; set; }

  // 安全令牌
  public BinaryBlob SecurityToken
  {
    get
    {
      if (this._securityToken == null)
        this._securityToken = new BinaryBlob(128);
      return this._securityToken;
    }
    set
    {
      this._securityToken = value;
    }
  }

  public string Username
  {
    get
    {
      return this._username ?? string.Empty;
    }
    set
    {
      this._username = value;
    }
  }
}

猜你喜欢

转载自www.cnblogs.com/Qintai/p/11828086.html
今日推荐