我在ASP.Net MVC中遇到AntiForgeryToken问题。如果我在我的网络服务器上执行iisreset并且用户继续他们的会话,他们会被退回到登录页面。并不可怕,但AntiForgery令牌爆炸,再次开始的唯一方法是在浏览器上吹掉cookie。
对于版本1的测试版,在我为我回读cookie时出错了所以我曾经在请求验证令牌之前擦除它,但是在发布时它已经修复了。
现在我想我会回到修复beta问题的代码,但我不禁想到我错过了什么。有没有一个更简单的解决方案,我应该放弃他们的助手并从头开始创建一个新的?我感觉很多问题是它与旧的ASP.Net管道如此紧密地联系在一起,并试图将其用于做一些它不是真正想要做的事情。
我查看了ASP.Net MVC 2 RC的源代码,看起来代码没有太大变化,所以我没有尝试过,我认为那里没有任何答案
以下是异常的堆栈跟踪的相关部分。
编辑:我刚才意识到我没有提到这只是试图在GET请求中插入令牌。这不是您开始执行POST时发生的验证。
System.Web.Mvc.HttpAntiForgeryException: A required anti-forgery token was not
supplied or was invalid.
---> System.Web.HttpException: Validation of viewstate MAC failed. If this
application is hosted by a Web Farm or cluster, ensure that <machineKey>
configuration specifies the same validationKey and validation algorithm.
AutoGenerate cannot be used in a cluster.
---> System.Web.UI.ViewStateException: Invalid viewstate.
Client IP: 127.0.0.1
Port: 4991
User-Agent: scrubbed
ViewState: scrubbed
Referer: blah
Path: /oursite/Account/Login
---> System.Security.Cryptography.CryptographicException: Padding is invalid and
cannot be removed.
at System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast)
at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo)
at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
--- End of inner exception stack trace ---
--- End of inner exception stack trace ---
at System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError)
at System.Web.UI.ViewStateException.ThrowMacValidationError(Exception inner, String persistedState)
at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
at System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter.Deserialize(String serializedState)
at System.Web.Mvc.AntiForgeryDataSerializer.Deserialize(String serializedToken)
--- End of inner exception stack trace ---
at System.Web.Mvc.AntiForgeryDataSerializer.Deserialize(String serializedToken)
at System.Web.Mvc.HtmlHelper.GetAntiForgeryTokenAndSetCookie(String salt, String domain, String path)
at System.Web.Mvc.HtmlHelper.AntiForgeryToken(String salt, String domain, String path)
答案 0 :(得分:41)
如果您的MachineKey设置为AutoGenerate,那么您的验证令牌等将无法在应用程序重新启动后继续存在 - ASP.NET将在启动时生成新密钥,然后将无法正确解密令牌。
如果你看到这么多,我建议:
1 执行此操作的最佳方法是使用loadbalanced应用程序,这将需要您设置静态MachineKey。另一个选择是通过在站点的根目录中放置一个名为app_offline.htm
的文件来关闭站点,这将使站点脱机并显示您的消息 - 至少用户会发现问题出错。
答案 1 :(得分:14)
现在我已经使用了一个解决方案,可以在抛出异常时擦除cookie。如果再次抛出异常,我会让它发生。
我现在不会将此标记为“答案”,希望有人能有更好的答案。
public static class MyAntiForgeryExtensions
{
// Methods
public static string MyAntiForgeryToken(this HtmlHelper helper)
{
return MyAntiForgeryToken(helper, null);
}
public static string MyAntiForgeryToken(this HtmlHelper helper, string salt)
{
string fragment;
string path = helper.ViewContext.HttpContext.Request.ApplicationPath;
try
{
fragment = helper.AntiForgeryToken(salt, null, path);
}
catch (HttpAntiForgeryException)
{
// okay, scrub the cookie and have another go.
string cookieName = GetAntiForgeryTokenName(path);
helper.ViewContext.HttpContext.Request.Cookies.Remove(cookieName);
fragment = helper.AntiForgeryToken(salt, null, path);
}
return fragment;
}
#region AntiForgeryData code that shouldn't be sealed
// Copied from AntiForgeryData since they aren't accessible.
internal static string GetAntiForgeryTokenName(string appPath) {
if (String.IsNullOrEmpty(appPath)) {
return "__RequestVerificationToken";
}
else {
return "__RequestVerificationToken_" + Base64EncodeForCookieName(appPath);
}
}
private static string Base64EncodeForCookieName(string s) {
byte[] rawBytes = Encoding.UTF8.GetBytes(s);
string base64String = Convert.ToBase64String(rawBytes);
// replace base64-specific characters with characters that are safe for a cookie name
return base64String.Replace('+', '.').Replace('/', '-').Replace('=', '_');
}
#endregion
}
答案 2 :(得分:11)
我遇到了这个问题并修复了你需要做的事情就是在你的web-config中添加一个显式的机器密钥......
<machineKey validationKey="D82960E6B6E9B9029D4CAB2F597B5B4AF631E3C182670855D25FBDE1BFAFE19EFDE92ABBD1020FC1B2AE455D5B5F8D094325597CE1A7F8B15173407199C85A16" decryptionKey="577404C3A13F154908D7A5649EEC8D7C8A92C35A25A3EC078B426BB09D426A71" validation="SHA1" decryption="AES" />
确保将其置于...内的web.config中
<system.web>
答案 3 :(得分:7)
您可以将AntiForgeryConfig.SuppressIdentityHeuristicChecks = true;
添加到global.asax
protected void Application_Start() {
AntiForgeryConfig.SuppressIdentityHeuristicChecks = true;
}
答案 4 :(得分:4)
如果我在我的网络服务器上执行iisreset 并且用户继续他们的 会话他们被反弹到登录 页。
iisreset 没有理由将用户带到登录页面。如果您使用cookie来跟踪身份验证信息并拥有无状态应用程序,则即使在重新启动服务器之后,用户也应该保持身份验证(当然,如果在重置期间发出请求,它将失败)。
答案 5 :(得分:4)
实际上我发现这在我的登录操作中起作用:
public ActionResult LogOn()
{
formsAuthentication.SignOut();
Response.Cookies.Clear();
Session[SessionKeys.USER_SESSION_KEY] = null;
Session.Clear();
Session.Abandon();
return View();
}
重要的部分是:Response.Cookies.Clear();