image


Пер. Под катом вас ждет перевод смешноватой и несложной статьи о CSRF и новомодном способе защиты от него.


Древний змий


Уязвимость CSRF или XSRF (это синонимы), кажется, существовала всегда. Её корнем является всем известная возможность делать запрос от одного сайта к другому. Допустим, я создам такую форму на своем сайте.


<form action="https://bankingsite.com/transfer" method="POST" id="stealMoney">  
<input type="hidden" name="to" value="John Doe">  
<input type="hidden" name="account" value="12416234">  
<input type="hidden" name="amount" value="$1,000">  

Ваш браузер загрузит мой сайт и, соответственно, мою форму. Её я могу незамедлительно отправить, используя простой javascript.


document.getElementById("stealMoney").submit(); 

Поэтому подобная атака буквально расшифровывается, как межсайтовая подделка запроса. Я подделываю запрос, который отправляется между моим сайтом и вашим банком. В действительности, проблема состоит не в том, что я отправлю запрос, а в том, что ваш браузер отправит вместе с запросом и ваши куки. Это означает, что запрос будет обладать всеми вашими правами, так что, если вы залогинены в текущий момент на сайте вашего банка, то только что вы пожертвовали мне тысячу долларов. Данке шон! Если же вы не были залогинены, то деньги всё еще на месте. Существуют несколько способов защиты от подобных злостных посягательств.


Способы защиты от CSRF


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


Проверка источника


Принимая запрос в нашем приложении, потенциально мы можем узнать о том, откуда он пришел, посмотрев на два заголовка. Они называются origin и referer. Так что мы можем проверить один или оба значения, чтобы узнать, пришёл ли запрос с нашего приложения или откуда-то ещё. Если источник запроса не ваш сайт, то можно просто на него ответить ошибкой. Проверка этих заголовков может нас защитить, но проблема в том, что они не всегда могут присутствовать.


accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8  
accept-encoding: gzip, deflate, br  
cache-control: max-age=0  
content-length: 166  
content-type: application/x-www-form-urlencoded  
dnt: 1  
origin: https://example.com  
referer: https://example.com /login  
upgrade-insecure-requests: 1  
user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36

Защитные токены


Существуют два способа использования уникальных защитных токенов, но принцип их использования един. Когда пользователь посещает страницу, скажем, страницу банка, то в форму перечисления денег вставляется скрытое поле с уникальным токеном. Если пользователь действительно находится на сайте банка, то вместе с запросом он отправит этот токен, так что можно будет проверить, совпадает ли он с тем, что вы вставили в форму. При попытке CSRF атаки атакующий никогда не сможет получить это значение. Даже в случае запроса к странице Same Origin Policy (SOP) не позволит ему прочитать страницу, где есть данный токен. Такой метод хорошо себя зарекомендовал, однако он требует от приложения логики по внедрению токенов в формы и проверки их подлинности при входящих запросах. Еще один похожий метод заключается во внедрении такого же токена в форму и передачи куки, содержащего то же значение. Когда настоящий пользователь отправляет форму, то происходит сверка значения токена в куки со значением в форме. Если они не совпадают, то такой запрос не будет принят сервером.


<form action="https://report-uri.io/login/auth" method="POST">  
    <input type="hidden" name="csrf_token" value="d82c90fc4a14b01224gde6ddebc23bf0">
    <input type="email" id="email" name="email">
    <input type="password" id="password" name="password">
    <button type="submit" class="btn btn-primary">Login</button>
</form> 

Так в чем проблема?


Вышеописанные методы защиты давали нам достаточно надежную защиту против CSRF на протяжении довольно долгого времени. Конечно, проверка заголовков origin и referer не на 100% надежна, так что большинство сайтов полагается на тактику с уникальными токенами. Сложность состоит в том, что оба метода защиты требуют от сайта внедрения и поддержки решения. Вы скажете, что это совсем несложно. Согласен! Никогда не возникало проблем. Но это некрасиво. По сути мы обороняемся от поведения браузера хитрым способом. А можем ли мы просто сказать браузеру перестать делать вещи, которые мы не хотим, чтобы он делал?.. Теперь можем!



