В статье «Система аутентификации на базе протокола HTTP Basic был рассмотрен алгоритм Basic аутентификации и с помощью него была построена система Basic аутентификации на основе ролей, работающая без специальной настройки IIS сервера и использующая базу данных для хранения учетных записей пользователей.У Basic есть один недостаток, а именно username и password передаются по сети открыто (clear text), base64 кодировка не может считаться защитой.
Аутентификация Digest
В этой статье будет рассмотрен алгоритм Digest аутентификации, решающей некоторые проблемы, имеющиеся у HTTP Basic Authentication. Например эта схема не передаёт password по сети открытым текстом. Официальное название схемы — «Digest Access Authentication».
Расширим нашу систему из предыдущей статьи.
К преимуществам Digest можно отнести следуещее:
- passwords не передаются открыто по сети
- способность защиты от повторяющихся атак (monitoring http nc value)
- способность создать защиту (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. Последовательность обработки запроса пользователя
- Проверяем, существует ли заголовок Authorization
- Проверяем, является ли он Digest
- Отсекаем слово Digest
- Берём username (обратите внимание — в заголовке отсутствует password — по крайней мере его не видно)
- проверяем базу данных с этим юзером, существует ли он — запоминаем его password из базы
- проверяем запрос по ролям против страниц с ролями, как мы уже делали в Basic.
Если что-то не так — переходим к пункту 6. Если всё ok — идём дальше (это ещё далеко не всё :))
5. Проверка пароля пользователя
- создаём строку A1 вида
A1 = unq(username) : unq(realm) : passwd
- хешируем A1
HA1 = MD5(A1)
- создаём строку A2 вида
A2 = Http Method «:» digest-uri
- хешируем A2
HA2 = MD5(A2)
- создаём строку GENRESPONSE вида
GENRESPONSE = HA1 «:» nonce «:» nc «:» cnonce «:» qop «:» HA2
- хешируем GENRESPONSE
HGENRESPONSE = MD5(GENRESPONSE)
если HGENRESPONSE равен response в заголовке Authorization запроса юзера и nonce в порядке — всё ok, нет — переходим к пункту 6
6. выдаём состояние ответа сервера 401, поднимающее модальное окно как в пункте 2 иначе говоря формируем WWW-Authenticate по типу Digest |