Обнаружена CSRF в OAuth2 при использовании функции «Подключить» Discord

Привет! Мне очень нужна помощь в решении этой ошибки:


Шаги для воспроизведения:
Когда пользователь нажимает кнопку «Подключить» (Connect) в Discord в разделе «Настройки»,


его корректно перенаправляет на страницу авторизации Discord.

Однако после нажатия кнопки «Разрешить» (Authorize) пользователя перенаправляет, и на нашем форуме появляется это сообщение:
image
а ошибка, показанная вверху этой темы, появляется в логах администратора.


Кажется, я прочитал и перепробовал всё, чтобы исправить это, но проблема всё ещё возникает. Я убедился, что настройки Client ID и Secret для Discord верны.
Также я проверил, что URI имеет правильный синтаксис (исходя из нескольких связанных тем, которые я видел):

Есть какие-нибудь предложения? Я готов попробовать что угодно, даже если вы не уверены, что это сработает :laughing:

Есть какие-нибудь идеи? Всё ещё не могу справиться с этим :confused:

Я думаю(?), что сузил круг до проблемы с nginx и/или кэшированием? Должны ли в discourse.conf быть определены специфичные для авторизации или CSRF настройки, которые мы могли упустить?

@merefield, @david, @sam — извините за уведомления, но я вижу ваши имена во многих обсуждениях, связанных с CSRF в прошлом. У вас есть какие-либо рекомендации по этому вопросу? Поскольку аутентификация через Discord является интегрированной частью Discourse, я в тупике и не понимаю, что может вызывать эту проблему.

Заранее благодарю за любую помощь, спасибо :smiling_face:

Мы, должно быть, нарушаем одно из этих условий?

Я всё ещё не могу найти закономерность. Иногда всё работает, и меня корректно подключают, но в других случаях я попадаю на страницу CSRF.

На данный момент больше всего вызывает подозрение последняя проверка условия в verified_request?.
Есть ли способ легко проверить, возвращает ли (valid_request_origin? && any_authenticity_token_valid?) значение true?

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


На снимках ниже показаны последовательные случаи: сначала мне удалось успешно связать учётную запись, затем я обновил страницу и попытался снова, но получил страницу с ошибкой «обнаружен CSRF». Я работал в режиме инкогнито и между успешным подключением и сбоем из-за CSRF буквально ничего не менял и не делал. Вот что я обнаружил:

На первом снимке видно, что кука _forum_session совпадает в заголовках запросов 1 и 2, что привело к успешному подключению.

Однако после перезагрузки страницы и повторной попытки (которая не увенчалась успехом) видно, что в поиске слева в заголовке запроса, приведшем к сбою, отображается только одно вхождение куки _forum_session.

Кратко: я почти уверен, что проблема заключается в том, что кука forum_session в заголовке запроса discord?reconnect не совпадает с кукой в заголовке последующего запроса callback?. Что может вызывать такое расхождение?

Хорошо, я думаю, мы приближаемся к решению.

Итак, на скриншоте ниже вы можете видеть, что POST-запрос обновления выполняется непосредственно после POST-запроса discord?reconnect.


И действительно, именно это устанавливает cookie _forum_session, из-за чего возникает несоответствие, о котором я упоминал выше.

Если же посмотреть на успешный пример подключения (ниже), то видно, что обновление происходит только до POST-запроса discord?reconnect.

Благодаря этому cookie _forum_session совпадает, и учётная запись успешно подключается без проблемы с CSRF.

Как предотвратить выполнение этого обновления после того, как пользователь начал процесс подключения?

@FerrariFlunker извините за медленный ответ, но у меня пока не было возможности посмотреть на это. Было бы здорово, если бы это сделал основной комьюнити-команда.

Если это хоть как-то утешит, я могу воспроизвести проблему, насколько я понимаю, получаю ту же ошибку:

(discord) Authentication failure! csrf_detected: OmniAuth::Strategies::OAuth2::CallbackError, csrf_detected | CSRF detected

Не волнуйся, @merefield, я ценю твой ответ!

У тебя возникает эта ошибка в твоей собственной среде? Я думаю, что в любом случае эту ошибку нужно проверить основной команде. Я уже более двух недель разбираюсь с этим и всё ещё не нашёл ответа :confused:

Отличное расследование! Такое состояние гонки действительно могло вызвать проблемы, с которыми вы столкнулись.

Тем не менее, у нас не было других сообщений об этой проблеме, поэтому похоже, что дело в чём-то специфичном для вашего сайта или конфигурации. Какие плагины установлены на сайте? Можете ли вы открыть вызов «update» и посмотреть, какой полезный груз отправляется?

После изучения вызовов обновления я склоняюсь к тому, что вы правы. Вот несколько скриншотов подтверждённых запросов «update», которые вызвали сбои CSRF.

Я вижу закономерность, связанную с CDN :face_with_monocle: Что может быть неправильно настроено в этом случае? Дайте знать, если вам всё ещё нужен список наших плагинов; я просто подумал, что сэкономлю этот ответ от +1 картинки :smile:

У меня есть отличная новость! Несколько дней назад я внимательнее изучил полезную нагрузку «обновления» и случайно смог связать это:

с одним из наших плагинов! :partying_face: :expressionless: :smile:
После отключения плагина, дополнительных тестов и успешного запуска соответствующей функции для сообщества… я могу с уверенностью сказать, что мы нашли виновника.

Таким образом, этим проблемным плагином оказался: GitHub - discourse/discourse-chat: Chat inside Discourse · GitHub

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

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

Если кто-то всё же внесёт исправление в плагин, мы (скорее всего :smile:) рассмотрим возможность повторного включения его на нашем сайте, но пока нам необходима стабильная функция подключения связанных аккаунтов.

Ещё раз спасибо всем, кто помогал в диагностике! :+1:

Спасибо за столь детальное расследование, @FerrariFlunker!

Я только что создал PR с исправлением в ядре Discourse:

Причина, по которой проблема была исправлена после удаления плагина Chat, заключается в том, что Chat активно использует этот API ‘PresenceChannel’, поэтому вероятность её возникновения гораздо выше. Я не думаю, что в плагине Chat потребуются какие-либо изменения.

Должно ли это исправить ту же проблему с входом через Google? Мои пользователи на одном из моих инстансов, где мы тестировали плагин чата, обошли его, но вход через Google сломался с той же ошибкой, что и вход через Discord.

Да, это исправление будет применяться ко всем типам входа: +1: