在 ASP.NET Core 2.0 或更高版本, FormTagHelper antiforgery 令牌注入 HTML 窗体元素。 Razor 文件中的以下标记自动生成防伪令牌:
<form method="post"> ... </form>在每个前面的情况下,ASP.NET Core 添加类似于以下一个隐藏的表单字段:
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">ASP.NET Core 包括三个筛选器来处理 antiforgery 令牌:
ValidateAntiForgeryTokenAutoValidateAntiforgeryTokenIgnoreAntiforgeryToken使用校验标识:
[ValidateAntiForgeryToken] public IActionResult Save() { return RedirectToAction("Index"); }分析源码校验规则:
public bool TryValidateTokenSet( HttpContext httpContext, AntiforgeryToken cookieToken, AntiforgeryToken requestToken, out string message) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } if (cookieToken == null) { throw new ArgumentNullException( nameof(cookieToken), Resources.Antiforgery_CookieToken_MustBeProvided_Generic); } if (requestToken == null) { throw new ArgumentNullException( nameof(requestToken), Resources.Antiforgery_RequestToken_MustBeProvided_Generic); } // Do the tokens have the correct format? if (!cookieToken.IsCookieToken || requestToken.IsCookieToken) { message = Resources.AntiforgeryToken_TokensSwapped; return false; } // Are the security tokens embedded in each incoming token identical? if (!object.Equals(cookieToken.SecurityToken, requestToken.SecurityToken)) { message = Resources.AntiforgeryToken_SecurityTokenMismatch; return false; } // Is the incoming token meant for the current user? var currentUsername = string.Empty; BinaryBlob currentClaimUid = null; var authenticatedIdentity = GetAuthenticatedIdentity(httpContext.User); if (authenticatedIdentity != null) { currentClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(httpContext.User)); if (currentClaimUid == null) { currentUsername = authenticatedIdentity.Name ?? string.Empty; } } // OpenID and other similar authentication schemes use URIs for the username. // These should be treated as case-sensitive. var comparer = StringComparer.OrdinalIgnoreCase; if (currentUsername.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || currentUsername.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { comparer = StringComparer.Ordinal; } if (!comparer.Equals(requestToken.Username, currentUsername)) { message = Resources.FormatAntiforgeryToken_UsernameMismatch(requestToken.Username, currentUsername); return false; } if (!object.Equals(requestToken.ClaimUid, currentClaimUid)) { message = Resources.AntiforgeryToken_ClaimUidMismatch; return false; } // Is the AdditionalData valid? if (_additionalDataProvider != null && !_additionalDataProvider.ValidateAdditionalData(httpContext, requestToken.AdditionalData)) { message = Resources.AntiforgeryToken_AdditionalDataCheckFailed; return false; } message = null; return true; }现在我们使用Wireshark观察:
使用Badboy录制,并重发,发现是可以重复发送并成功
当时我猜想,是不是这个Token只能使用一次应该就无效了,答案是否定的。
AntiForgeryToken主要是用来解决:跨站点请求伪造 (也称为 XSRF 或 CSRF),其它站点无法读取到当前域名的Cookie, 就算伪造一个__RequestVerificationToken,最终也会在校验防伪标识被识别出来:
