Создание криптографически защищенных токенов аутентификации
Фон:
Это действительно общий вопрос, связанный с лучшей практикой, но может быть полезно некоторое описание конкретной ситуации:
Мы разрабатываем "подключенное" приложение для iPhone. Он будет связываться с бэкэнд-приложением через службы REST. Чтобы не запрашивать у пользователя имя пользователя и пароль при каждом запуске приложения, мы откроем службу "Логин", которая проверяет их имя пользователя и пароль при первом запуске и возвращает токен аутентификации, который может быть использован для будущей сети запросы на обслуживание для реальных данных. У токена может быть срок действия, после которого мы попросим их повторно аутентифицироваться с их именем пользователя/паролем.
Вопрос:
Каковы наилучшие методы для создания такого токена, который будет использоваться для аутентификации?
Например, мы могли бы...
- Hash (SHA-256 и т.д.) - случайная строка и сохранить ее в базе данных для данного пользователя вместе с датой истечения срока действия. Сделайте простой поиск токена в последующих запросах, чтобы убедиться, что он соответствует.
- Зашифруйте идентификатор пользователя и некоторую дополнительную информацию (временную метку и т.д.) с помощью секретного ключа. Расшифруйте токен в последующих запросах, чтобы убедиться, что он был выпущен нами.
Похоже, что это должна быть проблема.
Ответы
Ответ 1
Основываясь на отзывах других ответов на этот вопрос, дополнительных исследованиях и обсуждениях в оффлайне, вот что мы в итоге сделали...
Было отмечено довольно быстро, что модель взаимодействия здесь по существу точно такая же, как модель, используемая в Autodesk Forms в ASP.NET, когда установлен флажок "помнить меня". Это просто не веб-браузер, делающий HTTP-запросы. Наш "билет" эквивалентен файлу cookie, который формирует комплекты аутентификации. Аутентификация форм использует по умолчанию "зашифровать некоторые данные с помощью секретного ключа" по умолчанию.
В нашей веб-службе входа мы используем этот код для создания билета:
string[] userData = new string[4];
// fill the userData array with the information we need for subsequent requests
userData[0] = ...; // data we need
userData[1] = ...; // other data, etc
// create a Forms Auth ticket with the username and the user data.
FormsAuthenticationTicket formsTicket = new FormsAuthenticationTicket(
1,
username,
DateTime.Now,
DateTime.Now.AddMinutes(DefaultTimeout),
true,
string.Join(UserDataDelimiter, userData)
);
// encrypt the ticket
string encryptedTicket = FormsAuthentication.Encrypt(formsTicket);
Затем у нас есть атрибут поведения операции для служб WCF, который добавляет IParameterInspector, который проверяет действительный билет в заголовках HTTP для запроса. Разработчики помещают этот атрибут поведения операции в операции, требующие аутентификации. Вот как этот код анализирует билет:
// get the Forms Auth ticket object back from the encrypted Ticket
FormsAuthenticationTicket formsTicket = FormsAuthentication.Decrypt(encryptedTicket);
// split the user data back apart
string[] userData = formsTicket.UserData.Split(new string[] { UserDataDelimiter }, StringSplitOptions.None);
// verify that the username in the ticket matches the username that was sent with the request
if (formsTicket.Name == expectedUsername)
{
// ticket is valid
...
}
Ответ 2
Построение собственной системы аутентификации всегда является "наихудшей практикой". То, что лучше всего оставить профессионалам, специализирующимся на системах аутентификации.
Если вы склонны строить свой собственный "истекающий билет из системы входа в систему", а не повторно использовать существующий, вероятно, хорошая идея, по крайней мере, ознакомиться с проблемами, которые привели к разработке подобных систем, как и Kerberos. Нежное введение здесь:
http://web.mit.edu/kerberos/dialogue.html
Также было бы неплохо взглянуть на то, какие дыры в безопасности были обнаружены в Kerberos (и аналогичных системах) за последние 20 лет, и убедитесь, что вы их не реплицируете. Kerberos был создан экспертами по безопасности и тщательно анализируется на протяжении десятилетий, и в нем все еще встречаются серьезные алгоритмические недостатки:
http://web.mit.edu/kerberos/www/advisories/MITKRB5-SA-2003-004-krb4.txt
Гораздо лучше учиться на своих ошибках, чем ваши собственные.
Ответ 3
Amazon.com использует токен сообщения HMAC SHA-1 для аутентификации и авторизации запросов. Они используют это для довольно большой коммерческой службы, поэтому я буду доверять их инженерным решениям. Google публикует OpenSocial API, который несколько похож. Основываясь на Google и Amazon.com, используя похожие и открыто опубликованные подходы к защите веб-запросов, я подозреваю, что это, вероятно, хорошие способы.
Ответ 4
Любой из двух ответов, которые вы предоставили, будет достаточным. Вы можете найти фреймворки, которые делают это для вас, но, правда, это не так сложно построить. (Каждая компания, с которой я работала, уже переделала.) Выбор данных, хранящихся в базе данных, в сравнении с зашифрованными данными "cookie" - это архитектурное решение - вы хотите, чтобы поиск в базе данных просматривался на каждом просмотре страницы, или вы бы предпочли пережевывать процессор с расшифровкой файлов cookie? В большинстве приложений использование зашифрованных файлов cookie обеспечивает выигрыш в производительности по шкале (если это связано). В противном случае это просто вопрос вкуса.
Ответ 5
Поскольку вы используете WCF, у вас есть множество опций, если вы используете CFNetwork - например, NTLM или Digest Authentication:
http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/Concepts/Concepts.html#//apple_ref/doc/uid/TP30001132-CH4-SW7
Я знаю, что это не отвечает на ваш конкретный вопрос, но я также столкнулся с этой проблемой (iPhone - Tomcat) и решил как можно больше использовать службы аутентификации на веб-сервере. В большинстве случаев нет существенного штрафа за включение информации аутентификации с каждым запросом. Быстрый Google раскрывает множество сообщений в блогах о службах WCF и RESTful (и некоторые связанные с ними вопросы по StackOverflow).
Надеюсь, это поможет!
Ответ 6
Вы должны реализовать:
которые являются именно рабочими потоками, от OAuth2, которые вы ищете. Не изобретайте велосипед.
Ответ 7
Это просто звучит как идентификатор сеанса с длительным сроком действия. Те же принципы, которые использовались для этого в веб-приложениях, могут применяться здесь.
Вместо информации о кодировании идентификаторы сеансов выбираются случайным образом из очень большого пространства (128 бит). Сервер сохраняет запись, связывающую идентификатор сеанса с пользователем и другую желаемую информацию, такую как время истечения срока действия. Клиент представляет идентификатор сеанса по защищенному каналу с каждым запросом.
Безопасность зависит от непредсказуемости идентификаторов сеанса. Создайте их с криптографическим RNG, из очень большого пространства.