Лучшие практики SPA для аутентификации и управления сеансами

При создании приложений стиля SPA с использованием таких фреймворков, как Angular, Ember, React и т.д., что люди считают лучшими методами аутентификации и управления сеансами? Я могу придумать пару способов рассмотрения проблемы.

  • Относитесь к нему не иначе, как к аутентификации с помощью обычного веб-приложения, предполагая, что API и UI имеют один и тот же исходный домен.

    Вероятно, это связано с наличием сеансового файла cookie, хранилища сеансов на стороне сервера и, возможно, некоторой конечной точки сеанса API, с которой может пройти аутентифицированный веб-интерфейс, чтобы получить текущую пользовательскую информацию, чтобы помочь с персонализацией или, возможно, даже определить роли/возможности на стороне клиента. Сервер по-прежнему будет применять правила защиты доступа к данным, конечно, пользовательский интерфейс будет использовать эту информацию для настройки опыта.

  • Относитесь к нему как к любому стороннему клиенту, используя общедоступный API и аутентифицируясь с помощью какой-либо системы токенов, подобной OAuth. Этот токен-механизм будет использоваться клиентским интерфейсом для аутентификации каждого запроса, сделанного в API-интерфейсе сервера.

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

Ответ 1

Этот вопрос был рассмотрен в несколько иной форме, здесь:

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

Но это обращается к нему со стороны сервера. Давайте посмотрим на это с клиентской стороны. Прежде чем мы это сделаем, есть важная прелюдия:

Javascript Crypto is Hopeless

Статья Матасано об этом известна, но уроки, содержащиеся в ней, очень важны:

http://www.matasano.com/articles/javascript-cryptography/

Подводя итог:

  • Атака "человек в середине" может тривиально заменить ваш криптокод на <script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
  • Атака "человек-в-середине" тривиальна против страницы, которая обслуживает любой ресурс по не-SSL-соединению.
  • Как только у вас есть SSL, вы все равно используете реальный крипто.

И добавить свое собственное:

  • Успешная атака XSS может привести к тому, что злоумышленник выполнит код в вашем браузере клиента, даже если вы используете SSL - так что, даже если у вас есть каждый люк, вы можете отключить криптовальщик вашего браузера, если ваш атакующий найдет способ выполнить любой код javascript на другом браузере.

Это делает невозможным или глупым множество схем аутентификации RESTful, если вы намереваетесь использовать клиент JavaScript. Давай посмотрим!

HTTP Basic Auth

Прежде всего, HTTP Basic Auth. Простейшая схема: просто передайте имя и пароль с каждым запросом.

Это, конечно, абсолютно требует SSL, потому что вы передаете Base64 (обратимо) кодированное имя и пароль с каждым запросом. Любой, кто прослушивает эту строку, может извлекать имя пользователя и пароль тривиально. Большинство аргументов "Basic Auth is insecure" исходят из места "Basic Auth over HTTP", что является ужасной идеей.

В браузере предусмотрена поддержка HTTP Basic Auth, но она уродлива, как грех, и вы, вероятно, не должны использовать ее для своего приложения. Альтернативой, однако, является спрятать имя пользователя и пароль в JavaScript.

Это самое RESTful решение. Сервер не требует каких-либо знаний о состоянии и аутентифицирует каждое индивидуальное взаимодействие с пользователем. Некоторые энтузиасты REST (в основном, соломенные) настаивают на том, что поддержание любого состояния является ересью и будет пениться во рту, если вы думаете о каком-либо другом методе проверки подлинности. Существуют теоретические преимущества такого соответствия стандартам - он поддерживается Apache из коробки - вы можете хранить свои объекты в виде файлов в папках, защищенных файлами .htaccess, если вам нужно сердце!

Проблема ? Вы используете на стороне клиента имя пользователя и пароль. Это дает evil.ru лучший треск на нем - даже самые простые из уязвимостей XSS могут привести к тому, что клиент будет бить свое имя пользователя и пароль злому серверу. Вы можете попытаться смягчить этот риск, хэшируя и засовывая пароль, но помните: JavaScript Crypto is Hopeless. Вы могли бы смягчить этот риск, оставив его до поддержки браузера Basic Auth, но... уродливым как грех, как упоминалось ранее.

HTTP Digest Auth

Возможно ли аутентификация дайджеста с помощью jQuery?

Более "безопасный" auth, это запрос хеша запроса/ответа. За исключением JavaScript Crypto is Hopeless, поэтому он работает только через SSL, и вам все равно придется кэшировать имя пользователя и пароль на стороне клиента, что делает его более сложным, чем HTTP Basic Auth, но не более безопасным.

Аутентификация запроса с дополнительными параметрами подписи.

Еще одно "безопасное" auth, где вы шифруете свои параметры с помощью несевых и временных данных (для защиты от повторных и временных атак) и отправки. Одним из лучших примеров этого является протокол OAuth 1.0, который, насколько мне известно, является довольно сложным способом реализации проверки подлинности на сервере REST.

http://tools.ietf.org/html/rfc5849

О, но нет клиентов OAuth 1.0 для JavaScript. Зачем?

JavaScript Crypto is Hopeless, помните. JavaScript не может участвовать в OAuth 1.0 без SSL, и вам все равно нужно локально хранить имя пользователя и пароль клиента, что ставит его в ту же категорию, что и Digest Auth - это сложнее, чем HTTP Basic Auth, но не более безопасно.

Знак

Пользователь отправляет имя пользователя и пароль, а взамен получает токен, который может использоваться для аутентификации запросов.

Это немного более безопасно, чем HTTP Basic Auth, потому что как только транзакция имени пользователя/пароля будет завершена, вы можете отбросить конфиденциальные данные. Это также меньше RESTful, поскольку токены составляют "состояние" и усложняют реализацию сервера.

SSL еще

Правда, вам все равно придется отправить это начальное имя пользователя и пароль, чтобы получить токен. Чувствительная информация по-прежнему касается вашего компрометирующего JavaScript.

Чтобы защитить учетные данные пользователя, вам все равно нужно избегать атаки злоумышленников из вашего JavaScript, и вам все равно нужно отправить имя пользователя и пароль по кабелю. Требуется SSL.

Срок действия токена

Общепринято применять политики токена, такие как "эй, когда этот токен слишком долго, отбросьте его и повторите проверку подлинности пользователя". или "Я уверен, что единственным IP-адресом, разрешенным для использования этого токена, является XXX.XXX.XXX.XXX". Многие из этих политик являются довольно хорошими идеями.

Firesheeping

Однако использование токена Без SSL все еще уязвим для атаки под названием "sidejacking": http://codebutler.github.io/firesheep/

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

tl; dr: Отправка незашифрованных токенов по проводу означает, что злоумышленники могут легко набить эти маркеры и притворяться вашим пользователем. FireSheep - это программа, которая делает это очень просто.

Отдельная, более безопасная зона

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

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

Cookie (только означает токен)

Возможно (и распространено) поставить токен аутентификации в файл cookie. Это не изменяет никаких свойств auth с токеном, это скорее удобная вещь. Все предыдущие аргументы все еще применяются.

Сессия (еще только означает токен)

Session Auth - это аутентификация только Token, но с несколькими отличиями, которые делают это несколько иначе:

  • Пользователи начинают с неавторизованного токена.
  • Бэкэнд поддерживает объект 'state', привязанный к токену пользователя.
  • Токен предоставляется в файле cookie.
  • Прикладная среда абстрагирует детали от вас.

Кроме того, однако, он не отличается от Token Auth, действительно.

Это еще больше удаляется от реализации RESTful - с объектами состояния, которые вы продвигаете дальше и дальше по пути простого RPC RPC на конечном сервере.

OAuth 2.0

В OAuth 2.0 рассматривается проблема "Как программное обеспечение A предоставляет ПО B доступ к данным пользователя X без программного обеспечения B, имеющего доступ к учетным данным пользователя X."

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

В сущности, OAuth 2.0 - это всего лишь протокол токена. Он обладает теми же свойствами, что и другие протоколы токенов - для защиты этих токенов вам все еще нужен SSL, он просто изменяет способ генерации этих токенов.

Существует два способа, которыми OAuth 2.0 может помочь вам:

  • Предоставление аутентификации/информации другим
  • Получение аутентификации/информации от других

Но когда дело доходит до этого, вы просто... используете токены.

Вернуться к вашему вопросу

Итак, вопрос, который вы задаете, - "Я должен хранить свой токен в файле cookie и настроить автоматическое управление сеансом моей среды на детали, или я должен хранить свой токен в Javascript и самостоятельно обрабатывать эти данные?"

И ответ: делайте то, что делает вас счастливыми.

Дело в автоматическом управлении сеансами, тем не менее, заключается в том, что для вас много волшебства, происходящего за кулисами. Часто лучше контролировать эти детали самостоятельно.

Мне 21, поэтому SSL да

Другой ответ: используйте https для всего, или разбойники украдут пароли и токены ваших пользователей.

Ответ 2

Вы можете повысить безопасность в процессе аутентификации с помощью JWT (JSON Web Tokens) и SSL/HTTPS.

Базовый идентификатор Auth/Session можно украсть с помощью:

  • Атака MITM (Man-In-The-Middle) - без SSL/HTTPS
  • Нарушитель получает доступ к пользовательскому компьютеру.
  • XSS

Используя JWT, вы шифруете данные аутентификации пользователя и сохраняете их на клиенте и отправляете вместе с каждым запросом API, где сервер /API проверяет токен. Невозможно дешифровать/прочитать без закрытого ключа (который хранится в сервере /API ) Прочитать обновление.

Новый (более безопасный) поток будет:

Войти

  • Пользователь регистрируется и отправляет учетные данные для входа в API (через SSL/HTTPS).
  • API получает учетные данные для входа
  • Если это действительно:
    • Зарегистрировать новый сеанс в базе данных Обновить обновление
    • Шифровать идентификатор пользователя, идентификатор сеанса, IP-адрес, метку времени и т.д. в JWT с закрытым ключом.
  • API отправляет токен JWT обратно клиенту (через SSL/HTTPS)
  • Клиент получает токен JWT и сохраняет в localStorage/cookie

Каждый запрос API

  • Пользователь отправляет HTTP-запрос в API (через SSL/HTTPS) с сохраненным токеном JWT в HTTP-заголовке
  • API читает заголовок HTTP и расшифровывает токен JWT с его закрытым ключом
  • API проверяет токен JWT, сопоставляет IP-адрес из HTTP-запроса с таковым в токере JWT и проверяет, закончился ли сеанс.
  • Если это действительно:
    • Обратный ответ с запрошенным контентом
  • Если это недействительно:
    • Исключение Throw (403/401)
    • Включение флага в систему
    • Отправить пользователю электронное письмо.

Обновлено 30.07.15:

Полезная нагрузка/претензии JWT действительно может быть прочитана без секретного ключа (секретная), и она не защищена для хранения в localStorage. Прошу прощения за эти ложные заявления. Однако они, похоже, работают над стандартом JWE (JSON Web Encryption).

Я реализовал это, сохранив заявки (userID, exp) в JWT, подписал его с закрытым ключом (секретным), который API/Бэкэнд знает и хранит в качестве защищенного файла cookie HttpOnly на клиенте. Таким образом, он не может быть прочитан с помощью XSS и не может быть обработан, иначе JWT не сможет проверить подпись. Кроме того, используя безопасный файл cookie HttpOnly, вы убедитесь, что cookie отправляется только через HTTP-запросы (не доступны для script) и отправляется только через безопасное соединение (HTTPS).

Обновлено 17.07.16:

JWT по своей природе не имеют гражданства. Это означает, что они недействительны/истекают. Добавляя SessionID в утверждениях токенов, вы делаете его с сохранением состояния, так как его действительность теперь зависит только от проверки подписи и даты истечения срока действия, это также зависит от состояния сеанса на сервере. Однако вверх вы можете легко лишить токены/сеансы, которые вы не могли бы сделать с помощью JVT без гражданства.

Ответ 3

Я бы выбрал вторую, символическую систему.

Знаете ли вы о ember-auth или ember-simple-auth? Они оба используют систему на основе токенов, например, состояния ember-simple-auth:

Легкая и ненавязчивая библиотека для реализации основанных на токенах аутентификации в приложениях Ember.js. http://ember-simple-auth.simplabs.com

У них есть управление сессиями, и их легко подключить к уже существующим проектам.

Существует также версия Ember App Kit примера ember-simple-auth: Рабочий пример набора ember-app-kit с использованием ember-simple-auth для проверки подлинности OAuth2.