Понимание токена подлинности Rails

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

Но я действительно не хочу просто решать эту проблему и продолжать. Я действительно хотел бы понять маркер Подлинности. Ну, мой вопрос: есть ли у вас какой-либо полный источник информации по этому вопросу, или вы бы потратили свое время, чтобы подробно объяснить здесь?

10 ответов

Решение

Что просходит

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

Почему так происходит

Поскольку токен аутентификации хранится в сеансе, клиент не может знать его значение. Это не позволяет людям отправлять формы в приложение Rails, не просматривая форму в самом приложении. Представьте, что вы используете сервис A, вы вошли в сервис и все в порядке. Теперь представьте, что вы пошли пользоваться услугой B, увидели понравившуюся вам картинку и нажали на нее, чтобы просмотреть ее в большем размере. Теперь, если какой-то злой код был в службе B, он может отправить запрос в службу A (в которую вы вошли) и попросить удалить вашу учетную запись, отправив запрос http://serviceA.com/close_account, Это то, что известно как CSRF (Подделка межсайтовых запросов).

Если служба A использует маркеры подлинности, этот вектор атаки больше не применяется, поскольку запрос от службы B не будет содержать правильный токен подлинности и не будет разрешен для продолжения.

Документация API описывает подробности о метатеге:

CSRF защита включена с protect_from_forgery метод, который проверяет токен и сбрасывает сеанс, если он не соответствует ожидаемому. Вызов этого метода генерируется для новых приложений Rails по умолчанию. Параметр токена называется authenticity_token по умолчанию. Имя и значение этого токена необходимо добавить в каждый макет, который отображает формы, включив csrf_meta_tags в голове HTML.

Заметки

Имейте в виду, что Rails проверяет только не идемпотентные методы (POST, PUT/PATCH и DELETE). GET-запрос не проверяется на подлинность токена. Зачем? поскольку в спецификации HTTP говорится, что запросы GET являются идемпотентными и не должны создавать, изменять или уничтожать ресурсы на сервере, а запрос должен быть идемпотентным (если вы выполняете одну и ту же команду несколько раз, вы должны получать один и тот же результат каждый раз).

Также реальная реализация немного сложнее, как определено в начале, обеспечивая лучшую безопасность. Rails не выдает одинаковый сохраненный токен с каждой формой. Он также не генерирует и не сохраняет новый токен каждый раз. Он генерирует и хранит криптографический хеш в сеансе и выдает новые криптографические токены, которые могут сопоставляться с сохраненным, каждый раз при отображении страницы. Смотрите request_forgery_protection.rb.

занятия

использование authenticity_token чтобы защитить ваши не идемпотентные методы (POST, PUT/PATCH и DELETE). Также убедитесь, что не разрешены какие-либо запросы GET, которые потенциально могут изменить ресурсы на сервере.


РЕДАКТИРОВАТЬ: Проверьте комментарий @erturne относительно GET-запросов, являющихся идемпотентными. Он объясняет это лучше, чем я здесь.

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

Если у вас просто возникают проблемы с тем, что rails отказывают в доступе к AJAX-скрипту, вы можете использовать

<%= form_authenticity_token %>

создать правильный токен при создании формы.

Вы можете прочитать больше об этом в документации.

Что такое CSRF?

Токен подлинности является контрмерой для подделки межсайтовых запросов (CSRF). Вы спрашиваете, что такое CSRF?

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

Сценарий:

  • Посетите сайт вашего банка, войдите.
  • Затем посетите сайт злоумышленника (например, рекламную рекламу ненадежной организации).
  • На странице злоумышленника есть форма с теми же полями, что и у банковской формы "Перевод средств".
  • Атакующий знает данные вашей учетной записи и имеет предварительно заполненные поля формы для перевода денег с вашей учетной записи на учетную запись злоумышленника.
  • На странице злоумышленника есть Javascript, который отправляет форму в ваш банк.
  • Когда форма отправлена, браузер включает ваши куки для сайта банка, включая токен сеанса.
  • Банк переводит деньги на счет злоумышленника.
  • Форма может быть в невидимом фрейме, поэтому вы никогда не узнаете, что атака произошла.
  • Это называется подделкой межсайтовых запросов (CSRF).

CSRF решение:

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

Токен подлинности используется для предотвращения атак подделки межсайтовых запросов (CSRF). Чтобы понять маркер подлинности, вы должны сначала понять атаки CSRF.

CSRF

Предположим, что вы являетесь автором bank.com, У вас есть форма на вашем сайте, которая используется для перевода денег на другой счет с запросом GET:

Хакер может просто отправить HTTP-запрос на сервер, говоря GET /transfer?amount=$1000000&account-to=999999, право?

Неправильно. Атака хакеров не сработает. Сервер будет в принципе думать?

А? Кто этот парень пытается инициировать перевод. Это не владелец аккаунта, это точно.

Как сервер знает это? Потому что нет session_id cookie, аутентифицирующий запрашивающего.

Когда вы входите под своим именем пользователя и паролем, сервер устанавливает session_id cookie в вашем браузере. Таким образом, вам не нужно аутентифицировать каждый запрос с вашим именем пользователя и паролем. Когда ваш браузер отправляет session_id cookie, сервер знает:

О, это Джон Доу. Он успешно вошел в систему 2,5 минуты назад. Ему хорошо идти.

Хакер может подумать:

Хм. Обычный HTTP-запрос не сработает, но если бы я мог на это рассчитать session_id печенье, я был бы золотым

В браузере пользователей есть набор файлов cookie для bank.com домен. Каждый раз, когда пользователь делает запрос bank.com домен, все куки отправляются вместе. В том числе session_id печенье.

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

Это довольно просто, на самом деле. Хакер может просто заставить вас зайти на его сайт. На своем веб-сайте он может иметь следующий тег изображения:

<img src="http://bank.com/transfer?amount=$1000000&account-to=999999">

Когда пользовательский браузер обнаружит этот тег изображения, он отправит запрос GET на этот URL. И поскольку запрос поступает из его браузера, он отправит вместе с ним все файлы cookie, связанные с bank.com, Если пользователь недавно вошел в bank.com... session_id Будет установлен cookie, и сервер подумает, что пользователь хотел перевести $1 000 000 на счет 999999!

Ну, просто не посещайте опасные сайты, и все будет в порядке.

Этого не достаточно Что если кто-то разместит это изображение на Facebook, и оно появится на вашей стене? Что если он внедряется в сайт, который вы посещаете с атакой XSS?

Это не так плохо. Только GET-запросы уязвимы.

Не правда. Форма, отправляющая запрос POST, может генерироваться динамически. Вот пример из Rails Guide по безопасности:

<a href="http://www.harmless.com/" onclick="
  var f = document.createElement('form');
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = 'http://www.example.com/account/destroy';
  f.submit();
  return false;">To the harmless survey</a>

Токен подлинности

Когда ваш ApplicationController имеет это:

protect_from_forgery with: :exception

Это:

<%= form_tag do %>
  Form contents
<% end %>

Составлено в это:

<form accept-charset="UTF-8" action="/" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
  Form contents
</form>

В частности, генерируется следующее:

<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />

Для защиты от CSRF-атак, если Rails не увидит токен аутентификации, отправленный вместе с запросом, он не будет считать запрос безопасным.

Как злоумышленник должен знать, что это за токен? Разное значение генерируется случайным образом каждый раз, когда генерируется форма:

Атака межсайтового скриптинга (XSS) - вот как. Но это другая уязвимость для другого дня.

Пример минимальной атаки, которая будет предотвращена

На моем сайте evil.com Я убедил вас отправить следующую форму:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="to"      value="ciro"></p>
  <p><input type="hidden" name="ammount" value="100"></p>
  <p><button type="submit">CLICK TO GET PRIZE!!!</button></p>
</form>

Если вы вошли в свой банк с помощью сеансовых файлов cookie, файлы cookie будут отправлены, а перевод будет осуществлен без вашего ведома.

То есть, когда в игру вступает токен CSRF:

  • с ответом GET, возвращающим форму, Rails отправляет очень длинный случайный скрытый параметр
  • когда браузер делает запрос POST, он отправит параметр вместе, и сервер примет его, только если он соответствует

Таким образом, форма в аутентичном браузере будет выглядеть так:

<form action="http://bank.com/transfer" method="post">
  <p><input type="hidden" name="authenticity_token" value="j/DcoJ2VZvr7vdf8CHKsvjdlDbmiizaOb5B8DMALg6s=" ></p>
  <p><input type="hidden" name="to"                 value="ciro"></p>
  <p><input type="hidden" name="ammount"            value="100"></p>
  <p><button type="submit">Send 100$ to Ciro.</button></p>
</form>

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

Этот метод предотвращения называется шаблоном синхронизатора.

Шаблон токена синхронизатора работает из-за той же политики происхождения: если бы я мог сделать запрос XHR GET в ваш банк из evil.comи прочитав результат, я смогу просто прочитать токен, а затем сделать запрос позже. Я объяснил это далее по адресу: /questions/5222510/avtokolonka-v-spiske-sharepoint/5222512#5222512

Я настоятельно рекомендую вам прочитать руководство OWASP по этому и любому другому вопросу безопасности.

Как Rails отправляет токены

Рассмотрено по адресу: Rails: как работает csrf_meta_tag?

В принципе:

  • HTML-помощники, такие как form_tag добавить скрытое поле в форму для вас, если это не форма GET

  • AJAX автоматически обрабатывает jquery-ujs, который читает токен из meta элементы, добавленные в ваш заголовок csrf_meta_tags (присутствует в шаблоне по умолчанию) и добавляет его к любому выполненному запросу.

    uJS также пытается обновить токен в формах в устаревших кэшированных фрагментах.

Другие профилактические подходы

Authenticity Token это метод rails для предотвращения "атак подделки межсайтовых запросов (CSRF или XSRF)".

Проще говоря, он гарантирует, что запросы PUT / POST / DELETE (методы, которые могут изменять содержимое) к вашему веб-приложению выполняются из браузера клиента, а не от третьей стороны (злоумышленника), имеющей доступ к созданному файлу cookie. на стороне клиента.

Поскольку Authenticity Token это так важно, и в Rails 3.0+ вы можете использовать

 <%= token_tag nil %>

создавать

<input name="authenticity_token" type="hidden" value="token_value">

в любом месте

Остерегайтесь механизма маркера подлинности, который может привести к условиям гонки, если у вас есть несколько одновременных запросов от одного и того же клиента. В этой ситуации ваш сервер может генерировать несколько токенов подлинности, когда их должен быть только один, и клиент, получающий более ранний токен в форме, не выполнит свой следующий запрос, потому что токен cookie сеанса был перезаписан. Есть описание этой проблемы и не совсем тривиальное решение здесь: http://www.paulbutcher.com/2007/05/race-conditions-in-rails-sessions-and-how-to-fix-them/

Методы где authenticity_token необходимо

authenticity_token требуется в случае идемпотентных методов, таких как post, put и delete, потому что идемпотентные методы влияют на данные.

Почему это требуется

Требуется предотвратить злые действия. authenticity_token сохраняется в сеансе, каждый раз, когда на веб-страницах создается форма для создания или обновления ресурсов, токен подлинности сохраняется в скрытом поле и отправляется вместе с формой на сервер. Перед выполнением действия отправленный пользователь authenticity_token перепроверяется с authenticity_token хранится в сессии. Если authenticity_token то же самое, тогда процесс продолжается, иначе он не выполняет действия.

Что такое аутентификационный токен?

Это случайная строка, используемая приложением rails, чтобы убедиться, что пользователь запрашивает или выполняет действие со страницы приложения, а не из другого приложения или сайта.

Зачем нужен аутентификационный токен?

Для защиты вашего приложения или сайта от подделки межсайтовых запросов.

Как добавить маркер аутентификации в форму?

Если вы генерируете форму, используя тег form_for, автоматически добавляется маркер аутентификации, иначе вы можете использовать <%= csrf_meta_tag %>,

Другие вопросы по тегам