Cookie, сеанс, токен, JWT, атаки, где хранить токен, проблемы безопасности? Все, что вам нужно знать, здесь.

Обновленная версия (сентябрь 2019 г.) доступна здесь.

TL;DR;

Аутентификация в одностраничном приложении (SPA) включает несколько шаблонов с плюсами и минусами. В этой статье будут перечислены основные важные концепции, которые необходимо знать и иметь в виду при работе с аутентификацией пользователей, особенно в этой общей архитектуре:

Предпосылки безопасности

Зашифрованная связь (httpS)

  • Поскольку аутентификация использует заголовки HTTP и обменивается высокочувствительными данными (пароль, токен доступа и т. Д.), Связь должна быть зашифрована, иначе кто-то, перехватывающий сеть, может их перехватить.

Не используйте параметры запроса URL для обмена конфиденциальными данными

  • Параметры URL-адреса и URL-запроса могут попадать в журнал сервера, журналы браузера, историю браузера: кто-то может получить данные оттуда и попытаться использовать их повторно.
  • Не обученные пользователи могут копировать и вставлять URL-адрес с токенами аутентификации, что может привести в основном к непреднамеренному захвату сеанса.
  • Вы можете столкнуться с ограничениями размера URL в браузерах или на серверах.

Предотвратить атаки методом перебора

  • Злоумышленник может попытаться вывести пароль, токен или имя пользователя, попробовав множество возможностей.
  • На вашем внутреннем сервере должны быть реализованы ограничители скорости, чтобы ограничить количество попыток и повторных попыток.
  • Бан или тарпит пользователей, которые попадают в слишком много ошибок сервера (300+, 400+, 500)
  • Не давайте подсказок о ваших технологиях, например, уберите X-Powered-By, который говорит, какой сервер вы используете в заголовке ответа. Вы можете использовать Helmetjs, если используете ExpressJS.

Регулярно обновляйте свои зависимости

  • Чтобы избежать использования пакетов с проблемами безопасности, обновите пакеты NPM:
# List security breaches 
npm audit
# Upgrade of minor and patch version following your version ranges in package.json
yarn outdated
yarn upgrade
# Interactive upgrade of minor and patch version following your version ranges in package.json 
yarn upgrade-interactive
# List outdated dependencies including major version
yarn upgrade-interactive --latest
# Same with npm
npm outdated
npm update
# Tool for upgrading to major versions (with potential breaking changes)
npm install -g npm-check-updates
ncu
  • Кроме того, обновляйте свой сервер, если вы не используете PaaS.

Добавить мониторинг

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

Аутентификация

Существует 2 основных механизма аутентификации (позже вы увидите, что мы можем комбинировать их) для идентификации клиента в REST API:

  • Жетон на предъявителя
  • Аутентификационный файл cookie

Жетон на предъявителя

Что такое жетон на предъявителя?

Маркер-носитель - это значение, которое входит в заголовок Authorization любого HTTP-запроса. Он нигде не сохраняется автоматически, у него нет срока годности и нет связанного домена. Это просто ценность:

GET https://www.example.com/api/users
Authorization: Bearer my_bearer_token_value

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

  • Заголовок
  • Полезная нагрузка (может содержать идентификатор пользователя и роли пользователя) + срок действия (необязательно)
  • Подпись

JWT - это криптографически безопасное средство обмена информацией, которое делает возможной аутентификацию без сохранения состояния. Подпись удостоверяет, что полезная нагрузка не была изменена (т. Е. Не скомпрометирована) благодаря симметричной или асимметричной (RSA) подписи. Заголовок содержит формат и адрес открытого ключа для проверки подписи (для асимметричной).

Обычно клиентское приложение получает токен JWT после аутентификации с помощью аутентификации пользователя / пароля (или других средств).

Он отправляет все следующие запросы на сервер с токеном JWT в заголовке HTTP благодаря JAVASCRIPT. Сервер проверяет, соответствует ли подпись полезной нагрузке, если есть совпадение, он может доверять содержимому полезной нагрузки.

Основные варианты использования

  • Защита трафика между браузером и определенной серверной частью
  • Защита трафика между мобильным приложением, настольным приложением и определенной серверной частью
  • Защита трафика между серверными модулями (M2M), контролируемыми разными сторонами (например, OpenId Connect), или внутри серверных служб одной стороны

Где хранить JWT?

Мы должны вручную сохранить JWT в клиентах (в памяти, локальном / сеансовом cookie, локальном хранилище и т. Д.).

Не рекомендуется хранить JWT в локальном хранилище браузера:

  • Он останется, если пользователь закроет браузер, чтобы сеанс можно было восстановить до истечения срока действия JWT.
  • Любой код JavaScript на вашей странице может получить доступ к локальному хранилищу: он не имеет никакой защиты данных.
  • Его не могут использовать веб-воркеры.

