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