LINUX.ORG.RU

Объясните, пожалуйста, как работает CSRF_TOKEN

 


0

2

Я прочитал статью:
https://www.securitylab.ru/analytics/292473.php
(не помогло)

Я рассмотрел, что показывает Firefox по F12 для LOR.
И для всех кук я вижу «SameSite: none».
Мне кажется, что это неправильно, но ладно, проблема моя в другом.

Ещё читал разное:
2023-08-29, @Chord, Сайты и доступ к кукам
2019-07-29, @pihter, А получалось у кого wget-ом авторизоваться на ЛОРе?
2017-08-16, @pseudo-cat, Авторизация на каком-то сайте про линуксы
2017-04-16, @ne-vlezay, Авторизация на LOR через Wget

Защита при помощи куки CSRF_TOKEN по-моему может помочь проверить только то, что пользователь получал форму с сайта недавно. То есть, это нормально для защиты от смены пароля (его меняют редко), но не поможет во всех остальных случаях активной работы с сайтом.

Как установить куку напрямую
curl -b "name=value"
wget --header="Cookie: name=value"

Перемещено maxcom из linux-org-ru



Последнее исправление: Saakx (всего исправлений: 9)
Ответ на: комментарий от firkax

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

Saakx
() автор топика
Ответ на: комментарий от Saakx

Токен шлётся в post-запросе, то есть отправитель формы доказывает что он знает токен. Зачем он на лоре в куках не знаю, это к макскому вопрос (впрочем я не проверял есть ли он в куках). Кука и правда отправится вне зависимости от того кто отправил форму и ничем от сессии не отличается.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)

Смысл защиты в том, чтобы сторонний сайт не смог разместить работающую форму для l.o.r. Т.е. форму он разместит, но достать CSRF token не сможет, и по этому отправка формы приведет к ошибке.

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

достать CSRF token не сможет

Но ему и не надо. Потому что его задача просто выполниться внутри сессии пользователя. Он это может сделать, например через рекламный баннер. И после этого браузер пользователя сам вышлет все куки, и эту тоже.

Saakx
() автор топика
Ответ на: комментарий от firkax

Токен шлётся в post-запросе, то есть отправитель формы доказывает что он знает токен.

Я что-то сомневаюсь, что это можно сделать без JavaScript (вытащить значение куки, чтобы вставить в скрытое поле формы). Да и JavaScript тоже можно выполнить без участия пользователя (в том числе и форму засабмитить, и заполнить тоже).

Saakx
() автор топика
Последнее исправление: Saakx (всего исправлений: 1)
Ответ на: комментарий от Saakx

Кука вообще не нужна, сайт когда отдаёт тебе html защищённой формы уже должен туда вставить <input type=hidden name=csrf value=...>. Хотя может быть и могут быть причины заполнять это поле уже на стороне браузерного js.

Да и JavaScript тоже можно выполнить без участия пользователя (в том числе и форму засабмитить, и заполнить тоже).

Выполнение js на другом сайте никак не поможет вставить csrf-токен от лора в форму. А выполнять js в контексте лора у взломщика не получится (если только ты сам ссылку javascript:... не откроешь или в js-консоль вредоносный код не вставишь).

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от Saakx

Кросс запросы запрещены в браузерах, браузер пользователя не сможет сделать запрос/отправку формы на linux.org.ru со страницы на example.com в которую добавили форму логина на linux.org.ru, а значит linux.org.ru не сможет вернуть сессию пользователя.

Плюс, нельзя просто взять и отправить сразу форму логина, нужно как минимум 2 соединения, первое получение формы, второе отправка формы, отваливается тупой брутфорс, и особенно глупый который в холостую шлёт запросы даже правильного логина но без CSRF. И прочие мелкие эффекты отмахивания назойливых мошек путём распыления репиллента.

Это конечно всё бесполезно, если форма на example.com отправляет данные на свой сервер, а тот уже имея данные для входа делает логин на стороне сервера. Но это и не для этого, для запрета этого другие механизмы. А CSRF_TOKEN это просто закрытие совсем уж очевидных лазеек для приколистов. Как лежачий полицейский, не мешает проехать в целом, но мешает ехать определённым образом.

https://developer.mozilla.org/ru/docs/Web/HTTP/Guides/CORS

(это если я правильно понимаю, могут быть нюансы)

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 3)
Ответ на: комментарий от Saakx

