@Html.AntiForgeryToken() 源码分析,表单防伪码的生成

时间:2023-03-09 01:54:13
@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();
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,对象是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 / ];
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();
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(, 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(), , 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 + ];
Buffer.BlockCopy(data, , array, , data.Length);
array[] = ;
array[] = ;
array[] = ;
array[] = ;
string hex = this._encoder(array, MachineKeyProtection.All);
return MachineKey40CryptoSystem.HexToBase64(hex);
} internal static string HexToBase64(string hex)
{
int num = hex.Length / ;
byte[] array = new byte[num];
for (int i = ; i < num; i++)
{
array[i] = (byte)((MachineKey40CryptoSystem.HexValue(hex[i * ]) << ) + MachineKey40CryptoSystem.HexValue(hex[i * + ]));
}
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 != )//对应加密的binaryWriter.Write(1);
{
return null;
}
//依照加密时候的分段大小对应解密
AntiForgeryToken antiForgeryToken = new AntiForgeryToken();
byte[] data = reader.ReadBytes();
antiForgeryToken.SecurityToken = new BinaryBlob(, data);
antiForgeryToken.IsSessionToken = reader.ReadBoolean();
if (!antiForgeryToken.IsSessionToken)
{
bool flag = reader.ReadBoolean();
if (flag)
{
byte[] data2 = reader.ReadBytes();
antiForgeryToken.ClaimUid = new BinaryBlob(, data2);
}
else
{
antiForgeryToken.Username = reader.ReadString();
}
antiForgeryToken.AdditionalData = reader.ReadString();
}
if (reader.BaseStream.ReadByte() != -)
{
return null;
}
return antiForgeryToken;
}