1

我正在尝试为我的 MVC3 应用程序实现 AntiForgeryToken。设置 FormAuthentication cookie 后,我遇到了 AntiForgeryToken 问题。这是一个解释我的问题的简单示例。

我有具有以下操作方法的家庭控制器:

public class HomeController : Controller
{
    public ActionResult Logon()
    {
        return View();
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Logon(string userName, string password)
    {
        FormsAuthentication.SetAuthCookie(userName, false);
        return View("About");
    }


    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult About(FormCollection form)
    {
        return View("PageA");
    }
 }

这是我的登录和关于视图:

登录.cshtml:

   @using (Html.BeginForm("Logon", "Home"))
   {

    @Html.AntiForgeryToken()

    <label> UserName :</label>
    <input  name = "userName" type="text"/>
    <br />
    <label> Password :</label>
    <input name = "password" type="password"/>
    <br />
    <br />
    <input type="submit" value="LogOn" />

   }

关于.cshtml

@using (Html.BeginForm("About", "Home"))
{

    @Html.AntiForgeryToken()
    <p> This is conent of page About</p>
    <input  name = "moreInfo" type="text"/>

    <input type="submit" value="SubmitAbout" />
}

我对“登录”发布方法没有问题。它正在验证 antiforgerytoken 并呈现 About 视图。有趣的是,当我在“关于”视图上发帖时,我收到错误“未提供所需的防伪令牌或无效”

有人能指出我在这里做错了什么吗?

感谢你的帮助。

4

3 回答 3

5

我做了一些测试,并确定即使在你调用之后FormsAuthentication.SetAuthCookie(...),问题是httpContext.User.Identity.Name在请求期间它仍然是空的。

因此,要解决此问题,您需要手动设置电流User,如下所示:

FormsAuthentication.SetAuthCookie(email, true);
this.HttpContext.User = new GenericPrincipal(new GenericIdentity(email), null);

这将设置调用User时使用的正确值。Html.AntiForgeryToken()

请注意,对于普通 PRG 模式的网站,此代码不是必需的,因为重定向后,User将加载正确的代码。

此外,由于您的Logon方法需要有效的用户名和密码,因此它不太容易受到 CSRF 攻击,因此您可能不需要ValidateAntiForgeryToken在该方法上使用。也许这就是 AntiForgeryToken 依赖于用户名的原因。CSRF 攻击通常只利用已经过身份验证的用户。

于 2011-08-30T05:00:52.257 回答
2

我似乎记得,一旦您登录,您的令牌现在就不同了,因为您的用户名我相信会更改此令牌,因此将不再有效。我会尝试仔细检查这一点,但几乎可以肯定我过去遇到过这个问题。

但是,在您上面的代码中,如果您使用此模式,您将遇到其他问题。除非出现异常/验证错误并且您正在重新显示页面,否则发布操作通常并不意味着显示视图。通常你会重定向。我看到有人在上面的评论中提到了这一点,他们是正确的。

这并不意味着您不应该使用这些操作,但请注意不要在登录时使用这些操作。之前的这篇文章提到了将用户名与令牌一起使用:

解决防伪令牌问题


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();
        }
    }


于 2011-08-29T18:37:03.873 回答
2

如果请求中存在同名 cookie,AntiForgeryToken Helper 不会将任何 cookie 添加到响应中。AntiForgeryToken Helper 也使用 Principal.Identity.Name 返回隐藏字段的值。

            AntiForgeryData formToken = new AntiForgeryData(cookieToken) {
               Salt = salt,
               Username = AntiForgeryData.GetUsername(httpContext.User)
            };

因此,当您的登录视图使用 Html.AntiForgeryToken 时,会在响应中设置一个新的 cookie 和一个具有相同值的隐藏字段。当您的登录视图发布此带有隐藏字段的 cookie 时,不会抛出异常,因为请求 cookie 和隐藏字段值都匹配。但是在 About 视图的情况下,响应中不会添加额外的 cookie,但是由于 IIdentty,将为 helper 返回一个新的隐藏值。因此,当您发布关于操作时,会引发异常,因为 cookie 和隐藏值不匹配。

这可能是 AntiForgeryToken 实现中的一个错误。

于 2011-08-29T18:47:32.077 回答