Если на сайте напрямую выполняется сторонний js-код (script src=сторонний_домен) то ему никакие защиты не помогут, и конкретно CSRF тут ни при чём.

Но обычно, если баннеры и содержат js, они сажаются а iframe-песочницы и доступа к контексту сайта не имеют. На лоре баннеры кажется просто картинки со ссылками, хотя я не изучал.

она высылается куда попало по http-протоколу

Не куда попало а только на linux.org.ru. Вопросы потенциального отсутствия шифрования и снифферов трафика по дороге - отдельная тема.

firkax ★★★★★
()
Ответ на: комментарий от Saakx

Но ему и не надо.

Надо, потому что CSRF токен еще параметром формы передается. Вот чтобы он туда попал нужно прочитать cookie, а это браузер не даст сделать.

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

CSRF токен еще параметром формы передается

С сервера. То есть, джаваскриптом читать не надо.

<form id="commentForm" action="/add_comment.jsp" method="POST" data-preview-tabs-initialized="true">
  <input type="hidden" name="csrf" value="Tg.....==">
  <input type="hidden" name="topic" value="18309049">
    <input type="hidden" name="replyto" value="18309099">
  <div class="warning-block" id="author-readonly-note"></div>
  <div class="control-group" data-format-mode="markdown">
    <div class="markup-tabs">
      <ul class="markup-tabs__nav">
        <li class="markup-tabs__tab active" data-tab="editor">Markdown</li>
      <li class="markup-tabs__tab" data-tab="preview">Предпросмотр</li></ul>
      <div class="markup-tabs__content">
        <div class="markup-tabs__panel active" data-panel="editor">
              <textarea id="msg" name="msg" required="true"></textarea>
        </div>
      <div class="markup-tabs__panel" data-panel="preview"><div class="markup-preview"></div></div></div>
    </div>
    <div class="help-block">Пустая строка (два раза Enter) начинает новый абзац.
                 Знак '&gt;' в начале абзаца выделяет абзац курсивом цитирования.<br>
<b>Внимание:</b> прочитайте описание разметки <a target="_blank" href="/help/markdown.md">Markdown</a>.
    </div>
  </div>
  <div class="help-block">
  </div>
  <div class="form-actions">
  <button type="submit" class="btn btn-primary">Поместить</button>
  <button type="submit" name="preview" class="btn btn-default preview-button-js-hidden">Предпросмотр</button>
        <button type="reset" name="cancel" id="cancelButton" class="btn btn-default">Отменить</button>
    <div class="help-block">Используйте Ctrl-Enter для размещения комментария</div>
  </div>
</form>

Меня смущает то, что:

  1. HTML получается динамический (это плохо для масштабирования);
  2. информация в одном HttpResponse отправляется два раза:
  • в HTTP-заголовке, чтобы установить куку;
  • и ЭТА ЖЕ информация передаётся в тексте формы.
    То есть, что-то одно из этих двух - лишнее.

Если на сайте напрямую выполняется сторонний js-код (script src=сторонний_домен) то ему никакие защиты не помогут

Я не вижу смысла передавать параметр внутри формы с клиента на сервер. Единственное что такая проверка проверяет - это то, что была ли эта форма запрошена ранее в этой сессии. И всё.
Длинные сессии длятся месяцами. А если ориентироваться на короткую сессию, то форма быстро и непредсказуемо может проэкспайриться.

Можно извернуться и проверять, что форма выдавалась в течении N минут до отправки (так же, как токен для сессии, только для конкретной формы). Но смысла не вижу. Потому что частоиспользуемые формы часто выдаются.

Saakx
() автор топика
Ответ на: комментарий от Saakx

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

Эта проверка проверяет, что форма действительно сгенерирована настоящим сервером (знающим правильный токен), а не каким-то сторонним скриптом. И именно это её цель.

Правильный сценарий отправки формы: юзер открывает в браузере страницу с ней и заполненным токеном, вписывает в форму свои тексты и шлёт её. Сервер проверяет токен и считает форму авторизованной.

Сценарий взломщика: ты заходишь на сайт https://hacks.co.cc/hack-lor/, который тебе отдаёт форму с action="https://www.linux.org.ru/...." с злонамеренным содержимым (например, создание темы со спамом, или форма самозабана) и джаваскриптом нажимает в ней кнопку отправки. Но, поскольку сервер взломщика не знает твой csrf-токен, он не сможет его правильно заполнить и лор, увидев post-запрос без правильного токена, отвергнет его.

Вообще, теоретически, примерно то же самое можно сделать простой проверкой http referrer-а, но проверку рефереров принято считать недостаточной, так как шансы того, что кто-то найдёт способ отправить запрос с поддельным реферером (который он знает какой должен быть т.к. он не меняется), или со спрятанным реферером, выше чем то, что кто-то найдёт способ украсть токен и отправить форму с ним. А ещё в некоторых браузерах рефереры отключены.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)

CSRF-токен привязан к конкретной сессии в браузере и эта информация (при идеальной безопасности) никогда не просачивается за пределы этой сессии.

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

kaldeon ★★
()
Ответ на: комментарий от Saakx

Я не вижу смысла передавать параметр внутри формы с клиента на сервер.

Это про тому, что ты не понимаешь что такое CSRF. Задача проверки убедиться, что форма была сформирована тем же сервером, что ее получил. А не кем-то еще.

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

Если контент статический, форма неизменна на все времена. Кто-бы её не сформировал, она будет именно такая. Кроме того, обратно это форма всё равно не передаётся, только данные от неё. Проверка состава и порядка полей - это другое.

CSRF-токен привязан к конкретной сессии

Для того, чтобы проверить сессию, достаточно одной сессионной куки. Не надо две (JSESSIONID и CSRF_TOKEN).

Saakx
() автор топика
Последнее исправление: Saakx (всего исправлений: 1)
Ответ на: комментарий от Saakx

Если контент статический, форма неизменна на все времена. Кто-бы её не сформировал, она будет именно такая.

Не будет, токен у всех свой.

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

Не будет

это печально. Страдает кеширование.

токен у всех свой

а это неважно, потому что кука CSRF_TOKEN точно так же у всех своя. Достаточно при приёме формы проверить ЕЁ, вместо проверки поля. Но точно так же можно было бы проверить идентификатор сессии, и посмотреть в данных сессии, была ли выдана эта форма ранее (и при желании - посмотреть как давно).

Таким образом, можно иметь и неизменную форму, и отсутствие лишней куки, и ту же степень безопасности.

Saakx
() автор топика
Ответ на: комментарий от Zhbert

раз это сделано и много где используется, значит смысл таки какой-то есть?

Может это легаси. Раньше смысл был, а теперь его нет.
Но против этой гипотезы тот факт, что и раньше смысла не было.

Saakx
() автор топика
Ответ на: комментарий от Saakx

Для того, чтобы проверить сессию, достаточно одной сессионной куки. Не надо две (JSESSIONID и CSRF_TOKEN).

Сессионная кука никак не поможет. Например, ты открываешь ссылку «удалить профиль» из почты от злоумышленника. Сессионная кука позволяет серверу аутентифицировать твоего пользователя и твой профиль успешно удаляется. (От этой проблемы можно защититься через SameSite=Strict или SameSite=Lax, в качестве альтернативного, но менее надёжного решения.)

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

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

kaldeon ★★
()
Последнее исправление: kaldeon (всего исправлений: 1)
Ответ на: комментарий от Saakx

Таким образом, можно иметь и неизменную форму, и отсутствие лишней куки, и ту же степень безопасности.

Защита работает всегда, независимо от того была форма недавно получена или нет. Статической формой этого нельзя решить.

maxcom ★★★★★
()
Ответ на: комментарий от kaldeon

Сессионная кука никак не поможет.

Ты прочитал идею не целиком. Там было не одно условие, которое надо проверять а два:

  • сессия есть
  • в этой сессии генерировалась эта форма

поэтому все дальнейшие рассуждения твои неправильные.

А у меня одни плюсы:

  • Нет hidden field — форма статическая, кешируется
  • Нет состояния на сервере — легко масштабировать
  • Безопасность — злоумышленник не может сгенерировать состояние форм (потому что у него нет ключа шифрования)
Saakx
() автор топика
Последнее исправление: Saakx (всего исправлений: 1)
Ответ на: комментарий от maxcom

Статической формой этого нельзя решить.

Но я решаю не просто формой, а комбинацией формы и состояния сессии.

Защита работает всегда, независимо от того была форма недавно получена или нет.

Вы не можете объяснить суть защиты.
Что именно в защите защищает?

Если проэкспайрится кука - защита не сработает. Значит защищает не случайное сгенерированное число.

Saakx
() автор топика
Ответ на: комментарий от Saakx

Но я решаю не просто формой, а комбинацией формы и состояния сессии.

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

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

«кука совпадает со значением поля формы» Я утверждаю, что эта проверка эквивалентна условию «форма была сгенерирована ранее в этой сессии».
Неважно, какое случайное число, неважно кто и когда его заполнил в форме (сервер заранее, или клиент из куки).
И кука могла бы быть на клиенте сгенерирована, вместе с полем формы.

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

Saakx
() автор топика
Последнее исправление: Saakx (всего исправлений: 1)
Ответ на: комментарий от Saakx

По поводу использования одного значения: технически это возможно сделать. Но тогда нам нужно будет его передавать в заголовке X-CSRF-Token. По сути, это всё тот же CSRF-токен, концептуально.

По поводу «плюсов»: всё то же самое реализуемо при использовании двух значений.

Я бы даже сказал, вариант с двумя токенами более безопасен, ибо работает принцип наименьшей привилегии: уж лучше пусть злоумышленник украдёт CSRF-токен, а не сессионную куку, которую мы можем защитить дополнительно через HttpOnly (HttpOnly недоступен на уровне JS, который необходим для X-CSRF-Token).

kaldeon ★★
()
Последнее исправление: kaldeon (всего исправлений: 1)
Ответ на: комментарий от Saakx

Я утверждаю, что эта проверка эквивалентна условию «форма была сгенерирована ранее в этой сессии».

Это не так.

maxcom ★★★★★
()

Статической формой этого нельзя решить.

Мы пытаемся различить два случая:

  • форма сгенерирована кодом, который имеет доступ к данным из кук
  • форма сгенерирована кодом, который не имеет доступа к данным из кук

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

(это мне на память, на тот случай, если я забуду, в чём было дело)

Saakx
() автор топика
Последнее исправление: Saakx (всего исправлений: 2)
Ответ на: комментарий от maxcom

ты не понимаешь что такое CSRF

А вот это почему/зачем?

        <csrf disabled="true"/>

https://github.com/maxcom/lorsource/blob/master/src/main/webapp/WEB-INF/springapp-security.xml#L51

Ну я к тому, что мы выяснили, что CSRF совершенно стандартный.
Почему бы в таком случае не использовать реализацию по-умолчанию?

Saakx
() автор топика
Последнее исправление: Saakx (всего исправлений: 1)
Ответ на: комментарий от maxcom

Зачем его сравнивать с кукой вместо того чтобы сравнивать с сохранённым на стороне сервера в сессии?

Хотя разница тут видимо не сильно большая, но кукой всё-таки чуть больше возможностей манипулировать или незаметно украсть её. То есть преимуществ она не даёт, а опасностей чуточку добавляет.

firkax ★★★★★
()
Ответ на: комментарий от Saakx

Почему бы в таком случае не использовать реализацию по-умолчанию?

Это потому что я не настоящий Java разработчик и Spring плохо знаю. Как-нибудь разберусь, возможно.

maxcom ★★★★★
()

CSRF токен нельзя класть в куки, его надо вшивать прямо в сами формы в виде hidden полей. Это как временный одноразовый дополнительный пароль для каждой конкретной формы, привязанный к существующей сессии в куке. Никто со стороны без твоего ведома не засабмитит от твоего лица форму, даже если твой браузер уже залогинен на важном сайте (имеется кука сессии).

SameSite делает то же самое, но средствами браузера. Браузер вставляет куку в запрос в зависимости от того как этот запрос инициирован. Но нужно чтобы браузер это умел.

Всё что делается через wget и прочее. Это не случай CSRF. CSRF делается когда ты пользуешься обычным браузером и сторонний сайт пытается использовать твои уже залогиненые сессии на других сайтах через формы, фреймы и аяксы.

neumond ★★
()

Фишка csrf токена в том, что сторонний JS не может его прочитать. Так как браузер не отдаёт тело ответа в скрипт, если это кроссдоменный вызов и нету CORS заголовка.

Так что не важно недавно форма была открыта или давно. Сторонний скрипт не сможет ни пароль сменить, ни сообщение оставить от имени пользователя.

KivApple ★★★★★
()
Ответ на: комментарий от Saakx

Есть два варианта.

Ты можешь встроить оригинальную форму в свой сайт видимым или скрытым iframe. Но ты не сможешь её программно отправить через метод submit, потому что фреймы на разных доменах не имеют доступа к DOM друг друга.

Юзер сам должен будет заполнить и отправить форму. Это более сложно с точки зрения социальной инженерии, а ещё форма может отказаться грузиться в iframe.

Либо ты можешь сделать свою собственную форму и указать ей action такой же как у ЛОР. Такую форму получится отправить программно, но ты не знаешь какой csrf токен в неё положить и ЛОР не отработает запрос несмотря на наличие куки авторизации.

А без токена можно было бы сделать на своей странице форму с нужным action и засабмиттить её без ведома юзера. Да, ты бы не узнал результат сабмита (браузер его не даст тебе из-за cors), но действие было бы сделано.

У csrf есть альтернатива - хранить авторизацию не в куках, а в local storage и добавлять программно к исходящим запросам. Но это имеет свои минусы: выше поверхность атаки при XSS (можно не только выполнить действие, но и увести токен для работы офлайн) и не работает со статическими страницами (подходит только для SPA или если всё статическое можно прочитать без авторизации).

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 3)

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

vel ★★★★★
()

Смотри. У нас есть сайт fakelor. На нём javascript-ом выполняют код, который посылает HTTP POST запрос на сайт лора. Браузер так работает, что посылает с этим запросом твои куки. Т.е. на сайт лора приходит запрос с твоими куками, который, например, создаёт тему с рекламой. И ты про это вообще не в курсе.

Чтобы такого не происходило, придумали csrf токен. Когда ты открываешь реальный лор, в теге будет скрытый input с каким-то значением (токеном). Этот токен генерируется на сервере и сервер знает, что он сгенерировал. Когда ты с реального лора посылаешь форму на сервер, токен будет в запросе и сервер проверит его. А если другой сайт попробует подобное сделать, у него ничего не получится. Он не сможет вытащить валидный токен с чужого сайта. Ты можешь посылать на чужой сайт POST запрос с формой. Но послать на чужой сайт GET запрос и пропарсить ответ ты не можешь (точней можешь, но только если чужой сайт будет кооперироваться, лор не будет).

Это древнее решение древней проблемы.

Сегодня есть решение проще. На авторизационную куку ты можешь поставить атрибут SameSite=Strict. В этом случае браузер не будет её отправлять при кросс-сайтовых запросах. Т.е. если другой сайт попробует отправить javascript-ом форму, то браузер просто не добавит туда эту куку и запрос будет будто от анонима. И всё, никаких токенов не нужно. Очень удобно. Но если кто-то 20 лет назад реализовал защиту на csrf токенах, она и сегодня будет прекрасно работать. Ну и SameSite всё же требует браузера, выпущенного хотя бы в последние 5 лет, а CSRF работает везде. С учётом специфической аудитории лора, может быть CSRF и лучше.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 2)
Ответ на: комментарий от vel

Это уже вопрос UI сайта. Никто никому ничего не должен. К примеру я отредактировал сообщение. Потом нажал «назад» и отредактировал ещё раз. Что в этом плохого? Ничего. А пытаться мне мешать это делать потому, что какой-то там токен помечен как использованный, это плохой UI. Лор, кстати, не мешает.

С другой стороны если я нажал на кнопку «поместить» при создании нового сообщения два раза - да, тут полезно второй запрос отклонить. Скорей всего это ошибка. Но вообще всё это к csrf не имеет отношения, это ортогональная фича, если можно так выразиться.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 3)
Ответ на: комментарий от vbr

А сама проблема из-за того, что веб был исторически спроектирован в сторону слишком простого использования, а не в сторону безопасности.

К примеру правильней было бы вообще запретить сайту делать любые HTTP запросы на другие домены кроме исходного. Хочешь картинку с другого сервера показать? Делай на своём сайте прокси, или ещё как-нибудь реализовывай, но в HTML это работать не будет. Хочешь отправлять форму на другой домен джаваскриптом? Да ты рехнулся, нельзя, вообще никак, без вариантов. Но - изначально сделали, что можно. И теперь пытаемся изолентой все эти дыры закрыть. Куча HTTP-заголовков, атрибутов и прочего, чтобы отрезать все ненужные возможности у сайта.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария