解决防伪令牌问题

时间:2011-04-24 00:00:06

标签: asp.net-mvc asp.net-mvc-3 antiforgerytoken

我有一个表单帖子,一直给我一个反伪造令牌错误。

这是我的表格:

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.EditorFor(m => m.Email)
    @Html.EditorFor(m => m.Birthday)
    <p>
        <input type="submit" id="Go" value="Go" />
    </p>
}

这是我的行动方法:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Join(JoinViewModel model)
{
    //a bunch of stuff here but it doesn't matter because it's not making it here
}

这是web.config中的machineKey:

<system.web>
  <machineKey validationKey="mykey" decryptionKey="myotherkey" validation="SHA1" decryption="AES" />
</system.web>

这是我得到的错误:

A required anti-forgery token was not supplied or was invalid.

我已经读过,更改HttpContext上的用户会使令牌无效,但这不会发生在这里。我的Join操作上的HttpGet只返回视图:

[HttpGet]
public ActionResult Join()
{
    return this.View();
}

所以我不确定发生了什么。我一直在搜索,一切似乎表明它是机器键更改(应用程序周期)或用户/会话更改。

还有什么可以发生?我该如何解决这个问题?

11 个答案:

答案 0 :(得分:31)

我不知道您是否意味着您可以按需获取错误 - 或者您在日志中看到错误但无论如何这里都是保证防伪令牌错误的方法。

等等......

  • 确保您已退出,然后输入登录信息
  • 双击登录按钮
  • 你会得到:
  

提供的防伪令牌适用于用户“”,但当前用户为“XXX@yahoo.com”。

(现在我假设这个确切的错误消息在MVC4中发生了变化,而且这与你得到的消息基本相同)。

那里有很多人仍然双击所有东西 - 这很糟糕!我刚刚醒来后想到了这一点,所以这是如何通过测试我真的不知道。您甚至不必双击 - 如果按钮没有响应,我第二次点击时自己就会出现此错误。

我刚刚删除了验证属性。我的网站始终是SSL,我并不过分关注风险。我现在需要它才能工作。另一种解决方案是使用javascript禁用按钮。

这可以在MVC4初始安装模板上重复。

答案 1 :(得分:19)

在Adam的帮助下,我将MVC源添加到我的项目中,并且能够看到有很多情况导致同样的错误。

以下是用于验证防伪标记的方法:

    public void Validate(HttpContextBase context, string salt) {
        Debug.Assert(context != null);

        string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
        string cookieName = AntiForgeryData.GetAntiForgeryTokenName(context.Request.ApplicationPath);

        HttpCookie cookie = context.Request.Cookies[cookieName];
        if (cookie == null || String.IsNullOrEmpty(cookie.Value)) {
            // error: cookie token is missing
            throw CreateValidationException();
        }
        AntiForgeryData cookieToken = Serializer.Deserialize(cookie.Value);

        string formValue = context.Request.Form[fieldName];
        if (String.IsNullOrEmpty(formValue)) {
            // error: form token is missing
            throw CreateValidationException();
        }
        AntiForgeryData formToken = Serializer.Deserialize(formValue);

        if (!String.Equals(cookieToken.Value, formToken.Value, StringComparison.Ordinal)) {
            // error: form token does not match cookie token
            throw CreateValidationException();
        }

        string currentUsername = AntiForgeryData.GetUsername(context.User);
        if (!String.Equals(formToken.Username, currentUsername, StringComparison.OrdinalIgnoreCase)) {
            // error: form token is not valid for this user
            // (don't care about cookie token)
            throw CreateValidationException();
        }

        if (!String.Equals(salt ?? String.Empty, formToken.Salt, StringComparison.Ordinal)) {
            // error: custom validation failed
            throw CreateValidationException();
        }
    }

我的问题是将Identity用户名与表单令牌的用户名进行比较的情况。在我的情况下,我没有设置用户名(一个是null,另一个是空字符串)。

虽然我怀疑很多人会遇到同样的情况,但希望其他人会发现看到正在检查的基本条件是有用的。

答案 2 :(得分:9)

AntiForgeryToken还会检查您登录的用户凭据是否未更改 - 这些也在Cookie中加密。您可以通过在global.asax.cs文件中设置AntiForgeryConfig.SuppressIdentityHeuristicChecks = true来关闭此功能。

答案 3 :(得分:8)

您应该阻止双重表单提交。 我使用这样的代码阻止了这类问题:

$('#loginForm').on('submit',function(e){
    var $form = $(this);

    if (!$form.data('submitted') && $form.valid()) {
      // mark it so that the next submit can be ignored
      $form.data('submitted', true);
      return;
    }

    // form is invalid or previously submitted - skip submit
    e.preventDefault();
});

$('#loginForm').submit(function () {
    $(this).find(':submit').attr('disabled', 'disabled'); 
});

答案 4 :(得分:3)

我刚遇到一个问题,其中@Html.AntiForgeryToken()被调用了两次,因此Anti-forger令牌被搞砸了HTTP Post有效负载。

答案 5 :(得分:2)

您是在一台服务器还是一个Web场?如果是单个服务器,请在web.config中注释掉machineKey元素,然后再次尝试作为基本起点。任何改变? 此外 - 你能想到你的cookie将被清除或到期的任何原因 - 它们也需要正常工作。

答案 6 :(得分:2)

在禁用提交按钮之前,必须检查表单是否有效。

<script type="text/javascript">
//prevent double form submission
$('form', '#loginForm').submit(function () {
    if ($(this).valid()) {
        $(this).find(':submit').attr('disabled', 'disabled');
    }
});

答案 7 :(得分:1)

另一个可能的问题是检查哪个导致了我的错误:我的一个表单中有两个@Html.AntiForgeryToken()

一旦删除问题就会消失。

答案 8 :(得分:1)

当我必须将应用程序迁移到新计算机时,我也遇到了同样的问题。我无法理解为什么这个错误突然出现,但觉得它确实与迁移有关,所以开始通过aspnet_reqsql调查数据库的注册,当它被淘汰时我意识到它必须与注册应用程序,当然我在类似的地方找到了答案。我也在旅途中发现有两种方法可以解决这个问题。

  1. ASP.NET会自动为每个应用程序生成加密密钥,并将密钥存储在HKCU注册表配置单元中。在服务器场上迁移或访问应用程序时,这些键不匹配,因此方法一是向web.config添加唯一的机器密钥。可以从IIS管理控制台生成计算机密钥,并将其添加到web.config

    部分

    &lt; machineKey validationKey =&#34; DEBE0EEF2A779A4CAAC54EA51B9ACCDED506DA2A4BEBA88FA23AD8E7399C4A8B93A006ACC1D7FEAEE56A5571B5AB6D74819CFADB522FEEB101B4D0F97F4E91&#34; decryptionKey =&#34; 7B1EF067E9C561EC2F4695578​​295EDD5EC454F0F61DBFDDADC2900B01A53D4&#34;验证=&#34; SHA1&#34;解密=&#34; AES&#34; /&GT;

  2. 第二种方法是使用AspNet_RegIIS和交换机-ga或使用-i

  3. 为所有应用程序通过appPool授予对HKCU注册表的访问权限
      

    aspnet_regiis -ga&#34; IIS APPPOOL \ app-pool-name&#34;

    无论您选择哪种方法都可以解决此问题,但我认为,未来迁移和服务器更改最强大的方法将是web.config中的唯一键,请记住这会导致应用覆盖HKCU注册表配置单元并让您运行应用程序。

答案 9 :(得分:1)

我遇到了类似的问题。我得到了:

The required anti-forgery form field "__RequestVerificationToken" is not present. 

我感兴趣的是我尝试调试它并在两个地方看到令牌,我希望在控制器中找到它

var formField = HttpContext.Request.Params["__RequestVerificationToken"];
var cookie = System.Web.HttpContext.Current.Request.Cookies["__RequestVerificationToken"].Value;

实际上我应该一直在这里看:

var formField = HttpContext.Request.Form["__RequestVerificationToken"];

由于Params不仅包含表单字段,它还包含QueryString,Form,Cookies和ServerVariables。

一旦红鲱鱼不在路上,我发现AntiForgeryToken的格式错误了!

答案 10 :(得分:-1)

html错误记录器行不正确。

你必须检查页面中的所有加载值是否为空。