Шпаргалки по безопасности: CSRF

    image

    Не смотря на то, что в последнем публиковавшемся перечне уязвимостей OWASP Top 10 2017 CSRF атаки отнесены к разряду “Удалены, но не забыты”, мы решили, что не будет лишним еще раз напомнить о том, как защититься от CSRF атак, опираясь на те же правила, предоставляемые OWASP.

    Использование CSRF токена

    Использование токена (как stateless, так и statefull методов) является первичным и самым популярным способом защиты. Токен должен быть уникален для каждой пользовательской сессии, сгенерирован криптографически стойким генератором псевдослучайных чисел. OWASP при этом рекомендует для шифрования использовать алгоритм AES256-GCM и SHA256/512 при использовании HMAC.

    Существует несколько подходов к работе с токенами: Synchronizer Token, Encryption based Token Pattern, HMAC Based Token

    Synchronizer Token

    При использовании подхода Synchronizer Token (statefull метод) подразумевается отправка токена при каждом запросе, подразумевающем какие-то изменения на стороне сервера. Если токен не является валидным, то сервер отклоняет запрос.
    При отправке запроса на сервер рекомендуется добавлять токен в параметры запроса, нежели в заголовок. Если все же вставляете токен в заголовок запроса, то убедитесь, что он не логируется на сервере. Полученный токен можно сохранять на стороне клиента в скрытом поле:

    <form action="/post.php" method="post">
    
    <input type="hidden" name="CSRFToken" value="l5824xNMAYFesBxing975yR8HPJlHZ">
    ...
    </form>


    в заголовках:

    POST /page HTTP/1.1
    Accept: application/json, application/xml, text/json, text/x-json, text/javascript,
    text/xml
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36
    Content-Type: application/json
    Host: example.com
    X-CSRF-TOKEN: l5824xNMAYFesBxing975yR8HPJlHZ


    или в куках

    POST /page HTTP/1.1
    Host: example.com
    Set-Cookie: CSRFToken=l5824xNMAYFesBxing975yR8HPJlHZ;
    Content-Type: application/x-www-form-urlencoded


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

    Также для повышения уровня безопасности предложенного метода, предлагается генерировать случайное имя параметра токена и/или сам токен для каждого запроса. При таком подходе время, в течение которого злоумышленник может воспользоваться украденным токеном, минимально. Однако это может привести к проблемам с юзабилити. Например, нажатие на кнопку “Назад” может привести к отправке на сервер невалидного токена, который содержался на предыдущей странице.

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

    Encryption based Token

    Данный подход является stateless, так как использует шифрование/дешифрование для валидации токена, а значит не требует хранения токена на стороне сервера.

    Сервер генерирует токен, состоящий из идентификатора сессии и timestamp (для предотвращения атаки повторного воспроизведения). Для шифрования рекомендуется использовать алгоритм шифрования AES256 в режиме блочного шифрования GSM/GSM-SIV. Использование режима ECB строго не рекомендуется. Зашифрованный сервером токен возвращается клиенту так же, как и в случае с «Synchronizer Token» в скрытом поле формы или же в заголовке/параметре ответа. При получении токена сервер должен расшифровать его, после чего сверить идентификатор сессии, а также сверить timestamp с текущим временем и убедиться, что оно не превышает установленного времени жизни токена.
    Если сверка идентификатора сессии проходит успешно, а timesmap – нет, то запрос может считаться валидным. Во всех остальных случаях рекомендуется отклонять запрос и регистрировать его, чтобы в дальнейшем понять как реагировать на подобные запросы.

    HMAC Based Token

    Также не требует хранения токена, принцип работы похож на Encryption based Token, за исключением того, что вместо шифрования токена используется HMAC (hash-based message authentication code) функция для генерации токена (рекомендуется использовать SHA256 или более сильный алгоритм). При этом токен представляет из себя результат HMAC функции от идентификатора сессии пользователя+ timestamp.

    Автоматизация работы с токенами

    Главная проблема в противодействии CSRF атакам заключается в том, что разработчики часто просто забывают добавлять функционал обеспечивающий работу с токенами. Чтобы избежать подобных проблем стоит автоматизировать данный процесс:

    • напишите обертку, автоматически добавляющую токен к запросам через тег form или при использовании ajax. Например, Spring Security использует подобный подход каждый раз, когда используется тег <form:form>

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

    • автоматически добавлять токен при рендере страницы. Данный подход используется CSRF Guard: токены добавляются ко всем href и src атрибутам, скрытым полям и во все формы

    Прежде чем пытаться написать собственную систему автоматической генерации токена, рекомендуется уточнить, имеет ли используемый вами фреймворк возможность по умолчанию обеспечить защиту от CSRF атак. Например, тот же фреймворк Django реализует защиту от CSRF.


    Login CSRF

    Воспользовавшись CSRF в форме входа, злоумышленник может войти в систему,
    под видом жертвы. С такой уязвимостью сталкивались такие гиганты как PayPal и Google.
    Бороться с CSRF в форме входа можно путем создания пре-сессий, которые создаются до того, как пользователь прошел аутентификацию, и включением токенов в форму входа.


    Samesite Cookie

    SameSite Cookie – атрибут, описанный в RFC6265bis, цель которого – противодействие CSRF атакам. Работает это следующим образом. Один из способов защиты – проверка заголовков origin и referer, по которым можно понять откуда пришел запрос, но такой подход требует внедрения механизма проверки. Используя атрибут SameSite, мы ограничиваем отправку куки с запросом с посторонних ресурсов. У данного атрибута есть несколько возможных значений: Strict, Lax и None.
    Использование значения strict подразумевает, что браузер не будет отправлять куки с любых источников, не совпадающих с доменным именем текущего ресурса.
    Значение lax дает возможность не блокировать куки с внешних ресурсов, переход с которых был осуществлен безопасным способом – по протоколу HTTPS. Lax обеспечивает баланс между удобством использования ресурса для пользователей и безопасностью.

    Выставить атрибут довольно просто:

    Set-Cookie: JSESSIONID=xxxxx; SameSite=Strict
    Set-Cookie: JSESSIONID=xxxxx; SameSite=Lax


    На момент написания статьи поддержка атрибута браузерами выглядит вот так:

    image


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

    Проверка заголовков

    Как уже упоминалось выше, один методов защиты – проверка значений referrer и origin заголовка запроса.
    Суть данной проверки сводится к проверке значений заголовков на стороне сервера. Если они совпадают с ресурсом, то запрос считается корректным, в противном случае отклоняется. Если заголовок Origin отсутствует, то нужно убедиться, что значение Referrer соответствует текущему ресурсу. OWASP рекомендует отклонять запросы, которые не содержат Origin или Referrer заголовки. Также можно логгировать все подобные запросы, чтобы после проанализировать их и принять решение о том как с ними поступать.

    Однако все усложняется, если ваше приложение находится за прокси-сервером, поскольку URL в заголовке при этом будет отличаться. В таком случае есть несколько вариантов:
    • сконфигурируйте ваше приложение таким образом, чтобы всегда знать происхождение запроса. Проблема подобного подхода заключается в установке правильного значения, если ваше приложение развернуто на нескольких окружениях (например, dev, QA, production), что приводит проблеме поддержки
    • используйте заголовок Host. Данный заголовок даст возможность определять источник запроса независимо от окружения
    • используйте заголовок X-Forwarded-Host, цель которого — хранение исходных заголовков, полученных прокси-сервером

    Все описанные методы работают только тогда, когда присутствуют заголовки origin и referer. Но встречаются случаи, когда данные заголовки отсутствуют. Вот ряд случаев, когда эти заголовки не включаются в запрос:
    • IE 11 не включается заголовок Origin для доверенных сайтов. Остается полагаться только на заголовок Referer
    • в случае перенаправления Origin не включен в запрос, так как считается, что он может содержать конфиденциальную информацию, которую не следует отправлять другому источнику
    • заголовок Origin включен для всех межсайтовых запросов, но большинство браузеров добавляют его только для POST/DELETE/PUT запросов

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

    Double Submit Cookie

    Данный подход довольно прост в реализации и не требует хранения токена на стороне сервера (stateless). Суть метода заключается в отправке пользователем токена в параметре запроса и в куках. Каждый запрос, требующий изменения состояния, сверяем значение токена в куках и в запросе. Если сверка идентификатора сессии проходит успешно, а timesmap – нет, то запрос может считаться валидным
    • +10
    • 5,8k
    • 2
    Акрибия
    44,75
    Компания
    Поделиться публикацией

    Комментарии 2

      0
      Encryption based Token

      Сервер генерирует токен, состоящий из идентификатора сессии и timestamp (для предотвращения атаки повторного воспроизведения)


      В данном случае еще была бы уместна соль, так как перехватив токен (как, например случайно переданный через GET), можно подобрать сам идентификатор сессии. Ведь это же хеш, да?
        0

        Если кратко, то суть в том, что можно сделать вредоносную страничку, при входе на которую будут сабмититься скрытые формы на полезный сайт, на котором предположительно авторизован юзер, например менять пароль/логин и отправлять их злоумышленнику. Да, удивительно, но такой запрос будет авторизован. В 2019 году когда все сабмитится через xmlhttprequest это можно сказать неактуально, так как с другого сайта ajax запрос будет неавторизован, а на бэке не-ajax запросы можно запретить для всех или определенных роутов типа if (!req.isXmlHttpRequest()) res.status(403).send()
        Даже если старый корпоративный сайт и формы повсюду, то просто на jQuery перехватываются все сабмиты и передаются через ajax. Имхо это проще, чем возня с CSRF токенами.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое