Ошибка Rails4 ActionController:: InvalidAuthenticityToken

У меня есть приложение Rails4, запущенное на производстве, и мои посетители периодически запускают ошибку ActionController:: InvalidAuthenticityToken, которую я не могу воспроизвести. Я получаю 2-4 ежедневных уведомления из разных форм, без четкой логики. Полученный отчет показывает, что authenticity_token, представленный формой, отличается от того, который хранится в сеансе. Как это возможно? Мне удалось несколько раз столкнуться с проблемой, однако ее невозможно воспроизвести, вся внезапная аутентичность_токена для формы отличается от той, что хранится в сеансе, и возникает InvalidAuthenticityToken.
Любые идеи, с которых начать искать?

Пример:

 Request:
-------------------------------

  * URL        : https://domain/signin
  * HTTP Method: POST
  * IP address : 113.96.xx.xx
  * Parameters : {"utf8"=>"✓", "authenticity_token"=>"MOh9JDE1AZ0CbIw/M33vfhjRShwzI6oqMhi8lk+n7OE=", "email"=>"[email protected]", "password"=>"[FILTERED]", "commit"=>"Sign In", "controller"=>"clients", "action"=>"signin", "locale"=>"en"}

-------------------------------
Session:
-------------------------------

  * session id: [FILTERED]
  * data: {"_csrf_token"=>"QazCSVGeZlxEh83XTM+f5PkC/zopwCF96yV4duRats0="}

Обновление. Хотелось добавить, что я обслуживаю страницы через два загруженных по весу AWS EC2-экземпляра и сохраняю сеансы в экземпляре Redis ElastiCache.

Ответы

Ответ 1

Чтобы ответить на мой собственный вопрос, если кто-то столкнется с той же проблемой, кажется, что удаление csrf_meta_tag из заголовка устраняет проблему для нас. Я не знаю, почему. Может быть, что rails javascript, ответственный за установку auth_token, каким-то образом вмешивался в наш javascript и вызывал эту проблему, но мое чувство кишки состоит в том, что для этого нужно было что-то с кешем, либо на сервере, либо на стороне клиента. В любом случае, после удаления csrf_meta_tag кажется, что мы избавились от этой проблемы. Просто убедитесь, что вы используете form_tag для всех ваших форм.

Ответ 2

Любые формы, созданные Rails (т.е. с form_for и т.д., а не с вами, помещая <form> в шаблон), будут иметь токен анти-CSRF, добавленный как скрытое поле, когда это необходимо. Если вы написали форму самостоятельно и не включили скрытый ввод CSRF, то Rails использует метатег CSRF и JavaScript для работы. Поэтому, если вы написали свою собственную форму, и вы не указали скрытое поле, и если клиентский JavaScript не работает по какой-либо причине, вы можете получить эту ошибку. Поскольку "клиентский JavaScript не работает по какой-либо причине" предложение трудно определить и отладить, я фактически намеренно удалил метатег CSRF на своем сайте. Таким образом, если я забуду включить скрытый ввод, он сломается для всех (быстро сбой), я сразу узнаю об этом, и я мог бы это исправить. Я бы порекомендовал вам это сделать.

С учетом сказанного я бы рекомендовал вам посмотреть журнал доступа для этих "посетителей". Вы видите что-то странное?

  • Получают ли они доступ к странице, на которой есть форма, перед ее отправкой? Если нет, возможно, это боты или фактические попытки CSRF с реальной сделкой (что это проверяет, не так ли?:)).
  • Они загрузили форму на одном EC2 и в итоге отправили на другой? Если да, можете ли вы отключить один EC2 и посмотреть, исчезли ли ошибки?
  • Неужели они потеряли свою сессию? Это может быть ваша проблема или ваша.

Ответ 3

Так как rails использует JavaScript для добавления форм authenticity_token в rails, я бы дважды трижды проверил, что у вас нет ошибки JS во время выполнения на основе динамического содержимого, вызывающего этот heisenbug. Если JS-ошибка приведет к повреждению всего файла application.js, ваши формы будут недействительными. Возможно ли это?

Ответ 4

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

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

Я вижу, что это проблема, если

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

Ответ 5

У меня были такие же проблемы. Сервер: nginx + пассажир

nginx.conf:

http {
    ...
    expires    90d;
    ...
    server {
        server_name domain1.com
        ...
    }
    server {
        server_name domain2.com
        ...
    }
    server {
        server_name domain_3_with_rails.com
        ...
    }
}

Проблемы с инструкцией "истекает 90d;" Браузер локально кэширует страницу с формой и с помощью authenticity_token.

Решение: добавить "expires 0d;" для доменов rails:

nginx.conf:

http {
    ...
    expires    90d;
    ...
    server {
        server_name domain1.com
        ...
    }
    server {
        server_name domain2.com
        ...
    }
    server {
        server_name domain_3_with_rails.com
        expires 0d;
        ...
    }
}

После этого обязательно перезапустите Nginx.

Для тех, у кого есть apache: apache, он, безусловно, имеет аналогичную инструкцию "expires" для nginx

Ответ 6

У меня такая же проблема. Я уже говорил, что если я выключу cookies (заблокировать домен, чтобы иметь возможность использовать файлы cookie), я буду запускать ActionController:: InvalidAuthenticityToken каждый раз, когда я делаю POST.

Таким образом, пользователь имеет JS, но не разрешает куки.

AFAIK токен анти-CSRF в Rails отправляется в качестве стороны сервера cookie сеанса, а затем сбой, поскольку cookie не может быть установлен.