MVC3 AntiForgeryToken Проблема - PullRequest
       17

MVC3 AntiForgeryToken Проблема

1 голос
/ 29 августа 2011

Я пытаюсь реализовать AntiForgeryToken для моего приложения MVC3. У меня проблема с AntiForgeryToken после установки cookie FormAuthentication. Вот простой пример, который объясняет мою проблему.

У меня есть домашний контроллер со следующими методами действий:

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

А вот мой логин и О просмотрах:

Logon.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" />

   }

About.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" />
}

У меня нет проблем с методом записи "Вход в систему". Это валидация антифогергена и рендеринг О представлении. Интересно, что при публикации в представлении «О программе» я получаю сообщение об ошибке «Требуемый токен защиты от подделки не был предоставлен или был недействительным»

Кто-нибудь может указать, что я здесь делаю неправильно?

Ценю вашу помощь.

Ответы [ 3 ]

5 голосов
/ 30 августа 2011

Я провел несколько тестов и определил, что даже после того, как вы позвоните 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-атаки обычно используют только аутентифицированных пользователей.

2 голосов
/ 29 августа 2011

Помощник AntiForgeryToken не добавляет в ответ куки-файлы, если в запросе есть куки-файлы с таким же именем. Также помощник AntiForgeryToken использует Principal.Identity.Name для возврата значения для скрытого поля.

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

Таким образом, когда в представлении «Вход в систему» ​​используется Html.AntiForgeryToken, для ответа устанавливается новый файл cookie и скрытое поле с тем же значением. Когда ваш вид входа в систему публикует этот файл cookie со скрытым полем, исключение не будет выдано, поскольку оба файла cookie запроса и значения скрытого поля совпадают. Но в случае представления «О программе» к ответу не будут добавлены дополнительные файлы cookie, но благодаря IIdentty будет возвращено новое скрытое значение для помощника. Поэтому, когда вы публикуете «О действии», возникает исключение, потому что cookie и скрытое значение не совпадают.

Это может быть ошибка в реализации AntiForgeryToken.

2 голосов
/ 29 августа 2011

Кажется, я помню, что после того, как вы вошли в систему, ваш токен изменился, так как ваше имя пользователя, как мне кажется, меняет этот токен, следовательно, больше не будет действительнымЯ попытаюсь проверить это дважды, но почти наверняка столкнулся с этим в прошлом.

Однако в приведенном выше коде вы столкнетесь с другими проблемами, если будете использовать этот шаблон.Действия публикации обычно не предназначены для отображения представления, если только не произошла ошибка исключения / проверки и вы повторно отображаете страницу.Вообще вы бы перенаправили.Я вижу, что кто-то затронул это в комментарии выше, и они правы.

Это не означает, что вы не должны использовать эти действия, но остерегайтесь пересекать это через логин.В этом предыдущем посте упоминается использование имени пользователя с токенами:

Устранение проблем с токенами против подделки


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


...