Решением может быть хранение JWT в сессионных файлах cookie, об этом мы поговорим позже.

Чтобы пойти дальше: https://auth0.com/docs/security/store-tokens

Базовые атаки

  • Атаки межсайтовый скриптинг (XSS) являются наиболее распространенными, когда javascript имеет дело с безопасностью: злоумышленник может поставить под угрозу JS-зависимости веб-сайта или использовать вводимые пользователем данные для добавления вредоносного кода javascript для кражи JWT жертвы. Затем злоумышленник использует его, чтобы выдать себя за пользователя.
  • Например, в комментарии в блоге пользователь может добавить JS в свой комментарий, чтобы выполнить клиентский JS на странице:
<img src=x onerror="&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041">

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

Cookie аутентификации

Файл cookie - это пара "имя-значение", которая хранится в веб-браузере и имеет дату истечения срока действия и связанный домен. Файлы cookie хранятся в веб-браузере. Их можно создать с помощью клиентского браузера JavaScript:

document.cookie = ‘my_cookie_name=my_cookie_value’ // JavaScript

или с сервера с помощью заголовка HTTP-ответа:

Set-Cookie: my_cookie_name=my_cookie_value // HTTP Response Header

Веб-браузер автоматически отправляет файлы cookie с каждым запросом в домен файла cookie.

GET https://www.example.com/api/users
Cookie: my_cookie_name=my_cookie_value

В большинстве случаев (с отслеживанием состояния) cookie используется для хранения идентификатора сеанса. Идентификатор сеанса управляется сервером (создание и тайм-аут). Мы говорим о с сохранением состояния, поскольку серверу необходимо управлять состоянием на сервере, тогда как токен JWT не имеет состояния.

Есть 2 вида файлов cookie (исходные):

  • Сеансовые файлы cookie: файл cookie удаляется при завершении работы клиента, поскольку в нем не указана директива Expires или Max-Age. Однако веб-браузеры могут использовать восстановление сеанса, что делает большинство файлов cookie сеанса постоянными, как если бы браузер никогда не закрывался. Тайм-аут сеанса должен обрабатываться на стороне сервера.
  • Постоянные файлы cookie
    Вместо истечения срока действия при закрытии клиента, постоянные файлы cookie истекают в определенный день (Истекает) или через определенный промежуток времени (Макс. -Возраст).

Файлы cookie сервера можно настроить с помощью нескольких опций:

  • HttpOnly cookie-файлы : JavaScript браузера не может их прочитать.
  • Безопасный файл cookie: браузер включает файл cookie в HTTP-запрос, только если запрос передается по безопасному каналу (обычно HTTPS).
  • Файлы cookie SameSite позволяют серверам требовать, чтобы cookie не отправлялся с межсайтовыми запросами, что в некоторой степени защищает от атак с подделкой межсайтовых запросов (CSRF). Файлы cookie SameSite все еще являются экспериментальными и пока поддерживаются не всеми браузерами.

Основные варианты использования

  • Защита трафика между браузером и определенной серверной частью
  • Файлы cookie затрудняют использование вашего API приложениями, не основанными на браузере (например, приложениями для мобильных устройств или планшетов).

Где хранить куки?

Они автоматически сохраняются в веб-браузере с указанием даты истечения срока действия (необязательно) и соответствующего домена.

Базовые атаки

  • Межсайтовый скриптинг (XSS), если файлы cookie не созданы с параметром HttpOnly: злоумышленник может внедрить код Javascript, который украдет файл cookie аутентификации жертвы.
  • Подделка межсайтовых запросов (CSRF) - распространенная атака с использованием файлов cookie аутентификации. Конфигурация CORS (Cross-Origin Resource Sharing) может быть выполнена на стороне сервера для авторизации только определенных имен хостов. Однако CORS проверяется браузером на стороне клиента. Хуже того, та же политика происхождения, заданная CORS, применима только для языков программирования на стороне браузера. Поэтому, если вы попытаетесь отправить сообщение на другой сервер, а не на исходный сервер, используя JavaScript, тогда в игру вступит та же политика происхождения, но если вы отправите сообщение непосредственно из HTML-формы, действие может указывать на другой сервер, например:
<form action="http://someotherserver.com">

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

Другой пример CSRF: предположим, что пользователь, пока он все еще авторизован на facebook.com, посещает страницу на bad.com. Теперь bad.com принадлежит злоумышленнику, который на bad.com закодировал следующее:

<img src="https://facebook.com/postComment?userId=dupont_123&comment=I_VE_BEEN_HACKED>

Чтобы уменьшить влияние XSS, для файла cookie должен быть установлен параметр HttpOnly.

Чтобы уменьшить влияние CSRF, для файла cookie должен быть установлен параметр SameSite. Параметр SameSite поддерживается не всеми браузерами, поэтому он не предотвратит все атаки CSRF. Могут быть использованы некоторые другие стратегии смягчения (которые можно комбинировать):

  • Короткое время ожидания сеанса (в финансовых доменах время ожидания составляет около 10 минут или меньше)
  • Критические действия всегда должны запрашивать учетные данные пользователя (например, запрашивать пароль пользователя в форме, которая изменяет адрес электронной почты пользователя)
  • Дважды отправленные файлы cookie: когда пользователь посещает сайт, сайт должен генерировать (криптографически стойкое) псевдослучайное значение и устанавливать его как файл cookie (без httpOnly , чтобы он был доступен из JS) на компьютере пользователя в дополнение к httpOnly аутентификационному файлу cookie. Сайт должен требовать, чтобы при каждой отправке формы это псевдослучайное значение содержалось как значение формы , а также как значение cookie. Когда на сайт отправляется POST-запрос, он должен считаться действительным только в том случае, если значение формы и значение cookie совпадают. Когда злоумышленник отправляет форму от имени пользователя, он может только изменять значения формы. Злоумышленник не может прочитать какие-либо данные, отправленные с сервера, или изменить значения файлов cookie в соответствии с политикой одного и того же происхождения. Это означает, что, хотя злоумышленник может отправить любое значение, которое он хочет, с формой, он не сможет изменить или прочитать значение, хранящееся в файле cookie. Поскольку значение cookie и значение формы должны быть одинаковыми, злоумышленник не сможет успешно отправить форму, если он не сможет угадать псевдослучайное значение (источник) или украсть значение из сопутствующей XSS-атаки.

Можем ли мы совместить и то, и другое?

Подведем итог, что мы ищем для нашего механизма аутентификации в нашем серверном API:

  • Поддержка браузера и вызовов M2M
  • XSS и CSRF максимально устойчивы
  • Без гражданства, если возможно

Как насчет того, чтобы поместить JWT в файл cookie, чтобы получить лучшее из обоих миров?

Наш API должен поддерживать токен-носитель JWT из заголовка запроса, а также JWT внутри файла cookie сеанса. Если мы хотим авторизовать javascript для чтения полезной нагрузки JWT, мы можем использовать подход аутентификации с двумя файлами cookie, объединив 2 типа файлов cookie, чтобы ограничить поверхность атаки XSS.

Подход с использованием двух файлов cookie был описан Питером Локком в статье https://medium.com/lightrail/getting-token-authentication-right-in-a-stateless-single-page-application-57d0c6474e3

Сервер JWT может без проблем обновлять каждый запрос, так как новый будет в ответе cookie и автоматически сохранен браузером. Таким образом, можно вернуть срок годности JWT.

Чтобы ограничить CSRF, мутации никогда не должны выполняться с помощью запроса GET, используйте PUT или POST. Мутации, вызывающие повышенную безопасность, должны снова запрашивать учетные данные пользователя, например, при изменении адреса электронной почты необходимо запрашивать пароль пользователя для подтверждения изменения. Временный файл cookie также может включать случайное число, которое считывается JS и отправляется в скрытом поле формы, если вместе с данными формы. Сервер должен проверить, соответствует ли случайное число в файле cookie значению из данных формы.

Заворачивать

Теперь процесс аутентификации для нашего SPA выглядит следующим образом:

  • ШАГ 1. Наше приложение SPA проверяет, существует ли cookie с полезной нагрузкой JWT, если да, пользователь прошел аутентификацию, в противном случае SPA перенаправляет на страницу / login. Если вы используете один файл cookie httpOnly, SPA должен выполнить вызов API, например // backend / api / me, чтобы узнать, кто является текущим пользователем, и получить несанкционированную ошибку, если файл cookie аутентификации (содержащий JWT) отсутствует или недействителен.
  • ШАГ 2 - Вариант 1: страница / login в интерфейсе пользователя запрашивает учетные данные пользователя (логин / пароль), а затем отправляет их в API серверной части с помощью запроса AJAX. Ответ AJAX установит файл cookie аутентификации с JWT внутри.
  • ШАГ 2 - Вариант 2: страница / login обеспечивает аутентификацию OpenID с использованием потока OAuth. Для потока предоставления кода авторизации / login должен перенаправлять все окно браузера на / / backend / auth / ‹provider›. Поток OAuth должен быть выполнен, а серверная часть должна установить файл cookie аутентификации с JWT внутри в последнем ответе. Затем он перенаправит браузер на внешний интерфейс. После этого SPA запустится снова, поэтому снова перейдите к ШАГУ 1.

Не стесняйтесь комментировать и высказывать свое мнение об этом посте. Если вам нравится то, что вы читаете, не забывайте хлопать в ладоши!

Часть 2 (Октябрь 2019): OAuth2, Saml, OpenID Connect, SSO, Grant flow, все, что вам нужно знать для вашего SPA (нажмите здесь).