По сути Same-Site куки могут свести на нет любую CSRF атаку. Насмерть. Чуть более, чем полностью. Адьёс, CSRF! Они действенно и быстро помогают решить проблему безопасности. К тому же применить их чрезвычайно просто. Возьмем для примера какую-то куку.


Set-Cookie: sess=smth123; path=/ 

А теперь просто добавьте атрибут SameSite


Set-Cookie: sess=smth123; path=/; SameSite

Всё, вы закончили. Нет, правда! Используя данный атрибут, вы как-бы говорите браузеру предоставить куки определенную защиту. Существую два режима такой защиты: Strict или Lax, в зависимости от того, насколько серьезно вы настроены. Атрибут Same-Site без указания режима будет работать в стандартном варианте т.е. Strict. Вы можете выставить режим так:


SameSite=Strict  
SameSite=Lax 

Strict


Такой режим предпочтительней и безопасней, но может не подойти для вашего приложения. Этот режим означает, что c ваше приложение не будет отправлять куки ни на один запрос с другого ресурса. Само собой в таком случае CSRF не будет возможен в корне. Однако здесь можно столкнуться с проблемой, что куки не будут пересылаться также при навигации высокого уровня (т.е. даже при переходе по ссылке). Например, если бы я сейчас разместил ссылку на Вконтакте, а Фейсбук использовал куки Вконтакте, то при переходе по ссылке вы бы оказались разлогинены, вне зависимости от того, были ли вы залогинены до этого. Такое поведение, конечно, может не порадовать пользователя, но можно быть уверенным в безопасности.


Что можно сделать с этим? Можно поступить, как Амазон. У них реализована как-бы двойная аутентификация с помощью двух куки. Первый куки позволяет амазону просто знать, кто вы и показывать вам ваше имя. Для него не используется SameSite. Второй куки позволяет делать покупки, менять что-то в аккаунте. Для него резонно используется SameSite. Такое решение позволяет одновременно предоставлять пользователям удобство и оставаться приложению безопасным.


Lax


Режим Lax решает проблемы с разлогированием описанную выше, но при этом сохраняет хороший уровень защиты. В сущности он добавляет исключение, когда куки передаются при навигации высокого уровня, которая использует “безопасные” HTTP методы. Согласно RFC безопасными методами считаются GET, HEAD, OPTIONS и TRACE.


Вернемся к нашему примеру атаки в начале.


<form action="https://bankingsite.com/transfer" method="POST" id="stealMoney">  
<input type="hidden" name="to" value="John Doe">  
<input type="hidden" name="account" value="12416234">  
<input type="hidden" name="amount" value="$1,000">  

Такая атака уже не сработает в режиме Lax, так как мы обезопасили себя от POST-запросов. Конечно, злоумышленник может использовать метод GET.


<form action="https://bankingsite.com/transfer" method="GET" id="stealMoney">  
<input type="hidden" name="to" value="John Doe">  
<input type="hidden" name="account" value="12416234">  
<input type="hidden" name="amount" value="$1,000">

Поэтому режим Lax можно назвать компромиссом между безопасностью и удобством пользователей.
SameSite куки защищают нас от CSRF атак, но нельзя забывать про другие виды уязвимостей. К примеру XSS или браузерные атаки по времени.


От автора перевода: К сожалению, пока SameSite куки поддерживают только Chrome и Opera, а также браузер для андроида. Пруф


Оригинал
До этого появлялась здесь

