Пишу RUSTful API на php, застрял на аутентификации с помощью сессий. Алгоритм у меня такой:
1. Клиент посылает login, RSA(pass)
2. Сервер расшифровывает пароль, хеширует, сравнивает в БД, в случае неудачи отправляет ошибку клиенту.
3. ?? Сервер генерирует случайный идентификатор сессии, записывает в бд (поля - user_id, sid, expired)
на данном шаге не совсем понимаю, какое время жизни сессии нужно задать
4. Сервер посылает клиенту sid и expired, т.е. индентификатор сессии и время жизни в секундах.
5. При последующих запросах клиент посылает в заголовке/в теле запроса sid.
Отсюда вопросы:
1. Как найти оптимальное время жизни сессии?
2. При запросах с текущим sid нужно ли увеличивать продолжительность жизни сессии?
3. В случае "гибели" сессии, клиент должен выполнить шаг 1 заново? (Тогда клиент должен постоянно хранить логин и шифрованный пароль, так?)
4. Как защитить sid от перехвата?
Артем: Если перехватывается весь трафик таким образом, что пакеты от клиента на сервер не проходят вообще, то ваш пример просто усложняет, но работать не будет. Как сервер, у которого НЕТ пароля а только его хеш, сможет расшифровать вашу строку?
От классических сессий при работе через API отказался.
1. Для авторизации пользователь вводит логинпароль, устройство отправляет их по https на account/auth
2. account/auth выдает token (token_id:token_val) и secret
3. все дальнейшие запросы устройство отправляет по http указывая token и подписывая запросы с помощью secret
Как работает.
Сервер получает запрос, видит что пришел token, разбивает его через двоеточие на input_id и input_val. Выбирает из базы токен с пришедшим input_id, получает из базы значение token_val и secret. Сравнивает input_val и token_val. Если в базе нашелся токен с нужным id и значения val равны, пришло время проверить достоверность запроса.
Клиент помимо токена передал sign (подпись), которую сформировал так (например) secret+api_path+query_param. На стороне сервера вам известно api_path и api_param, а secret выбрали из базы. Хешировать подпись принято через hmac().
Помимо токена и подписи можно передавать time и так же класть его в sign, и на стороне сервера отсекать запросы запросы которым больше 60 сек.
Таким образом.
Если кто то слушает ваш канал, он не сможет подделывать запросы (а значит компроментировать), и из-за проверки времени жизни запроса не сможет вечно получать данные по однажды перехваченного запроса.
А в базе токены можете хранить пока клиент сам не запросит их уничтожения и сохранить время последного обращения через токен, и удалять токены которые не использовались более 60 дн.
D' Normalization: Пароль известен только клиенту, тогда подойдет ли такой способ: к каждому запросу добавляем параметр sign=RSA(sha256(pass+все остальные параметры запроса)), на сервере расшифровываем закрытым ключом, хешируем и сравниваем. Если я правильно понимаю, то получить доступ к серверу теперь можно, только получив пароль кейлоггером или от пользователя, т.е. заразив его ПК или используя Соц.инж.?
Артем: Зачем вообще использовать пароль? В данном случае secret и есть pass, которым солится строка.
И уж украсть пароль с устройста - намного проще, чем перехватывать/подделывать ssl сессию.
D' Normalization: плюс secret в том, что даже если его украдут, то не смогут долго использовать, а вот если украдут пароль, то его смогут и сбрудить\постоянно использовать.
D' Normalization: Такой механизм защищает от подделки запроса, а ваш - нет.
Перехватив token (кстати, для чего id и val?) и secret, можем лепить любые запросы.
Перехватив sign=RSA(sha256(pass+параметры)) мы не расшифруем строку, не имея закрытого ключа. Не зная пароля не сможем составить другой запрос.
А пароль можно украсть и в вашем, и в моем случае, от этого действительно ничего не спасет, только вот провайдер API в этом случае не будет виноват.
Артем: использовать один мастер ключ для всех пользователей - прямая дорога ------> туда.
Не стоит заморачиваться там, где это не требуется. Если у вас действительно такие чувствительные к перехвату данные, то стоит смотреть на более стойкие решение (посмотрите реализацию протокола telegram).
Если не хочется сильно заморачиваться, и так нравится RSA, то стоит генерировать для каждого пользователя отдельные public/private ключи, и чтобы свой public ключ он скачивал через другой источник вручную, тогда никто ничего не перехватит, и не сможет навредить.
Артем: "кстати, для чего id и val?", только чтобы из БД выбирать по INT и по нему же индекс сделать. Ведь каждый запрос к API, это проверка токена. Индекс по числовому полю легче, чем индекс по varchar.
Пишу RUSTful API на php, застрял на аутентификации с помощью сессий
Вы уверены что Вы понимаете что такое REST? Вы пишите что угодно, но не REST! Одно из требований к REST это stateless. Другими словами в Ваших запросах УЖЕ есть все необходимое для выполнения операции и тоже самое будет в ответе "либо приходи позже" либо "держи результат запроса на операцию". В REST нет такого термина, как сессия. Если у Вас это получилось, то вы написали не REST
Давайте холивар не будем разводить, на том же хабре очень много споров было, можно ли использовать сессии в RESTful. Скажите тогда, как аутентификацию сделать?
Скажите, пожалуйста, как определять, что пользователь имеет доступ к данному методу? Нельзя же просто по id пользователя отдавать его сообщения, нужно еще что-то передавать - вот про это и спрашивает автор.
Дмитрий: По вашему, авторизация (и аутентификация) и REST - несовместимые вещи что ли? Или вы предлагаете каждый раз пару логин-пароль вместе с запросом отправлять? Вам не кажется, что это не совсем безопасно?
Артем: Почему не совместимы HTTPS - раз, внутри шифрованного трафика шлешь по http basic-authentication - это два, в БД на стороне сервера хранится в PBKDF2 формате - это три. Любой запрос сопровождается токеном с определенным сроком действия, в котором закодирован USER-ID - это четыре. Токен выдается по user_id, password - это пять.
>>Или вы предлагаете каждый раз пару логин-пароль вместе с запросом отправлять?
Смотри номер-раз. Сейчас почти любой веб-ресурс чуть ли не обязан работать по HTTPS. А многие компании принудительно переводят пользователей на HTTPS.
Lomoson: Пользователь пишет не REST, хоть и думает что пишет его. Если бы у него был REST. То никак проблем с учетом сессий. В каждом запросе указывается токен, в котором expire-информация с кодированием user_id. По user_id можно понять нужно ли сервису отвечать на user_id или нет?
Артем: Да не привязался я к нему. Просто чувак путает термины. Если бы он сказал пишу Web API к сервису, то ни одного замечания бы не было. Но он же говорит REST !!! Это все равно что придут люди к фанатам Зенита и будут утверждать что в футболе можно и рукать как в баскетболе и разницы никакой ;)
Я так понимаю, что уже ответ не актуальный, но все-таки REST скорее подразумевает толстый клиент, по-этому состояние он хранить не должен. Yii2 умеет из коробки делать аутентификацию для REST, другие фреймворки, думаю, тоже.