Что такое токен CSRF? Каково его значение и как оно работает?

629

Я пишу приложение (Django, так оно и происходит), и я просто хочу понять, что такое «токен CSRF» и как он защищает данные. Являются ли почтовые данные небезопасными, если вы не используете токены CSRF?

Шон
источник
13
Это секретный, специфичный для пользователя токен во всех отправляемых формах и URL-адресах с побочными эффектами для предотвращения подделок межсайтовых запросов. Более подробная информация здесь: en.wikipedia.org/wiki/Cross-site_request_forgery
Роберт Харви
1
Похоже, что есть тонкая грань между защитой вопроса и запретом его из-за слишком широкой: D
anton1980
2
Из таблицы предупреждений о подделке межсайтовых запросов (CSRF) OWASP : « Межсайтовый скриптинг не нужен для работы CSRF. Однако любая уязвимость межсайтового скриптинга может быть использована для того, чтобы победить все методы ослабления CSRF [...]. Это связано с тем, что полезная нагрузка XSS может просто прочитать любую страницу на сайте с помощью XMLHttpRequest [...]. Крайне важно, чтобы не было никаких уязвимостей XSS, чтобы гарантировать, что защита CSRF не может быть обойдена. "
toraritte

Ответы:

1497

Подделка межсайтовых запросов (CSRF) простыми словами

  • Предположим, вы в настоящее время вошли в свой онлайн-банкинг по адресу www.mybank.com
  • Предположим, перевод денег из mybank.comприведет к запросу (концептуально) формы http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>. (Ваш номер счета не нужен, поскольку он подразумевается вашим логином.)
  • Вы заходите www.cute-cat-pictures.org, не зная, что это вредоносный сайт.
  • Если владелец этого сайта знает форму вышеуказанного запроса (просто!) И правильно угадает, что вы вошли в систему mybank.com(требует некоторой удачи!), Он может включить на своей странице запрос типа http://www.mybank.com/transfer?to=123456;amount=10000(где указан 123456номер их учетной записи на Каймановых островах). и 10000это сумма, которую вы ранее думали, что вы были рады иметь).
  • Вы получили эту www.cute-cat-pictures.orgстраницу, поэтому ваш браузер выполнит этот запрос.
  • Ваш банк не может распознать источник этого запроса: ваш веб-браузер отправит запрос вместе с вашим www.mybank.comфайлом cookie, и он будет выглядеть совершенно законно. Там идут ваши деньги!

Это мир без токенов CSRF .

Теперь для лучшего с токенами CSRF :

  • Запрос передачи продлевается с третьим аргументом: http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971.
  • Этот токен представляет собой огромное случайное число, которое невозможно угадать, и которое mybank.comбудет отображаться на их собственной веб-странице, когда оно будет предоставлено вам. Это отличается каждый раз, когда они обслуживают любую страницу никому.
  • Злоумышленник не может угадать токен, не может убедить ваш веб-браузер отказаться от него (если браузер работает правильно ...), и поэтому злоумышленник не сможет создать действительный запрос, поскольку запросы с неправильный токен (или токен не будет) будет отклонен www.mybank.com.

Результат: Вы сохраняете свои 10000денежные единицы. Я предлагаю вам пожертвовать часть этого в Википедии.

(Ваш пробег может отличаться.)

РЕДАКТИРОВАТЬ из комментария стоит прочитать:

Стоит отметить, что скрипт www.cute-cat-pictures.orgобычно не имеет доступа к вашему токену анти-CSRF www.mybank.comиз-за контроля доступа HTTP. Эта заметка важна для некоторых людей, которые необоснованно отправляют заголовок Access-Control-Allow-Origin: *для каждого ответа веб-сайта, не зная, для чего он нужен, просто потому, что они не могут использовать API с другого веб-сайта.

Лутц Пречелт
источник
36
И очевидно, что токен в идеале должен называться токеном анти- CSRF, но название, вероятно, достаточно сложное, как оно есть.
Лутц Пречелт
3
@LutzPrechelt спасибо. почему javascript не может получить токены подлинности из браузера?
BKSpurgeon
72
Стоит отметить, что скрипт www.cute-cat-pictures.orgобычно не имеет доступа к вашему токену анти-CSRF www.mybank.comиз-за контроля доступа HTTP. Это примечание важно для некоторых людей, которые необоснованно отправляют заголовок Access-Control-Allow-Origin: *для каждого ответа веб-сайта, не зная, для чего он нужен, просто потому, что они не могут использовать API с другого веб-сайта.
SOFe
9
@AugustinRiedinger Если злоумышленник откроет веб-страницу на своем компьютере - поскольку у него нет cookie-файла вошедшего в систему пользователя - он не получит соответствующий токен csrf (каждый токен csrf должен быть действителен только для конкретного сеанса пользователя). Если злоумышленник попытается загрузить веб-страницу, содержащую токен, на компьютер пользователя с помощью сценария, размещенного на веб-сайте cute-cat-pictures, браузер запретит ему читать www.mybank.com (и токен) из-за та же политика происхождения.
Марсель
13
@LutzPrechelt Я думаю, что недостаточно, чтобы токен всегда отличался, он должен быть связан с сеансом, и сервер должен проверить, чтобы полученный токен был создан для сеанса, который сервер идентифицирует по полученному куки. В противном случае, хакер может просто зайти в mybank и получить действительный токен. Поэтому, если вы используете новый токен с каждой формой, вы должны сохранить его в паре с sessionid на сервере. Вероятно, проще использовать один и тот же токен для каждой сессии.
Марсель
222

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

Чтобы предотвратить это, django отправит случайный ключ как в cookie, так и в данные формы. Затем, когда пользователи отправляют сообщения POST, он проверяет, идентичны ли два ключа. В случае обмана пользователя сторонний веб-сайт не может получить куки-файлы вашего сайта, что приводит к ошибке аутентификации.

Дмитрий Шевченко
источник
@DmitryShevchenko Привет, пытаясь понять, чем этот метод ввода cookie + формы отличается от простой проверки реферала на стороне сервера? Все примеры, которые я нахожу, связаны с тем, что хакер обманывает пользователя, чтобы он отправлял со своего сайта реальный сайт.
Итан
Хорошо, я выяснил, почему реферер не используется. Во многих случаях он блокируется, поскольку иногда считается, что он содержит конфиденциальную информацию. Корпорации и их доверенные лица обычно делают это. Однако, если используется HTTPS, есть большая вероятность, что он не будет заблокирован.
Итан
4
Изменить реферера легко, я бы не сказал, что это надежная часть информации. Однако токен CSRF создается с использованием секретного ключа сервера и обычно привязывается к пользователю
Дмитрий Шевченко,
1
Я не очень понимаю, почему это угроза безопасности. Пользователь будет авторизован на другом сайте ... но исходный сайт не сможет найти эту информацию. Правильно?
Аакил Фернандес,
6
Хорошо, предположим, что я вставляю вредоносный фрейм " bank.com/transfer?from=x&to=y ", скажем, в Facebook.com. Если вы являетесь клиентом bank.com и заходите на Facebook, этот iframe загрузит банковскую страницу с вашими файлами cookie (потому что браузер отправит их на известный домен) и осуществит перевод денег. Без тебя ничего не зная.
Дмитрий Шевченко
74

Сайт генерирует уникальный токен при создании страницы формы. Этот токен необходим для отправки / получения данных на сервер.

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

tkone
источник
10
Может ли пользователь получить выходные данные токена в источнике, получить cookie-файл, отправленный ему, а затем отправить его со стороннего сайта?
Джек Маркетти
9
@JackMarchetti да. но это будет дорогостоящим, поскольку каждый раз, когда вы хотите отправить форму со стороннего сайта, вам придется загружать страницу и анализировать токен. Токены CSRF должны идеально сочетаться с другими формами безопасности, если вы обеспокоены этим вектором атаки
tkone
4
У меня тот же вопрос, что и у @JackMarchetti, но не ясно, меняется ли маркер CSRF при каждом входе в систему. Если он останется прежним, что помешает злоумышленнику сначала войти в систему, получить токен запроса и затем вставить этот токен в атаку?
Пол Прейбиш
7
@PaulPreibisch должно меняться при каждой загрузке страницы, а не при каждом входе в систему. Таким образом, злоумышленнику придется запрашивать страницу каждый раз, когда он хочет отправить форму. Делает это намного сложнее.
августа
9
@tkone, это не сильно усложняет. Если только удваивает количество усилий и времени. Это не добавляет никакой запрещающей обработки. Хитрость также заключается в том, чтобы связать токен CSRF с файлом cookie, специфичным для домена, и отправить этот файл cookie вместе с формой. И cookie, и данные публикации формы должны быть отправлены на сервер по запросу POST. Для этого потребуется атака на угон cookie, чтобы эмулировать законный запрос.
Педро Кордейру
56

В блоге Cloud Under есть хорошее объяснение токенов CSRF.

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

Форма может быть такой простой:

 <form action="http://a.com/tweet" method="POST">
   <input type="text" name="tweet">
   <input type="submit">
 </form> 

Теперь представьте, плохой парень копирует и вставляет эту форму на свой вредоносный веб-сайт, скажем, b.com. Форма все еще будет работать. Пока пользователь входит в ваш Твиттер (т. Е. У него есть действующий файл cookie сеанса для a.com), запрос POST будет отправляться http://a.com/tweetи обрабатываться как обычно, когда пользователь нажимает кнопку отправки.

Пока что это не большая проблема, если пользователь знает, что именно делает форма, но что если наш плохой парень подправит форму следующим образом:

 <form action="https://example.com/tweet" method="POST">
   <input type="hidden" name="tweet" value="Buy great products at http://b.com/#iambad">
   <input type="submit" value="Click to win!">
 </form> 

