@Html.AntiForgeryToken() 源碼分析,表單防偽碼的生成


源碼來自MVC4
@Html.AntiForgeryToken() 源碼分析

public MvcHtmlString AntiForgeryToken()
{
    return new MvcHtmlString(AntiForgery.GetHtml().ToString());
}

AntiForgery源自System.Web.Helpers.AntiForgery

public static HtmlString GetHtml()
{
    if (HttpContext.Current == null)
    {
        throw new ArgumentException(WebPageResources.HttpContextUnavailable);
    }
    TagBuilder formInputElement = AntiForgery._worker.GetFormInputElement(new HttpContextWrapper(HttpContext.Current));
    return formInputElement.ToHtmlString(TagRenderMode.SelfClosing);
}

//查到_worker的創建
private static readonly AntiForgeryWorker _worker = AntiForgery.CreateSingletonAntiForgeryWorker();

//發現IAntiForgeryTokenSerializer來自AntiForgeryTokenSerializer
//而且發現用所以接口的實例對象,開始查看具體代碼實現

private static AntiForgeryWorker CreateSingletonAntiForgeryWorker()
{
    ICryptoSystem cryptoSystem = MachineKey45CryptoSystem.Instance;
    if (cryptoSystem == null)
    {
        cryptoSystem = new MachineKey40CryptoSystem();
    }
    IAntiForgeryConfig config = new AntiForgeryConfigWrapper();
    IAntiForgeryTokenSerializer serializer = new AntiForgeryTokenSerializer(cryptoSystem);
    ITokenStore tokenStore = new AntiForgeryTokenStore(config, serializer);
    IClaimUidExtractor claimUidExtractor = new ClaimUidExtractor(config, ClaimsIdentityConverter.Default);
    ITokenValidator validator = new TokenValidator(config, claimUidExtractor);
    return new AntiForgeryWorker(serializer, config, tokenStore, validator);
}

//_worker的GetFormInputElement,發現value是_serializer.Serialize序列出來的,於是查看AntiForgeryTokenSerializer對象

public TagBuilder GetFormInputElement(HttpContextBase httpContext)
{
    this.CheckSSLConfig(httpContext);
    AntiForgeryToken cookieTokenNoThrow = this.GetCookieTokenNoThrow(httpContext);
    AntiForgeryToken antiForgeryToken;
    AntiForgeryToken token;
    this.GetTokens(httpContext, cookieTokenNoThrow, out antiForgeryToken, out token);
    if (antiForgeryToken != null)
    {
        this._tokenStore.SaveCookieToken(httpContext, antiForgeryToken);
    }
    TagBuilder tagBuilder = new TagBuilder("input");
    tagBuilder.Attributes["type"] = "hidden";
    tagBuilder.Attributes["name"] = this._config.FormFieldName;
    tagBuilder.Attributes["value"] = this._serializer.Serialize(token);
    return tagBuilder;
}

//查到AntiForgeryTokenSerializer對象的Serialize函數

public string Serialize(AntiForgeryToken token)
{
    string result;
    using (MemoryStream memoryStream = new MemoryStream())
    {
        using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream))
        {
            binaryWriter.Write(1);
            binaryWriter.Write(token.SecurityToken.GetData());
            binaryWriter.Write(token.IsSessionToken);
            if (!token.IsSessionToken)
            {
                if (token.ClaimUid != null)
                {
                    binaryWriter.Write(true);
                    binaryWriter.Write(token.ClaimUid.GetData());
                }
                else
                {
                    binaryWriter.Write(false);
                    binaryWriter.Write(token.Username);
                }
                binaryWriter.Write(token.AdditionalData);
            }
            binaryWriter.Flush();
            result = this._cryptoSystem.Protect(memoryStream.ToArray());
        }
    }
    return result;
}

/關鍵的序列化函數
//產生疑惑token.SecurityToken.GetData(),這個數據哪里來的
//token.IsSessionToken這個是bool,看命名就知道是用來判斷是不是生成關聯session的token


//退回后查看AntiForgeryToken這個class
//
//發現這段 GetFormInputElement函數里面如此創建

AntiForgeryToken token;
this.GetTokens(httpContext, cookieTokenNoThrow, out antiForgeryToken, out token);


//通過上方查看_worker知道的實例
// ITokenStore tokenStore = new AntiForgeryTokenStore(config, serializer);
//查到

private void GetTokens(HttpContextBase httpContext, AntiForgeryToken oldCookieToken, out AntiForgeryToken newCookieToken, out AntiForgeryToken formToken)
{
    newCookieToken = null;
    if (!this._validator.IsCookieTokenValid(oldCookieToken))
    {
        AntiForgeryToken antiForgeryToken;
        newCookieToken = (antiForgeryToken = this._validator.GenerateCookieToken());
        oldCookieToken = antiForgeryToken;
    }
    formToken = this._validator.GenerateFormToken(httpContext, AntiForgeryWorker.ExtractIdentity(httpContext), oldCookieToken);
}

//繼續追看

public AntiForgeryToken GenerateFormToken(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken cookieToken)
{
    AntiForgeryToken antiForgeryToken = new AntiForgeryToken
    {
        SecurityToken = cookieToken.SecurityToken,
        IsSessionToken = false//原來默認是false,暫時認為默認是不使用session的
    };
    bool flag = false;
    if (identity != null && identity.IsAuthenticated)
    {
        if (!this._config.SuppressIdentityHeuristicChecks)
        {
            flag = true;
        }
        antiForgeryToken.ClaimUid = this._claimUidExtractor.ExtractClaimUid(identity);
        if (antiForgeryToken.ClaimUid == null)
        {
            antiForgeryToken.Username = identity.Name;
        }
    }
    if (this._config.AdditionalDataProvider != null)
    {
        antiForgeryToken.AdditionalData = this._config.AdditionalDataProvider.GetAdditionalData(httpContext);
    }
    if (flag && string.IsNullOrEmpty(antiForgeryToken.Username) && antiForgeryToken.ClaimUid == null && string.IsNullOrEmpty(antiForgeryToken.AdditionalData))
    {
        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, WebPageResources.TokenValidator_AuthenticatedUserWithoutUsername, new object[]
        {
            identity.GetType()
        }));
    }
    return antiForgeryToken;
}

//經過這樣一看token的創建就清晰了


//接着查看token的token.SecurityToken這個屬性

public BinaryBlob SecurityToken
{
    get
    {
        if (this._securityToken == null)
        {
            this._securityToken = new BinaryBlob(128);//發現默認是128,對象是BinaryBlob
        }
        return this._securityToken;
    }
    set
    {
        this._securityToken = value;
    }
}

//查看BinaryBlob的構造函數,出現一個GenerateNewToken函數

public BinaryBlob(int bitLength) : this(bitLength, BinaryBlob.GenerateNewToken(bitLength))
{
}

//GenerateNewToken源碼

private static byte[] GenerateNewToken(int bitLength)
{
    byte[] array = new byte[bitLength / 8];
    BinaryBlob._prng.GetBytes(array);
    return array;
}

//該死,有出現一個未知的東西,_prng
private static readonly RNGCryptoServiceProvider _prng = new RNGCryptoServiceProvider();

[SecuritySafeCritical]
public override void GetBytes(byte[] data)
{
    if (data == null)
    {
        throw new ArgumentNullException("data");
    }
    RNGCryptoServiceProvider.GetBytes(this.m_safeProvHandle, data, data.Length);
}

[SecurityCritical, SuppressUnmanagedCodeSecurity]
[DllImport("QCall", CharSet = CharSet.Unicode)]
private static extern void GetBytes(SafeProvHandle hProv, byte[] randomBytes, int count);

//至此必應了一下RNGCryptoServiceProvider類(bing查msdn特別方便)
//https://msdn.microsoft.com/zh-cn/library/system.security.cryptography.rngcryptoserviceprovider(v=vs.110).aspx
//http://www.cnblogs.com/izanami/archive/2011/04/20/2022173.html
原來RNGCryptoServiceProvider的GetBytes用經過加密的強隨機值序列填充字節數組,最終的隨機數據生成!

//現在這段序列化的部分已經解開了一些了
binaryWriter.Write(1);
binaryWriter.Write(token.SecurityToken.GetData());//數據明了,一段用經過加密的強隨機值數組
binaryWriter.Write(token.IsSessionToken);//Bool判斷是否使用session
if (!token.IsSessionToken)
{
    if (token.ClaimUid != null)
    {
        binaryWriter.Write(true);
        binaryWriter.Write(token.ClaimUid.GetData());//也是一個BinaryBlob
    }
    else
    {
        binaryWriter.Write(false);
        binaryWriter.Write(token.Username);
    }
    binaryWriter.Write(token.AdditionalData);
}
binaryWriter.Flush();
result = this._cryptoSystem.Protect(memoryStream.ToArray());


//繼續解開ClaimUid
//在上文的 GenerateFormToken發現這樣的一段
antiForgeryToken.ClaimUid = this._claimUidExtractor.ExtractClaimUid(identity);
//_claimUidExtractor在創建的地方是ClaimUidExtractor
//發現源碼

public BinaryBlob ExtractClaimUid(IIdentity identity)
{
    if (identity == null || !identity.IsAuthenticated || this._config.SuppressIdentityHeuristicChecks)
    {
        return null;
    }
    ClaimsIdentity claimsIdentity = this._claimsIdentityConverter.TryConvert(identity);
    if (claimsIdentity == null)
    {
        return null;
    }
    string[] uniqueIdentifierParameters = ClaimUidExtractor.GetUniqueIdentifierParameters(claimsIdentity, this._config.UniqueClaimTypeIdentifier);
    byte[] data = CryptoUtil.ComputeSHA256(uniqueIdentifierParameters);
    return new BinaryBlob(256, data);
}
public static byte[] ComputeSHA256(IList<string> parameters)
{
    byte[] result;
    using (MemoryStream memoryStream = new MemoryStream())
    {
        using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream))
        {
            foreach (string current in parameters)
            {
                binaryWriter.Write(current);
            }
            binaryWriter.Flush();
            using (SHA256 sHA = CryptoUtil._sha256Factory())
            {
                byte[] array = sHA.ComputeHash(memoryStream.GetBuffer(), 0, checked((int)memoryStream.Length));
                result = array;
            }
        }
    }
    return result;
}

//發現是SHA256的哈希計算值
//然后是AdditionalData

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

//最后是這句result = this._cryptoSystem.Protect(memoryStream.ToArray());
//MachineKey40CryptoSystem : ICryptoSystem

public string Protect(byte[] data)
{
    byte[] array = new byte[data.Length + 4];
    Buffer.BlockCopy(data, 0, array, 4, data.Length);
    array[0] = 133;
    array[1] = 135;
    array[2] = 242;
    array[3] = 102;
    string hex = this._encoder(array, MachineKeyProtection.All);
    return MachineKey40CryptoSystem.HexToBase64(hex);
}

internal static string HexToBase64(string hex)
{
    int num = hex.Length / 2;
    byte[] array = new byte[num];
    for (int i = 0; i < num; i++)
    {
        array[i] = (byte)((MachineKey40CryptoSystem.HexValue(hex[i * 2]) << 4) + MachineKey40CryptoSystem.HexValue(hex[i * 2 + 1]));
    }
    return HttpServerUtility.UrlTokenEncode(array);
}

//將那些隨機生成的數據變成了16進制字符串
//加密到此結束了

 

//最后就是對應的解密了

public AntiForgeryToken Deserialize(string serializedToken)
{
    try
    {
        using (MemoryStream memoryStream = new MemoryStream(this._cryptoSystem.Unprotect(serializedToken)))
        {
            using (BinaryReader binaryReader = new BinaryReader(memoryStream))
            {
                AntiForgeryToken antiForgeryToken = AntiForgeryTokenSerializer.DeserializeImpl(binaryReader);
                if (antiForgeryToken != null)
                {
                    return antiForgeryToken;
                }
            }
        }
    }
    catch
    {
    }
    throw HttpAntiForgeryException.CreateDeserializationFailedException();
}


private static AntiForgeryToken DeserializeImpl(BinaryReader reader)
{
    byte b = reader.ReadByte();//從當前流中讀取下一個字節,並使流的當前位置提升 1 個字節。
    if (b != 1)//對應加密的binaryWriter.Write(1);
    {
        return null;
    }
    //依照加密時候的分段大小對應解密
    AntiForgeryToken antiForgeryToken = new AntiForgeryToken();
    byte[] data = reader.ReadBytes(16);
    antiForgeryToken.SecurityToken = new BinaryBlob(128, data);
    antiForgeryToken.IsSessionToken = reader.ReadBoolean();
    if (!antiForgeryToken.IsSessionToken)
    {
        bool flag = reader.ReadBoolean();
        if (flag)
        {
            byte[] data2 = reader.ReadBytes(32);
            antiForgeryToken.ClaimUid = new BinaryBlob(256, data2);
        }
        else
        {
            antiForgeryToken.Username = reader.ReadString();
        }
        antiForgeryToken.AdditionalData = reader.ReadString();
    }
    if (reader.BaseStream.ReadByte() != -1)
    {
        return null;
    }
    return antiForgeryToken;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM