Ошибка 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 не может быть установлен.