Как сделать аутентификацию с помощью REST API? (Браузер + Родные клиенты)

Я создаю веб-приложение с помощью Rails. На данный момент я использую Devise с сеансами HTTP, которые было довольно легко настроить и хорошо работает.

Приложение состоит из одного URL-адреса, предоставляющего веб-приложение AJAX. Остальные доступные URL принадлежат REST API. Таким образом, все и каждый небольшой запрос данных выполняется через AJAX.

Теперь я хотел бы расширить все, чтобы поддерживать родных клиентов. Я много читаю о autache, не относящемся к учетной записи, http basic и digest auth, http-сессиях, файлах cookie, xsrf и т.д. И теперь я чувствую, что у меня нет безопасного приложения, потому что всегда есть способ захватить некоторые его части.

1.: HTTP-сессия Vs. токен аутентификации без учета состояния

Какая разница? Я не понимаю.

  • HTTP-сессия:

    • Клиент запрашивает URL-адрес (первый запрос на сервер)
    • Сервер дает обычный ответ плюс уникальную строку (== session ID)
    • Клиент должен отправить эту строку с каждым запросом (который делается автоматически с использованием HTTP-заголовка)
    • Клиент входит в систему → Сервер запоминает, что данный идентификатор сеанса теперь зарегистрирован
    • Клиент посещает страницу, для которой требуется auth → ничего особенного, потому что идентификатор сеанса будет автоматически отправляться на сервер через HTTP-заголовок
  • токен authen:

    • URL-адрес запроса клиента (первый запрос на сервер)
    • Сервер просто дает обычный ответ без любого ключа или токена или id
    • (здесь ничего особенного)
    • Клиент входит в систему → Сервер создает токен аутентификации и отправляет этот токен клиенту внутри ответа
    • Клиент посещает страницу, для которой требуется аутентификация → Клиент должен отправить токен аутентификации

Для меня оба способа выглядят довольно похожими. С Rails я также могу выбрать сохранение сеанса внутри базы данных... Devise будет делать то же самое с токеном authen auth.

2: Метод аутентификации

Сейчас я использую POST /users/sign_in с {"user":{"email":"[email protected]","password":"p455w0rd"}}.

Но есть и другие возможности, такие как HTTP basic auth и HTTP digest auth, но также такие решения, как oAuth (слишком большой для моей цели).

Из того, что я читал:

  • Относительно sign_in безопасности нет никакой разницы между текущим POST /users/sign_in и базовым протоколом HTTP. Оба используют открытый текст.
  • Для sign_out Недостаток базового авторизации HTTP: Выход из системы возможен только при закрытии окна браузера.
  • HTTP digest auth имеет огромное преимущество: он вообще не передает пароль (просто хеш пароля плюс случайная сгенерированная строка)
  • (немецкий) Wikipedia говорит: HTTP-дайджест auth не поддерживается всеми браузерами. Может быть, эта информация является способом старой?!

Что мне нужно:

  • имена пользователей и хешированные пароли (bcrypt), хранящиеся в базе данных.
  • пользователь может изменить свой пароль, а пароль не будет отправлен в виде открытого текста. (Такая же проблема возникает, когда речь идет о пользователе sign_up). Возможные решения?
    • конечно: использование SSL/TLS
    • запрос клиента a want_to_change_password_salt и использует его для шифрования пароля на стороне клиента. , но (?!) таким образом я отправил важную часть хэшированного пароля по проводному плюс хешированному паролю. Звучит неуверенно для меня?!

3: токен CSRF

Как уже говорилось, прямо сейчас у меня есть обычный веб-сайт AJAX с использованием REST API. Он имеет защиту XSRF: веб-сайт доставлен рельсами и, таким образом, встроил токен XSRF. Я прочитал его с помощью AJAX и передал его при выполнении POST. Затем Rails возвращает запрошенные данные и новый токен XSRF, который затем я использую для следующего POST.

Теперь я хочу изменить свое серверное приложение для работы с родными клиентами. Собственный клиент не загрузит HTML-страницу и, следовательно, не получит токен CSRF. Поэтому мне пришло в голову следующее:

  • Создайте ресурс REST-токена XSRF. Поэтому (native) клиент должен запросить токен XSRF с этого ресурса, прежде чем он сможет сделать первый POST.
  • Отключить полностью защиту XSRF.

Вопросы:

  • Как работает защита XSRF (в Rails)? Как сервер знает, какой токен принадлежит клиенту? Единственный способ, о котором я могу думать, - это сеансы. Это предположение приводит к:
  • Если я отключу сеанс, чтобы создать полностью автономный REST API, защита XSRF больше не будет работать. Правильно?

4: токен без аутентификации

Здесь у меня в основном много вопросов:

  • Имеют ли те же проблемы безопасности, что и сеансы HTTP? Я имею в виду: кража идентификатора сеанса имеет такой же эффект, как кража токена аутентификации. Правильно?
  • Истечение срока действия маркера auth должно работать так же, как и с сеансами HTTP: сервер должен хранить где-нибудь (база данных, соответственно, сеанс) временную метку, и проверить это.
  • sign_out тоже работает?
    • Сессия: Уничтожьте сеанс на сервере
    • Маркер аутентификации: уничтожьте токен на сервере
  • Из того, что я прочитал, должно быть более безопасно хранить токен аутентификации внутри HTTP-заголовка (точно так же, как идентификатор сеанса), поскольку журналы сервера могут содержать параметры GET и, следовательно, могут содержать токен.
  • Должен ли он быть простым токеном аутентификации или было бы лучше, если бы клиент также передал свой user_id или даже хешированный пароль? ЗДЕСЬ Я прочитал, что клиент должен отправить:
    • user_id
    • expiration_date
    • хеш (или то, что HMAC?) [ user_id, expiration_date, SECRET_KEY]. Где SECRET_KEY - это в основном случайная строка, сгенерированная сервером.

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

Спасибо:)


Здесь немного новой информации и новых вопросов ;-):

5: Родные клиенты

Что касается родных клиентов, нет (простого) способа использования сеансов:

  • Нативный клиент без браузера

  • Таким образом, это будет нелегко обрабатывать файлы cookie (и без файлов cookie нет обычной обработки сеанса)

Итак, возможны 3 варианта:

  • Внедрить обработку сеанса для собственных клиентов. Это будет выглядеть так:

    • Войти
    • прочитайте HTTP-заголовок ответа, чтобы получить файлы cookie
    • сохраните все данные cookie, которые вам нужны (особенно тот, у которого есть материал сеанса) локально.
    • отправьте этот идентификатор сеанса с каждым запросом.
  • Не используйте сеансы вообще. С точки зрения собственного клиента это почти то же самое, что и 1.:

    • Войти
    • Получите определенный токен аутентификации из заголовка HTTP или тела ответа (это ваше приложение, хотя оно зависит от вас)
    • сохранить этот токен локально
    • отправить этот токен с каждым запросом
  • Гибридный подход. Это в основном означает, что сервер должен различать браузер и собственный клиент, а затем проверять предоставленный идентификатор сеанса и данные сеанса или (для собственных клиентов) проверять предоставленный токен аутентификации.

6: токен CSRF с апатридом (= нет сеанса/без файлов cookie) auth

CSRF Protection защищает ваших пользователей от вредоносных веб-сайтов, которые пытаются выполнить какой-либо запрос по вашему API на имя вашего зарегистрированного пользователя, но без вашего ведома пользователя. Это довольно просто при использовании сеансов:

  • Пользователь регистрируется в вашем API
  • Сессия создана
  • В вашем браузере пользователей будет установлен файл cookie с этим идентификатором сеанса.
  • Каждый запрос, который ваш пользователь делает, ваш API автоматически аутентифицируется, потому что браузер отправит все файлы cookie (включая идентификатор сеанса) вместе с каждым запросом вашего API.

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

  • Напишите пользовательский HTML <form>, который указывает на ваш API
  • Позвольте пользователю как-то нажать кнопку Submit

Конечно, эта форма будет выглядеть примерно так:

<form action="http://your.api.com/transferMoney" method="post">
  <input type="hidden" name="receiver" value="ownerOfTheEvilSite" />
  <input type="hidden" name="amount" value="1000.00" />
  <input type="submit" value="WIN MONEY!!" />
</form>

Это приводит к следующим предположениям:

  • Защита CSRF необходима только потому, что браузеры автоматически отправляют файлы cookie.

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

  • Если у вас есть дизайн API, который не использует Cookies для аутентификации пользователя, нет возможности делать CSRF. Поскольку злоумышленник должен знать токен аутентификации и явно отправлять его вместе со злонамеренным запросом.

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


7: Правильные HTTP-методы для выбора

Вход/Вход и выход/Выход:

Никогда используйте GET для (по крайней мере) трех причин:

  • Защита CSRF в большинстве случаев защищает только POST, PUT, PATCH и DELETE, и поэтому CSRF может войти в систему без его знаний при использовании запроса GET

  • Запросы GET должны никогда изменять состояние приложения. Но когда вы используете сеансы, состояние приложения изменяется при входе/выходе из системы, потому что сеанс создается или уничтожается.

  • При использовании запроса GET и передачи информации аутентификации в качестве параметров URL (т.е. http://your.api.com/login?username=foo&password=bar) возникает другая проблема: журналы сервера! Большинство серверов просто регистрируют каждый HTTP-запрос, включая все параметры URL. Это означает: если ваш сервер взломан, нет необходимости взломать хэши паролей из вашей базы данных, они должны просто посмотреть файлы журналов сервера. Кроме того, злоумышленник может также прочитать информацию для входа для каждого пользователя. Решения:

    • Используйте POST (или любой другой метод) и отправляйте информацию об аутентификации внутри тела запроса. Или:
    • Отправлять информацию об аутентификации в заголовках HTTP. Поскольку эта информация обычно не отображается в файлах журнала сервера. Или:
    • Посмотрите конфигурацию сервера и сообщите ему, чтобы удалить каждый параметр URL, который называется "пароль" (или обфускация, поэтому URL-адрес становится login?username=foo&password=*** внутри журналов). Но я предлагаю просто использовать тело запроса для такого рода информации вместе с методом POST.

Итак, вы можете использовать, например:

POST http://your.api.com/authentication для входа

DELETE http://your.api.com/authentication для выхода


8.: Пароли и Хеширование

Аутентификация работает только с секретным ключом. И, конечно, этот ключ должен храниться в секрете. Это означает:

  • Никогда храните пароль в виде открытого текста в базе данных. Доступно несколько библиотек для обеспечения безопасности. На мой взгляд, лучший вариант - bcrypt.

  • bcrypt. Он оптимизирован для хэш-паролей. Он автоматически генерирует соль и хеширует пароль несколько раз (раунды). Кроме того, сгенерированная хэш-строка содержит все необходимое: количество раундов, соль и хеш. Хотя вам просто нужно сохранить эту одну String, и нет необходимости писать что-либо вручную.

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

  • раунды. Это просто, как часто пароль должен быть хэширован. При использовании 5000 раундов хеширующая функция вернет хэш хэш хэша хэша пароля. Там в основном единственная причина для этого: это стоит мощности процессора! Это означает: когда кто-то пытается нанести вред вашему хешу, он занимает 5000 раз дольше при использовании 5000 раундов. Для вашего приложения это не имеет значения: если пользователь знает свой пароль, он не узнает, если сервер принял 0.0004ms или 2ms, чтобы проверить его.

  • хорошие пароли. Лучшая хеширующая функция бесполезна, если пароль слишком прост. Если он может быть взломан, используя словарь, это не имеет большого значения, если вы хэшировали его 5000 раундами: это может занять несколько часов дольше, но что за несколько часов, если это может быть месяцы или годы? Хотя убедитесь, что ваши пользовательские пароли содержат обычные рекомендации (нижний + верхний регистр + номера + специальные символы и т.д.).


9: отправка зашифрованных паролей по кабелю

Если вы не можете (или не хотите) полагаться на HTTPS, но не хотите отправлять пароли в cleartext при входе в систему, вы можете использовать асимметричную криптографию (http://en.wikipedia.org/wiki/Public-key_cryptography).

Этот сервер создает пару ключей (открытый ключ и закрытый ключ). Открытый ключ предоставляется клиентам, закрытый ключ должен быть конфиденциальным!

Клиент теперь может шифровать данные с помощью открытого ключа, и эти данные могут быть дешифрованы только владельцем закрытого ключа (= сервер).

Это не должно (!) использоваться для хранения паролей в базе данных, потому что если ваш сервер будет взломан, у хакера будут зашифрованные пароли изакрытый ключ для дешифрования. Хотя продолжайте использовать некоторый алгоритм хэширования (например, bcrypt) для хранения паролей в вашей базе данных. Другая причина заключается в том, что вы можете легко создать новую пару ключей, если вы считаете, что кто-то взломал вам шифрование.

HTTPS в основном работает одинаково. Хотя, если ваше приложение использует HTTPS (что рекомендуется), с точки зрения безопасности не может быть большой пользы. Но, как указано выше, если вы не можете использовать HTTPS по какой-либо причине или не доверяете ему, это способ создать собственное безопасное соединение.

И имейте в виду, что реальное соединение HTTPS шифрует все (!) соединение и все данные, а не только данные пароля. И он шифрует его в обоих направлениях: от клиента к серверу и к клиенту.