Почему HttpAntiForgeryException возникает случайно даже со статическим ключом машины? - PullRequest
17 голосов
/ 29 марта 2012

У нас есть приложение ASP.NET MVC 2 (.NET 4), работающее в Windows Azure (последняя версия 2.x ОС) с двумя экземплярами веб-роли.

Мы используем токен защиты от подделки, предоставленный MVC для всех запросов POST, и мы установили статический ключ компьютера в web.config, чтобы все работало на нескольких компьютерах и при перезапусках. В 99,9% случаев работает отлично.

Время от времени, однако, мы регистрируем исключение HttpAntiForgeryException с сообщением «Требуемый токен защиты от подделки не был предоставлен или был недействительным».

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

Ошибка возникает в различных браузерах и, очевидно, вызывает проблемы у пользователей, поскольку им приходится повторять операцию, иначе они могут потерять некоторые данные. Достаточно сказать, что мы не смогли воспроизвести проблему локально, но это происходит только в Windows Azure.

Почему это происходит? Как мы можем избежать этого?

Ответы [ 5 ]

17 голосов
/ 11 мая 2012

Я тоже столкнулся с этим недавно и нашел две причины.

1.Браузер восстанавливает последний сеанс при открытии для кэшированной страницы

Если у вас есть кэшируемая страница, которая выполняет публикацию на ваш сервер (т. Е. Будет включена защита от подделки), и для пользователя настроен браузер навосстановить последний сеанс при запуске (эта опция существует в Chrome) страница будет отображаться из кэша.Однако cookie подтверждения запроса не будет, поскольку он является файлом cookie сеанса браузера и удаляется при закрытии браузера.Поскольку файл cookie исчез, вы получаете исключение против подделки.Решение. Верните заголовки ответа, чтобы страница не кэшировалась (например, Cache-Control: private, no-store).

2.Состояние гонки, если при запуске на ваш сайт открывается более одной вкладки

Браузеры имеют возможность открыть набор вкладок при запуске.Если более одного из них попало на ваш сайт, который возвращает cookie-файл подтверждения запроса, вы можете столкнуться с условием гонки, при котором cookie-запрос подтверждения запроса перезаписывается.Это происходит потому, что более одного запроса попадает на ваш сервер от пользователя, для которого не настроен файл cookie проверки запроса.Первый запрос обрабатывается и устанавливает cookie проверки запроса.Затем второй запрос обрабатывается, но он не отправил cookie (еще не был установлен во время запроса), поэтому сервер генерирует новый.Новый перезаписывает первый, и теперь эта страница получит исключение запроса на защиту от подделки при следующем выполнении сообщения.Каркас MVC не обрабатывает этот сценарий.Эта ошибка была сообщена команде MVC в Microsoft.

7 голосов
/ 29 марта 2012

Маркер защиты от подделки содержит имя пользователя, подключенного в данный момент, когда он отправляется. А при проверке его действительности текущий подключенный пользователь проверяется на соответствие тому, который использовался при выдаче токена. Так, например, если у вас есть форма, в которой пользователь еще не аутентифицирован, и вы выдаваете маркер защиты от подделки, в нем не будет храниться имя пользователя. Если при отправке формы вы аутентифицируете пользователя, токен больше не будет действительным. То же самое относится и к выходу из системы.

Вот как выглядит метод Validate:

public void Validate(HttpContextBase context, string salt)
{
    string antiForgeryTokenName = AntiForgeryData.GetAntiForgeryTokenName(null);
    string str2 = AntiForgeryData.GetAntiForgeryTokenName(context.Request.ApplicationPath);
    HttpCookie cookie = context.Request.Cookies[str2];
    if ((cookie == null) || string.IsNullOrEmpty(cookie.Value))
    {
        throw CreateValidationException();
    }
    AntiForgeryData data = this.Serializer.Deserialize(cookie.Value);
    string str3 = context.Request.Form[antiForgeryTokenName];
    if (string.IsNullOrEmpty(str3))
    {
        throw CreateValidationException();
    }
    AntiForgeryData data2 = this.Serializer.Deserialize(str3);
    if (!string.Equals(data.Value, data2.Value, StringComparison.Ordinal))
    {
        throw CreateValidationException();
    }
    string username = AntiForgeryData.GetUsername(context.User);
    if (!string.Equals(data2.Username, username, StringComparison.OrdinalIgnoreCase))
    {
        throw CreateValidationException();
    }
    if (!string.Equals(salt ?? string.Empty, data2.Salt, StringComparison.Ordinal))
    {
        throw CreateValidationException();
    }
}

Один из возможных способов отладки - это перекомпилировать ASP.NET MVC из его исходного кода и точно указать, в каких случаях if вы вводите исключение.

1 голос
/ 25 сентября 2012

У меня есть несколько веб-приложений MVC3, которые также получают это довольно регулярно. Большинство из них потому, что клиент не отправляет тело POST. И большинство из них IE8 из-за некоторой ошибки с ajax-запросами, предшествующей обычной публикации. Существует исправление для IE, которое, похоже, устраняет симптомы, что в некоторой степени доказывает, что в этих случаях это ошибка клиента

http://support.microsoft.com/?kbid=831167

Есть несколько дискуссий по этой проблеме в Интернете, хотя ничего особенного, я определенно не собираюсь возиться с тайм-аутами keep-alive, которые в некоторых местах являются «решением» ...

https://www.google.com/search?q=ie8+empty+post+body

Мне никогда не удавалось воспроизвести его с различными попытками сброса соединений между POSTS, поэтому я боюсь, что у меня нет реального решения для случая пустых тел POST в IE. Мы немного уменьшили его, чтобы убедиться, что мы никогда не используем метод POST при получении данных через ajax.

Если вы регистрируете полный запрос, проверьте, является ли тело POST пустым, и если это так, то, вероятно, это будет более старый IE. И я не имею в виду Content-Length: 0, у него обычно будет Content-Length, который кажется правильным в заголовках, но буквально ничего не будет после заголовков в запросе.

Проблема в целом все еще остается для меня загадкой, потому что мы все еще получаем случайное исключение, когда существует полное тело POST. Наши имена пользователей никогда не меняются, и наши ключи также являются статическими, я не пробовал добавлять отладку к источнику, если я когда-нибудь об этом узнаю, я сообщу о своих выводах.

0 голосов
/ 03 мая 2013

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

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

Я обнаружил, что лучшим решением является перехват события onbeforeunload для страницы и явная очистка значения скрытого поля ввода, содержащего значение токена в DOM.

Если загружена кэшированная копия страницы, она, кажется, содержит очищенное значение поля ввода.Затем я проверяю это в функции готовности документа и при необходимости перезагружаю страницу:

window.location.reload(true);

Кажется, что она работает достаточно эффективно, и я подозреваю, что это может быть и для кода защиты от подделки MVC.

0 голосов
/ 29 марта 2012

Есть несколько вариантов того, что вы можете попробовать. Вы можете попробовать выполнить удаленное взаимодействие с устройством и просмотреть журнал событий, чтобы узнать, сможете ли вы получить дополнительную информацию о том, где это происходит. Если это не помогает, вы можете использовать DebugDiag или другой инструмент для захвата дампа процесса (DebugDiag позволит вам захватить один во время этого конкретного исключения) А затем посмотрите на это, чтобы увидеть, что происходит.

Если вы не можете понять это оттуда, вы всегда можете создать службу поддержки в Microsoft, чтобы помочь вам разобраться в этом.

...