Дайджест-аутентификация (Digest access authentication)

Ранее я описывал алгоритм авторизации Digest, как самому собрать пакет данных

Для  PHP все оказалось проще,  в  CURL уже есть готовый механизм, который делает 2 запроса для правильной авторизации

Это параметр CURLOPT_HTTPAUTH => CURLAUTH_DIGEST,

 

 

Полностью запрос:


$request =[
    'SOAPAction: "http://yandex.ru/"',
    'Content-Type: text/xml;charset=UTF-8',
];


$options = array(
    CURLOPT_URL            => $url,
    CURLOPT_HEADER         => true,
    CURLOPT_VERBOSE        => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_SSL_VERIFYPEER => false,    // for https
    CURLOPT_USERPWD        => $username . ":" . $password,
    CURLOPT_HTTPAUTH       => CURLAUTH_DIGEST,
    CURLOPT_HTTPHEADER     => $request,
    CURLOPT_POST           => true,

    CURLOPT_POSTFIELDS     =>"xmlRequest=" . $input_xml // если нужно передать xml в теле POST
);

$ch = curl_init();

curl_setopt_array( $ch, $options );

try {
    $raw_response  = curl_exec( $ch );


    // validate CURL status
    if(curl_errno($ch))
        throw new Exception(curl_error($ch), 500);

    // validate HTTP status code (user/password credential issues)
    $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    if ($status_code != 200)
        throw new Exception("Response with Status Code [" . $status_code . "].", 500);

} catch(Exception $ex) {
    if ($ch != null) curl_close($ch);
    throw new Exception($ex);
}

if ($ch != null) curl_close($ch);

echo "raw response: " . $raw_response;



HTTP Digest

Документация по Digest http://www.hardline.ru/4/38/4862/

Проверка Curl

curl -v —digest —user ‘User:Password’ «https://yandex.ru/MessageService/WebService.svc»

 

Система аутентификации на базе протокола HTTP Digest. Усиление модуля.

В статье «Система аутентификации на базе протокола HTTP Basic был рассмотрен алгоритм Basic аутентификации и с помощью него была построена система Basic аутентификации на основе ролей, работающая без специальной настройки IIS сервера и использующая базу данных для хранения учетных записей пользователей.У Basic есть один недостаток, а именно username и password передаются по сети открыто (clear text), base64 кодировка не может считаться защитой.

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

В этой статье будет рассмотрен алгоритм Digest аутентификации, решающей некоторые проблемы, имеющиеся у HTTP Basic Authentication. Например эта схема не передаёт password по сети открытым текстом. Официальное название схемы — «Digest Access Authentication».

Расширим нашу систему из предыдущей статьи.

К преимуществам Digest можно отнести следуещее:

  1. passwords не передаются открыто по сети
  2. способность защиты от повторяющихся атак (monitoring http nc value)
  3. способность создать защиту (monitoring nonce)
    • в определённый промежуток времени
    • от определённого client
    • от определённого request

Один сайт может одновременно использовать несколько систем защиты, например Basic и Digest

Пришло время рассмотреть работу алгоритма Digest аутентификации:

1. Первый запрос от User Agent к Http Server заголовок Authorization пустой — значит server должен возвратить запрос на аутентификацию. Например такой:

2. ответ сервера:

HTTP/1.1 401 Unauthorized
        WWW-Authenticate: Digest
                 realm="testrealm@host.com",
                 qop="auth",
                 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                 opaque="5ccc069c403ebaf9f0171e9517f40e41",
                 algorithm=MD5,
                 stale=false

Разберём заголовок WWW-Authenticate (как вы заметили, он усложнился по сравнению с заголовком Basic):

realm Строка, указывающая юзеру где он и какой пароль вводить например «registered_users@gotham.news.com».
nonce Уникальная строка, которая генерируется на сервере в момент ответа 401. запрещено использовать кавычку, так как внутри заголовка строка в кавычках рекомендуется также закодировать её base64, например time-stamp H(time-stamp «:» ETag «:» private-key)
opaque Строка, которую юзер должен будет вернуть на сервер в неизменённом виде Рекомендуется закодировать base64
stale true/false
Индикатор, который показывает, что если true — запрос был правильный, username-password тоже, nonce неправильный false или любое другое значение или отсутствие stale — неправильные username, password
algorithm optional, MD5 = default
qop указывает «quality of protection».
«auth» указывает authentication,
«auth-int» указывает authentication + integrity protection. могут быть оба через запятую

3. У юзера всплывает модальное окно, предлагающее ввести username и password (обратите внимание, окно отличается от Basic окна). Происходит запрос юзера для аутентификации:

GET ... ... HTTP/1.1
        Authorization: Digest
                 username="Mufasa",
                 realm="testrealm@host.com",
                 nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                 uri="/dir/index.html",
                 qop=auth,
                 nc=00000001,
                 cnonce="0a4f113b",
                 response="6629fae49393a05397450978507c4ef1",
                 opaque="5ccc069c403ebaf9f0171e9517f40e41"

Теперь разберём заголовок Authorization:

username имя юзера
realm см. WWW-Authenticate
qop см. WWW-Authenticate (должно совпадать с одним из списка qop WWW-Authenticate)
algorithm см. WWW-Authenticate (должно совпадать)
opaque см. WWW-Authenticate (должно совпадать)
uri запрос (например страница)
response 32-character строка — именно c её помощью проверяется пароль.
nonce
nc once count — сколько раз был использован текущий nonce
cnonce уникальная строка, посылаемая браузером на сервер

4. Последовательность обработки запроса пользователя

  1. Проверяем, существует ли заголовок Authorization
  2. Проверяем, является ли он Digest
  3. Отсекаем слово Digest
  4. Берём username (обратите внимание — в заголовке отсутствует password — по крайней мере его не видно)
  5. проверяем базу данных с этим юзером, существует ли он — запоминаем его password из базы
  6. проверяем запрос по ролям против страниц с ролями, как мы уже делали в Basic.

Если что-то не так — переходим к пункту 6. Если всё ok — идём дальше (это ещё далеко не всё :))

5. Проверка пароля пользователя

  1. создаём строку A1 вида
    A1 = unq(username) : unq(realm) : passwd
  2. хешируем A1
    HA1 = MD5(A1)
  3. создаём строку A2 вида
    A2 = Http Method «:» digest-uri
  4. хешируем A2
    HA2 = MD5(A2)
  5. создаём строку GENRESPONSE вида
    GENRESPONSE = HA1 «:» nonce «:» nc «:» cnonce «:» qop «:» HA2
  6. хешируем GENRESPONSE
    HGENRESPONSE = MD5(GENRESPONSE)

если HGENRESPONSE равен response в заголовке Authorization запроса юзера и nonce в порядке — всё ok, нет — переходим к пункту 6

6. выдаём состояние ответа сервера 401, поднимающее модальное окно как в пункте 2 иначе говоря формируем WWW-Authenticate по типу Digest