Использование дайджест-аутентификации HTTP на iPhone

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

Мне кажется, что управление «сеансами» в iPhone — довольно «черный ящик» для нас, разработчиков. Правда ли, что мы не можем видеть, как фреймворк обрабатывает/сохраняет http-сессии?

Если я просто тусклый, не мог бы кто-нибудь объяснить, как, вероятно, обрабатывать аутентификацию HTTP Digest на iPhone?

Мой основной прогон:

  • Сделать запрос на защищенный URL
  • Сервер отправляет 401
  • клиент создает и сохраняет учетные данные и передает их обратно на сервер
  • сервер проверяет учетные данные, завершает запрос, если он проверен, отправляет еще один 401, если нет.
  • сделать последующий запрос на безопасный URL
  • сервер снова запрашивает авторизацию........

Это работает для одиночных запросов, но если я делаю дополнительные последующие запросы, сервер снова запрашивает авторизацию. Сервер сохранил сеанс для конкретного пользователя, но iPhone по какой-то причине не делает запрос в рамках того же сеанса... Поэтому сервер должен выбрасывать объект аутентификации и создавать новый каждый раз, когда клиент делает запрос на защищенный URL.

Я уверен, что это неправильное поведение.

Если мы посмотрим, как ведет себя браузер в этой ситуации:

  • Браузер запрашивает данные с безопасного URL
  • сервер отправляет 401
  • браузер запрашивает у пользователя учетные данные, сохраняет их, передает на сервер
  • сервер проверяет учетные данные, возвращает данные, если они проверены, и отправляет еще один 401, если нет.
  • последующие запросы, сделанные для защиты URL-адресов, не запрашивают учетные данные, поскольку браузер управляет сеансом.

Я создаю NSURLCredential и сохраняю его в NSURLCrendtialStorage. Затем, когда приложение получает «didReceiveAuthenticationChallenge», я извлекаю учетные данные из хранилища и передаю их обратно, создавая учетные данные, если они не существуют (по первому запросу).

Любая помощь будет принята с благодарностью. Спасибо.


person Jasarien    schedule 08.09.2009    source источник


Ответы (2)


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

Действительно, одна из основных причин использования Digest заключается в том, чтобы не использовать сеансы только для поддержания состояния входа в систему. Сеансы тяжелые и вредят масштабируемости.

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

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

Если у вас есть один и тот же одноразовый номер, используемый в каждом запросе, тогда пользовательский агент будет продолжать использовать его вместе с «ha1» от пользователя/пароля для запроса последующих ресурсов. Это делается упреждающе, поэтому вызов никогда не происходит.

Конечно, использование одного и того же одноразового номера вносит элемент небезопасности, поскольку атаки с повторным воспроизведением становятся тривиальными для любого, кто может прослушивать трафик. Nonces придется менять на регулярной основе.

Следовательно, если вы получаете запрос от пользовательского агента с недопустимым заголовком авторизации, но причина его недействительности заключается в том, что одноразовый номер неправильный (используется просроченный), то в запросе включите «stale=true» (по умолчанию ложный). Это информирует пользовательский агент о том, что ваша причина отклонения заключается в том, что одноразовый номер устарел (конечно, другая информация также может быть неправильной, но это не имеет значения, поскольку вы не собираетесь позволять ей воспроизводиться в любом случае).

При получении такого stale=true пользовательский агент будет знать, что он не авторизован, но вместо повторного запроса пользователя (или создания исключения, если это компонент без пользовательского интерфейса) повторит старые критерии с новым одноразовым номером.

Я не могу сказать, влияет ли что-то из этого на вас, но то, как определяются и сигнализируются nonces и staleness, — это, безусловно, первое, на что я бы обратил внимание.

person Jon Hanna    schedule 18.08.2010
comment
Джасариен, я понимаю, что галочка означает, что это поставило вас на правильный путь? (для моего любопытства и, возможно, для того, чтобы кто-то нашел ту же проблему). - person Jon Hanna; 22.09.2010

Я написал приложение для iPhone с HTTP-аутентификацией и испытал то, что вы описываете. (Мое приложение использует базовую аутентификацию вместо дайджест-аутентификации, но здесь это не имеет большого значения.)

Причина проблемы на стороне iPhone. Сервер должен ответить кодом 401, если iPhone не отправляет учетные данные в заголовке HTTP-запроса. И на самом деле это не так, хотя это легко могло бы быть, если учетные данные хранятся в хранилище учетных данных.

Это странное поведение сильно сказалось на скорости работы приложения, так как каждый запрос вызывал два обращения к серверу вместо одного (первый со статусом 401, второй со статусом 200).

Я решил это, вручную установив учетные данные в заголовке HTTP-запроса:

NSString* credentials = [NSString stringWithFormat: @"%@:%@", usr, pwd];
const char* credentialsChars = [credentials cStringUsingEncoding: NSUTF8StringEncoding];
credentials = [CommunicationUtil stringBase64WithData: (const UInt8*) credentialsChars length: strlen(credentialsChars)];
NSString* authorizationHeader = [NSString stringWithFormat: @"Basic %@", credentials];

NSMutableURLRequest* request =
    [[NSMutableURLRequest alloc] initWithURL: url 
        cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
        timeoutInterval: 15];

    [request setValue: authorizationHeader forHTTPHeaderField: @"Authorization"];

Теперь мое приложение работает очень плавно и очень отзывчиво.

Решение будет выглядеть несколько иначе для дайджест-аутентификации. Но вы получите идею.

person Codo    schedule 11.08.2010
comment
Дайджест-аутентификация будет выглядеть совершенно иначе. Если ASIHTTPRequest уже поддерживает это, я бы использовал это вместо того, чтобы подделывать какую-то криптографию. - person tc.; 13.08.2010
comment
Согласно документам, ASIHTTPRequest делает, поэтому я бы согласился с предложением tc. Это избавит вас от головной боли от необходимости кодировать все это вручную. - person chilitechno.com; 15.08.2010
comment
Я изучил детали дайджест-аутентификации и должен согласиться: я довольно громоздкий в реализации. Вычисление хэшей MD5 — самая простая часть. Обработка всех деталей в заголовке HTTP — сложная часть. Так что да: вероятно, лучше всего попробовать ASIHTTPRequest. - person Codo; 17.08.2010