Что такое хеширование
Отпечаток (хэш) — это результат работы функции хэширования, которая вернёт для любого значения строку фиксированной длины.Используя специальный математический алгоритм, такая функция умеет преобразовывать любую переданную информацию к строке фиксированной длины (например, 32 или 64 символа).
Причём любому массиву информации, будь это все статьи из Википедии, или одно слово, всегда будет соответствовать уникальный отпечаток. Повторный вызов функции для одного и того же исходника всегда возвращает один и тот же хэш.Обратная операция (получить из отпечатка оригинал) невозможна.
Возьмём простой пример. У нас есть информация, для которой мы хотим получить отпечаток. Пусть такой информацией будет следующая строка:
«Я знаю только то, что ничего не знаю, но другие не знают и этого»
Результат обработки этой строки хэширующей функцией SHA-1 будет таким:6b3cb0df50fe814dee886b4e1c747dda6ce88b37
Хэширующие функции часто используются для контроля целостности информации при передачи по сети. Например, чтобы убедиться в том, что загруженный файл не был повреждён, достаточно получить его хэш и сравнить данный хэш с опубликованным на сайте. Если в файле поменялся хоть один байт, то эти отпечатки будут совершенно разными.Нам же функции хэширования помогут для сравнения паролей.
Cookies
Cookies (в дальнейшем просто «куки») — небольшие фрагменты данных, которые веб-сервер отправляет браузеру.Браузер сохраняет их у себя, а при следующем посещении веб-страницы отправляет обратно. Благодаря этому, веб-сервер сможет узнать своего «старого» посетителя, идентифицировать его.
Form based аутентификации
Про аутентификацию через
HTML
форму вы можете прочитать в статье
«PHP Аутентификация через форму»
Oauth = fetch request token redirect to authorization fetch access token call api
В примере с GMail мы использовали 2 вида удаленных вызовов: а) редирект через браузер; б) обращение к API изнутри скрипта.
И мы вскрыли ряд проблем с безопасностью, что наводит на мысль: вызовов должно быть больше. Так и происходит в OAuth: добавляются еще промежуточные запросы от скрипта Consumer-а к Provider-у, оперирующие токенами. Давайте их рассмотрим.
Аутентификация
Представим интернет-магазин. Все его страницы можно разделить на две половины: публичные и приватные.К публичным относятся страницы каталога, информации о товаре, условия доставки и так далее. К приватным — корзина покупок, история заказов.Совершенно очевидно, что корзина покупок у каждого покупателя должна быть своя, а иметь к ней доступ должен только сам владелец и никто больше.
Процедура проверки возможности доступа пользователя к определенной части сайта и называется аутентификацией.Весь процесс аутентификации всегда состоит из нескольких шагов:
Валидация при регистрации
При регистрации обязательно следует проверять данные, которые пользователь ввел в поля.
Выход с сайта
Если на сайте есть вход, то должен быть и выход. Таким выходом будет специальный сценарий, который очистит сессию и переадресует на главную страницу.Чтобы очистить сессию, достаточно очистить массив $_SESSION:$_SESSION = []
Грабли вторые: «подслушивание» секретного ключа
Предположим, мы как-то защитили retpath, и он теперь может указывать только на наш сайт. Но проблема с параметром secret остается.
Secret можно подсмотреть из-за спины или перехватить методом прослушивания WiFi-трафика. Или на вашем сайте когда-нибудь найдется XSS-уязвимость, позволяющая «утянуть» секретный ключ. Имея значение secret, злоумышленник сможет прочитать вашу адресную книгу. Значит, нужно обезопасить secret от перехвата (в идеале — вообще его не передавать через URL).
Грабли первые: подмена адреса возврата retpath
Ну конечно же, вы догадались, что злоумышленник на своем сайте первым делом разместит ссылку
и заставит вас на нее кликнуть. В результате он получит секретный ключ, который вернул GMail, а значит, и ваши контакты:
Грабли третьи: слишком много редиректов
Если для каждого вызова API требуется разный secret, то нам придется организовывать столько редиректов на сайт Service Provider-а, сколько у нас вызовов. При интенсивном использовании API это работает очень медленно, да и неудобно порядком…
Грабли четвертые: плохая идентификация consumer-а
GMail, конечно, хочет знать, кто пользуется его API. Разрешить доступ одним сайтам и запретить — другим… Значит, при формировании запроса в форме импорта контактов Consumer (сайт) должен «представляться» Service Provider-у (GMail-у). В нашем случае эту функцию частично выполняет retpath (имя сайта в нем), но данный способ не универсален, т.к. механизм «представления» должен быть задейстсован еще и при вызове API-методов.
Дайджест-аутентификация (digest)
Дайджест-аутентификация – более безопасная и надежная альтернатива простой, но небезопасной базовой аутентификации.
Итак, как это работает?
Дайджест-аутентификация использует криптографическое хеширование MD5 в сочетании с использованием одноразовых номеров. Таким образом, информация о пароле скрывается для предотвращения различных видов злонамеренных атак.
Это может показаться немного сложным, но станет яснее, когда вы увидите, как это работает на простом примере.
Демонстрация работы oauth на примере простого приложения
Чтобы «вживую пощупать» OAuth, нам потребуются две вещи:
Добавляем соль
На самом деле md5 не дает полной защиты от расшифровки.
В случае простого или очень популярного пароля хеш элементарно расшифровывается с помощью гугла.
Ещё немного терминологии
Следует различать два термина: аутентификация и авторизация.
Как прочитать куки
В PHP максимально упрощён процесс чтения информации из кукисов. Все переданные сервером куки становятся автоматически доступны в специальном глобальном массиве $_COOKIEТак, чтобы получить содержимое куки с именем «visit_count», достаточно обратиться к одноимённому элементу массива $_COOKIE, например вот так:
Обратите внимание: установив в сценарии куку через setcookie, прочитать её можно будет только при следующем посещении страницы.
Как установить куки: функция setcookie
Являясь серверным языком программирования, PHP может управлять заголовками, которые отправляет сервер, а значит может устанавливать и читать куки.Чтобы добавить новую куку, необходимо вначале определиться со следующими критериями:
- Название этой куки (может состоять только из символов латинского алфавита и цифр);
- Значение, которое предполагается хранить;
- Срок жизни куки — это обязательное условие.
За установку куки в PHP отвечает функция setcookie, ей нужно передать как минимум три параметра, описанных выше. Пример:
Обратите внимание, что срок жизни указывается в относительной величине. В этом примере кука будет существовать ровно 30 дней с момента установки.
Как устроены сессии
- PHP генерирует уникальный идентификатор браузера.
- Идентификатор сохраняется в специальную куку и передаётся с каждым запросом.
- Все данные, которые записываются в сессию, PHP автоматически сохраняет в специальном файле на сервере.
Благодаря существованию сессий в PHP, мы можем сохранять любые данные так же просто, как присваивать их переменным. Но, в отличие от переменных, эти данные будут сохраняться для пользователя между запросами в пределах сеанса.
Перепишем сценарий для подсчета посещений, но теперь используем сессии:
Краткое резюме
Как видите, дайджест-аутентификацию сложнее понять и реализовать.
Она также более безопасна, чем обычная проверка подлинности, но все же уязвима для атаки “человек посередине”. RFC 2617 рекомендует использовать вместо этого дайджест-аутентификацию базовой аутентификации, поскольку она устраняет некоторые ее недостатки.
Итак, вкратце дайджест-аутентификация:
- Не отправляет пароли в виде обычного текста по сети.
- Предотвращает повторные атаки
- Защита от подделки сообщений
Некоторые из слабых мест:
- Уязвимость к атаке “человек посередине”.
- Многие параметры безопасности не требуются, и поэтому дайджест-проверка подлинности будет менее безопасной, если не задана.
- Предотвращает использование надежных алгоритмов хеширования паролей при хранении паролей.
В связи с этим дайджест-аутентификация пока не получила широкого распространения. Базовая аутентификация намного проще и в сочетании с SSL еще более безопасна, чем дайджест-аутентификация.
Об изобретении велосипедов
Хороший способ понять что-то — сделать это самому, наступив попутно на все разложенные грабли. Сейчас мы будем изобретать велосипед: попробуем представить, как бы мы решали задачу взаимодействия Consumer-а и Service Provider-а без всяких стандартизированных протоколов.
Во-первых, напишем саму форму импорта контактов с GMail:
Далее попросим разработчиков GMail сделать так, чтобы при переходе пользователя по URI /auth.php ему бы выдавалась форма авторизации (в нашем веломире GMail написан на PHP). После успешного ввода пароля пользователь должен редиректиться на сайт, чей URL указан в параметре retpath.
Итак, после ввода пароля пользователь будет возвращаться к нам на сайт по следующему адресу:
А мы из скрипта /import.php обратимся к API GMail, передадим в него ключ Y49xdN0Zo2B5v0RR и загрузим контакты:
Ну что же, давайте теперь считать грабли (потому что шишки считать будет уже поздно).
Области безопасности
Области безопасности предоставляют способ связать разные права доступа к разным группам ресурсов на сервере. Они называются защитными пространствами.
Фактически это означает, что в зависимости от ресурса, к которому вы хотите получить доступ, вам может потребоваться ввести разные учетные данные.
На сервере может быть несколько областей. Например, один может быть для статистической информации веб-сайта, доступ к которой имеют только администраторы веб-сайта. Другой вариант – изображения веб-сайтов, к которым другие пользователи могут получить доступ и загрузить изображения.
План этой статьи
Прежде чем перейти к основной части, давайте посмотрим, как именно мы будем двигаться.
- Рассмотрим проблемы, которые возникают при «ручной» реализации кросс-авторизации.
- Поговорим о том, что такое «приложение» и кто такой Consumer.
- Коснемся основ криптографии.
- Обозначим демо-приложение, которое мы будем писать в этой статье.
- Определимся с тестовым сервером OAuth, на котором будем экспериментировать.
- Пройдем по всем шагам протокола OAuth и приведем исходники скрипта.
Подробное объяснение
Давайте определим это:
Атрибут ответа рассчитывается следующим образом:
Полезные ссылки по oauth
См. также:
Предопределённые php переменные
php.net
Приложение = consumer доступ к api
При работе с OAuth важно, что термин Consumer не ограничивается смыслом «сайт». Consumer — это некоторое
приложение
, а уж где оно размещено, не так важно. Примеры Consumer-ов из реальной жизни:
Но из одного OAuth каши не сваришь. Действительно, все, что дает OAuth, — это возможность авторизоваться на удаленном сервисе (Service Provider) и делать автризованные запросы к API. Не важно, как устроен этот API: это может быть чистый SOAP, REST-подход т. д. Главное, чтобы каждый метод API принимал на вход специальные параметры, передаваемые согласно протоколу OAuth.
Пример
Задача очень проста: сохранять и показывать посетителю страницы, сколько раз он посетил наш сайт. Для этого будем сохранять количество посещений в отдельной куке, увеличивая значения на единицу при каждой загрузке страницы.
Пример digest authentication
1. Пользовательский клиент -> Сервер
Реализация регистрации пользователя
Вернёмся к форме регистрации.Выше говорилось, что вместо пароля лучше хранить его отпечаток. Для получения отпечатка существуют множество хэшируюших функций. К счастью, нам не надо разбираться в их многообразии, потому что в PHP есть стандартная функция, которая делает ровно то, что нужно.Вот пример как из пароля получить отпечаток, пригодный для хранения в базе:
$passwordHash = password_hash('iloveponies', PASSWORD_DEFAULT);
Вызов этой функции вернёт следующую строку: $2y$10$a1pgDBerBsqD24D7qOAsl.QFTvwxQQGe4r1oWhD7f9yEnDvx4i7tWИменно это значение и следует хранить в БД, вместо пароля.
Регистрация на сайте
Перед тем, как мы начнем добавлять аутентификацию на своем сайте, придётся добавить форму для регистрации нового аккаунта.Аккаунт — это учётная запись пользователя.Чтобы завести аккаунт, требуется пройти регистрацию — это заполнение специальной формы, где пользователь указывает свою почту, пароль, и, возможно, дополнительную информацию.
Регистрация приложения и его параметры
Поговорим о том, откуда появляются приложения и как Service Provider о них узнает. Все достаточно просто: Service Provider имеет специальную форму регистрации приложений, которой может воспользоваться любой желающий. Вот пример такой формы:
После регистрации приложения вам выдается 5 параметров, которые требуются для работы с OAuth. Вот как они могут выглядеть:
Связанные с аутентификацией заголовки запросов/ответов
Сервер выдает запрос, используя заголовок ответа WWW-Authenticate. Он содержит информацию о протоколе и области безопасности.
После того, как клиент вводит учетные данные, запрос отправляется снова. На этот раз с заголовком авторизации, содержащим алгоритм и комбинацию имени пользователя и пароля.
Если учетные данные верны, сервер возвращает ответ и дополнительную информацию в необязательном заголовке ответа Authentication-Info.
Сессии
Мы уже умеем сохранять информацию для пользователя между посещениями страницы с помощью кук. Но зачем же нам ещё сессии, и для чего они нужны?Сессии, они же сеансы, это, по сути, просто удобная обёртка над куками. Они также позволяют хранить данные, релевантные пользователю, но с некоторыми отличиями и ограничениями:
- Данные хранятся не произвольное время, а только до закрытия вкладки с веб-страницей.
- Чтобы сессии работали, в начале каждого сценария надо вызывать функцию
session_start()
. - Доступный объём для хранения информации намного больше.
Запись и чтение информации при использовании сессий выглядит просто как работа со специальным массивом $_SESSION.
Собираем всё вместе
Теперь, научившись устанавливать и читать куки, напишем полноценный сценарий, который будет считать и выводить количество посещений страницы пользователем:
Сообщение = документ цифровая подпись
«Цифровая подпись» — звучит страшно, но на самом деле это достаточно очевидная вещь. Когда вы ручкой подписываетесь на каком-либо документе, вы удостоверяете, что этот документ написан вами, а не кем-то другим. Ваша подпись как бы «добавляется» к документу и идет с ним в «одном комплекте».
Аналогично, цифровая подпись добавляется к некоторому блоку данных, удостоверяя: тот, кто сформировал эти данные, не выдает себя за другого. Цифровая подпись не шифрует документ, она лишь гарантирует его подлинность! Поставить подпись позволяет тот самый Shared Secret, который известен получателю и отправителю, но более никому.
Как это работает? Пусть наш $sharedSecret = 529AeGWg, и мы сообщили его шепотом на ухо принимающей стороне. Мы хотим передать сообщение «Мой телефон 1234567» с гарантированной защитой от фальсификации злоумышленником.
- Consumer добавляет цифровую подпись к сообщению, в общем виде —
$transfer = $message . "-" . md5($message . $sharedSecret); // $transfer = "Мой телефон 1234567" . "-" . md5("Мой телефон 1234567" . "529AeGWg")
- Service Provider принимает данные, разбивает их обратно на 2 части — $message и $signature — и проделывает точно такую же операцию:
$signatureToMatch = md5($message . $sharedSecret); // $signatureToMatch = md5("Мой телефон 1234567" . "529AeGWg");
Дальше остается только сравнить получившееся значение $signatureToMatch с тем, что было в полученных данных $signature и рапортовать о подделке, если значения не совпали.
Структура аутентификации запроса/ответа
Когда кто-то отправляет запрос, вместо того, чтобы немедленно ответить на него, сервер отправляет запрос клиенту. Он требует от пользователя предоставить подтверждение личности, введя секретную информацию (имя пользователя и пароль).
После этого запрос повторяется с использованием предоставленных учетных данных, и, если они верны, пользователь получает ожидаемый ответ. Если учетные данные неверны, сервер может повторно отправить запрос или просто отправить сообщение об ошибке.
Фундамент oauth
Примечательно, что «подводных граблей» осталось еще много. Я не буду их здесь описывать, потому что эти грабли лежат в Марианской впадине (глубоко, 10920 м). На описание уязвимостей пришлось бы потратить с десяток страниц. Так что я сразу перейду к описанию OAuth, где все проблемы уже решены.
Хранение паролей
Пароль пользователя — это секретный набор символов, который используется в дальнейшем в ходе аутентификации. Зная пароль пользователя, злоумышленник может войти на сайт под его именем. По этой причине пароль нельзя хранить в базе в открытом виде. Ведь если информацию из БД сайта украдут, то
данные всех пользователей станут скомпрометированными.Вместо самого пароля, в базе будут храниться их отпечатки — хэши.
Чем oauth отличается от openid?
OAuth часто называют «протоколом для роботов», в отличие от OpenID — «протокола для пользователей». Не путайте их!
Конец скрипта: вывод виджета
Окончание скрипта должно быть понятно и без подробных разъяснений.