Комментарии (27)


  1. dartraiden
    04.08.2017 03:41

    Оригинал: https://www.kuoll.com/the-end-of-csrf/
    Судя по всему, это урезанная версия этой статьи.


    1. Kirylka Автор
      04.08.2017 04:04

      Судя по всему так и есть. Находил до этого только http://www.sjoerdlangkemper.nl/2016/04/14/preventing-csrf-with-samesite-cookie-attribute/


  1. dartraiden
    04.08.2017 03:49

    Пишут, что внедрение поддержки SameSite неактуально, поскольку Google предлагает на замену ей Cookie Prefixes, поддержка которых уже реализована в Crome 49+ и Firefox 50+.


    1. Kirylka Автор
      04.08.2017 04:07
      +2

      На удивление мало информации о Cookie Prefixes в интернетах. Надо будет проработать и, если разберусь досконально, напишу здесь. P.S. Добавил ссылку на оригинал оригинала:)


    1. neolink
      04.08.2017 09:19
      +3

      судя по тому, что расположено по вашей ссылке это ограничения на установку куки. т.е. грубо говоря защита от того, что кто-то на поддомене инжектнет в браузер куку на основной домен. https://tools.ietf.org/html/draft-west-cookie-prefixes-05 в Introduction об этом написано, как бы это не пересекающийся кейс с csrf


    1. pansa
      04.08.2017 12:04

      Это было бы странно, учитывая, что гугл — один из авторов драфта на фичу на ietf…


  1. domix32
    04.08.2017 10:33

    Так вот почему bundlestar так по-дурацки работал — вполне возможно, что они ставили этот strict и после покупки автоматически разлогинивали при возврате в магазин.


    1. Kirylka Автор
      04.08.2017 10:44

      Не знаю конкретно, что там было, но я посмотрел хедеры их HTTP-ответа и очень заметно, что они обеспокоены безопасностью :D


  1. kakutuzov
    04.08.2017 13:42

    Проблема в том что защита от CSRF давно уже реализована в браузерах через хедер Access-Control-Allow-Origin.
    Остаточная проблема в scrf это устаревшие браузеры, которые не поддерживают этот хедер. Новый флаг для кукисов ничем не поможет, т.к. в старых браузерах его и не появится.


    1. AKiNO
      04.08.2017 14:30
      +5

      Не путайте CSRF и CORS, это немного разные атаки. Первый про формы, второй про ajax-запросы.


  1. Mendel
    04.08.2017 14:22
    -1

    Я думаю это главный претендент в номинации «самая бесполезная фича в браузерах».
    Данная идея только понижает общий уровень безопасности.
    Старые браузеры сразу становятся уязвимы.
    Плюс защита от XSRF (классическая, с токенами) неплохо защищала еще и от XSS, значительно снижая возможный ущерб, а допустить XSS проще чем XSRF ибо больше точек отказа. Т.е. и здесь ухудшение безопасности.
    Ради чего? «Ну это… так красивее».


    1. Kirylka Автор
      04.08.2017 14:33

      Для старых браузеров придется поддерживать токены, к примеру. Как всегда с новыми фичами. Не совсем понял, как CSRF-токены спасали от XSS.


      1. Mendel
        06.08.2017 15:06

        И что мы выиграем если будем все равно использовать токены?
        В чем профит? Есть кейс где описанные в статье заголовки защитят, а токены бы не справились? Нет.
        Токены ставить надо? Да. Иначе проблемы со старыми браузерами. А старые браузеры будут вечно. ИЕ6 еще изредка встречается, а тут такое.
        В чем профит кроме «о! круто! новая фича!»?


        1. neolink
          08.08.2017 12:07

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


          1. Mendel
            10.08.2017 16:32

            Ну да, я где-то ниже писал, что если у вас много легаси и сделать по нормальному нельзя, то оно уместно, но… как можно ошибиться если контроллер данные из форм берет через отдельный метод/сервис/функцию, которая по умолчанию требует токена? Специально отключить проверку? Отстрелить ногу можно более изящно.
            А если проверка пишется ДЛЯ КАЖДОЙ формы то это говнокод. WET должен стать DRY.
            И костыли тут не спасут. Кроме случая когда легаси, и надо «пусть не безопасно, но хоть как-то» или «исправлять мы не будем, вроде и так работает, но если вдруг где-то пропустили, то будет страховочка».


    1. arkamax
      04.08.2017 17:54

      Один из вариантов XSS — это когда у вас на странице оказывается чужой JavaScript код (еще раз, напрямую на странице), и как следствие, получает прямой доступ к контексту страницы и DOM, вместе со всеми CSRF-токенами, и возможности их отправлять куда угодно, включая ваши endpoints (CORS, кстати, при этом тоже радостно все пропустит — потому что скрипт локальный и для браузера ничем не отличается от ваших собственных). Именно поэтому токенная защита от CSRF не имеет смысла при возможности проведения XSS (это, кстати, цитата в переводе с одной из презентаций на прошлонедельном DEFCON). Если я что-то не понял в вашем посте, прошу прокомментировать.


      1. Mendel
        06.08.2017 15:02

        Да, конечно, токены не панацея от XSS.
        Я говорю лишь о том, что есть кейсы в которых токены бы спасли, но их нет…
        Например уязвим блог на том же домене что и атакуемая админка. Админка не уязвима.
        Вы внедряете скрипт в блог, там-же форма атаки, и всё, привет.
        Конечно XSS лечится не токенами, но токены часто могут помешать развить уязвимость и минимизировать ущерб. Но мы собственно не об этом.

        Мой комментарий был о том, что данная фича не приносит ничего полезного, а только ухудшает ситуацию по сравнению с текущей.
        Если мы используем оба способа одновременно — и токены и описанные заголовки, то это лишние сущности. Больше безопасности не будет, зато нам нужно сделать лишние телодвижения для этих заголовков.
        Если же мы не используем токены, то… то безопасность ухудшается.
        1) страдают старые браузеры
        2) в некоторых случаях другие уязвимости могут привести к большему ущербу чем привели бы с токнами
        Выигрыш? Выигрыш в том что «красивее».

        Единственный случай когда это может несколько повысить безопасность — если у вас очень много легаси-кода который очень, очень WET (Write Everything Twice), токены обрабатываются не в едином месте а прописаны руками в сотнях и сотнях форм (причем как правило такие приложения идут с шаблонами и бизнеслогикой в перемешку), «высушить» и вообще очеловечить код не представляется возможным, но хочется увеличить безопасность на случай если где-то в этой мешанине кто-то пропустил токены, и окажется уязвимость. Но это довольно специфичный и редкий случай, и тулить его везде — контрпродуктивно.


  1. haiflive
    04.08.2017 14:33
    -3

    что бы использовать подобную защиту надо быть уверенным, что на вашем сайте нет XSS, уж лучше токены


    1. Prototik
      04.08.2017 18:27
      +1

      При наличии XSS как-бэ вообще уже фиолетово — не спасёт ничего…


      1. pansa
        04.08.2017 20:50

        Спасет CSP. Только его нормально настраивают единицы.


      1. haiflive
        05.08.2017 21:36
        +1

        да, пожалуй не поможет, побежал искать сканер XSS…


  1. hamilyon2
    04.08.2017 18:01

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


    1. Akuma
      05.08.2017 07:35

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

      Самый лучший метод, который я встречал — запутывание и шифрование. Например, комментарии на YouTube. По быстрому я так и не смог разобраться, как их отправить «самому» и просто эмулировал клик по кнопке, который кстати тоже обычным .click() не заработает :)


      1. Rusted
        07.08.2017 19:07

        Чистые Anti-CSRF токены не предназначены для защиты от ботов которые знают актуальные имя/пароль пользователя или имеют аутентификационный токен/куку, т.е. в случае CSRF атаки сам сценарий атаки немного другой. А вот разработчики youtube скорее всего делали свою защиту с учетом как раз этого требования.


  1. Asikk
    05.08.2017 13:29

    Когда-то давно делал защиту следующим образом:


    1. При логине генерирования токен, который сохранялся в сессии
    2. Пользователь редиректилкся на страницу https://y.site/TOKEN/main
    3. Все следующие запросы были по относительным линка


    1. sumanai
      05.08.2017 15:30

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


      1. Asikk
        05.08.2017 20:59

        Абсолютно верно. Использовал в интернет банкинг одного из банков. Из минусов — не френдли урл