• 源码看微软验证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;
        }
      }
    }
  • 相关阅读:
    盘点黑客攻击途径:最常用的7个策略及简单的防护方法
    python 小技巧
    vi 使用方法
    Mac下添加环境变量(一劳永逸)
    增强for循环
    十大排序算法
    java中break、continue、return作用
    Mac zsh中所有命令失效
    Mac 每次都要执行source ~/.bash_profile 后,配置的环境变量才生效
    Mac下添加环境变量
  • 原文地址:https://www.cnblogs.com/Qintai/p/11828086.html
Copyright © 2020-2023  润新知