Теперь, если один из ваших пользователей попадет на сайт злоумышленника и нажмет кнопку «Нажмите, чтобы выиграть!» Кнопка, форма отправляется на ваш сайт, пользователь правильно идентифицируется по идентификатору сеанса в куки и скрытый твит публикуется.

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

Форма может быть легко отправлена ​​отовсюду. Обычно это общая черта, но во многих случаях важно разрешить отправку формы только из домена, к которому она принадлежит.

Еще хуже, если ваше веб-приложение не различает запросы POST и GET (например, в PHP с использованием $ _REQUEST вместо $ _POST). Не делай этого! Запросы на изменение данных могут быть отправлены так же просто, как <img src="http://a.com/tweet?tweet=This+is+really+bad">встроенные в вредоносный веб-сайт или даже по электронной почте.

Как сделать так, чтобы форму можно было отправлять только с моего собственного сайта? Вот где появляется токен CSRF. Маркер CSRF - это случайная, трудно угадываемая строка. На странице с формой, которую вы хотите защитить, сервер сгенерирует случайную строку, токен CSRF, добавит ее в форму в качестве скрытого поля и также каким-то образом запомнит, либо сохранив ее в сеансе, либо установив cookie содержащий значение. Теперь форма будет выглядеть так:

    <form action="https://example.com/tweet" method="POST">
      <input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn">
      <input type="text" name="tweet">
      <input type="submit">
    </form> 

Когда пользователь отправляет форму, сервер просто должен сравнить значение отправленного поля csrf-token (имя не имеет значения) с токеном CSRF, запомненным сервером. Если обе строки равны, сервер может продолжить обработку формы. В противном случае сервер должен немедленно прекратить обработку формы и ответить ошибкой.

Почему это работает? Есть несколько причин, по которым злоумышленник из нашего примера не может получить токен CSRF:

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

Поскольку вредоносная страница злоумышленника загружается браузером вашего пользователя из другого домена (b.com вместо a.com), у злоумышленника нет шансов написать код JavaScript, который загружает контент и, следовательно, текущий токен CSRF нашего пользователя из Ваш сайт. Это связано с тем, что веб-браузеры по умолчанию не разрешают междоменные запросы AJAX.

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

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

Вам не нужно защищать запросы PUT и DELETE, потому что, как объяснено выше, стандартная HTML-форма не может быть отправлена ​​браузером с использованием этих методов.

С другой стороны, JavaScript действительно может делать другие типы запросов, например, используя функцию $ .ajax () jQuery, но помните, что для работы запросов AJAX домены должны совпадать (если вы не настроили явно свой веб-сервер) ,

Это означает, что часто вам даже не нужно добавлять токен CSRF в запросы AJAX, даже если они являются запросами POST, но вам нужно будет убедиться, что вы обходите проверку CSRF только в своем веб-приложении, если запрос POST на самом деле AJAX запрос. Вы можете сделать это, посмотрев на наличие заголовка типа X-Requested-With, который обычно включают в себя запросы AJAX. Вы также можете установить другой пользовательский заголовок и проверить его наличие на стороне сервера. Это безопасно, потому что браузер не будет добавлять пользовательские заголовки к обычной отправке формы HTML (см. Выше), поэтому у мистера Плохого парня нет шансов смоделировать это поведение с помощью формы.

Если вы сомневаетесь в запросах AJAX, потому что по какой-то причине вы не можете проверить заголовок, такой как X-Requested-With, просто передайте сгенерированный токен CSRF в свой JavaScript и добавьте токен в запрос AJAX. Есть несколько способов сделать это; либо добавьте его в полезную нагрузку, как обычная форма HTML, либо добавьте пользовательский заголовок в запрос AJAX. Пока ваш сервер знает, где искать его во входящем запросе и может сравнить его с исходным значением, которое он запоминает из сеанса или файла cookie, вы сортируетесь.

Дэн
источник
Спасибо за подробную информацию. Во время почтового запроса сайт должен отправить токен csrf на сервер, поэтому когда клиент отправит этот токен csrf на сервер? Это при выполнении запроса опций предполетной проверки? Прошу прощаться на этой части ..
Sm Srikanth
@Dan Почему b.com может получить доступ к файлам cookie другого сайта a.com?
Закира
8

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

gladysbixly
источник
7
Не полагайтесь на заголовок реферера, его легко подделать.
Каг
3
Это правильный ответ! Токен ДОЛЖЕН быть привязан к сеансу на сервере. Сравнение данных Cookie + Form, таких как ответы с наибольшим количеством голосов, совершенно неверно. Эти компоненты оба являются частью запроса, который создает клиент.
Ли Дэвис,
3
Вообще-то, нет. Токен ДОЛЖЕН быть привязан к каждому ЗАПРОСУ на Сервер. Если вы привязываете его только к сеансу, вы рискуете украсть токен сеанса и отправить запрос с этим токеном. Поэтому для максимальной безопасности токен должен быть привязан к каждому http-запросу.
chrisl08