Аутентификация. Схемы с паролем.
Терминология
Прежде всего, следует внести некоторую определенность в содержание слов Авторизация и Аутентификация, которые очень часто появляются подряд и иногда употребляются одно вместо другого, однако, их значения несколько отличаются. Аутентификация - это проверка того, что пользователь является тем, за кого себя выдаёт. Как правило, это процедура проверки логина и пароля пользователя. В случае успешности такой проверки, пользователю предоставляется допуск к контенту с ограниченным доступом. Информация с ограниченным доступом (также известна под сокращением ИсОД) - это определённые данные в системе, доступ к которым имеют не все. Процедура проверки того, может ли пользователь получить доступ к ИсОД называется Авторизацией.
Например, в некой системе пользователи делятся на администраторов и модераторов. Администраторы имеют права создавать новый контент, в то время как модераторы - вносить изменения в наявный. Для того, чтобы администраторы не отменяли действия модераторов, для них внесение изменений будет невозможным - только создание новых записей. Когда пользователь входит в систему, он вводит логин и пароль и, если он сделал это правильно, попадает в систему. Это означает, что он прошел аутентификацию. Далее пользователь пытается отредактировать определенную запись и в этот момент осуществляется авторизация - проверка возможности для данного пользователя выполнить это действие. Обратите внимание: пользователь может успешно аутентифицироваться (тоесть войти в систему), но получить отказ в авторизации (доступе к определённым функциям системы). Технически возможно и обратное - авторизация проходит только для неаутентифицированних пользователей. Такое бывает крайне редко, но пример привести можно: пусть комментарии к публикации могут оставлять только пользователи-гости, невыполнившие вход в систему. Для тех, кто выполнил вход, доступны расширенные функции обсуждения, а для гостей - только комментарии. В этом случае авторизация есть, а аутентификации - нет.
Обычно авторизация является значительно более простой процедурой, чем аутентификация. Если пользователь вошёл в систему, то системе становится известна его роль (администратор, модератор, посетитель, и т.д.), и по этой роли система может ограничивать доступы к собственным ресурсам. Совсем другое дело с аутентификацией. Главная проблема с ней заключается в том, что аутентификационные данные (логин и пароль) могут быть украдены или подделаны, вследствие чего вход в систему будет предоставлен совершенно не тому пользователю, который был зарегистрирован. По терминологии такая ситуация називается "ошибка второго рода" - предоставление доступа в систему незарегистрированному пользователю. Как очевидно следует из названия, должна существовать "ошибка первого рода". Да, она существует и заключается в том, что зарегистрированный пользователь получает отказ во входе в систему. Необходимость борьбы с ошибками второго рода вынуждает усложнять процедуру аутентификации,- введение альтернативных каналов (многофакторная аутентификация) или исключительно персональных данных (биометрическая аутентификация). Но все эти усложнения как раз и приводят к увеличению ошибок первого рода, когда у пользователя нет под рукой альтернативного устройства (смартфона), или, из-за изменения условий освещения, фотография лица пользователя не воспринимается как аутентичная.
Чрезмерное усложнение схем аутентификации лишь ради того, чтобы её улучшить, обычно имеет противоположный еффект. Пользователю становится труднее войти в систему, а программисту - реализовать алгоритмы аутентификации. Все эти сложности "играют на руку" злоумышленникам, каторые только и ищут недостатки в системе. А чем система сложнее, тем больше обнаруживается таких недостатков. Где-то должна существовать граница адекватной целесообразности, после которой дополнительные меры улучшения начинают приводить также и к ухудшению. Такая граница существует и известна под криптографическим Принципом Керкгоффза. Этот принцип очень стар и появился для военной техники, но, современным языком, он утверждает, что вся надёжность системы должна вмещаться в надёжности пароля. Другими словами, если пароль ненадёжен или ненадёжно хранится, то ни одно усложнение системы не улучшит её защищенность. И наоборот, если пароль надёжен, как и надёжна схема его передачи, то даже несложные алгоритмы будут иметь достаточное качество для наших задач. Это не значит, что подойдёт любой ненадёжный алгоритм аутентификации, но простейшего из надёжных будет абсолютно достаточно.
Добавить соли и перемешать
В схемах аутентификации с паролем есть два наиболее уязвимых аспекта: передача пароля между пользователем и сервером и хранение пароля на сервере. Уязвимость при передаче заключается в том, что протокол НТТР предусматривает полную открытость, тоесть ни одну из его частей нельзя скрыть. Это же касается и пароля, который передаётся от пользователя к системе в составе НТТР пакета. Частично данную проблему решает шифрованный протокол HTTPS, но относительно надёжности такой защиты существует много споров. Детальнее эту проблему мы обсудим в другом разделе. Здесь сосредоточимся на проблеме хранения пароля на сервере системы.
Зачем система хранит пароль? Ответ кажется очевидным - для аутентификации, сравнения пароля, который отправляет пользователь, з тим паролем, который был заложен в систему при регистрации. Какие же проблемы это создаёт? Во-первых, проблему утечки данных. Если систему взломают, то злоумышленники получат доступ к данным сервера, а следовательно, и к паролям пользователей. Во-вторых, проблему несанкционированного доступа. Администраторы системы, которые имеют доступ к её данным, могут получить сведения о любом пароле. Его использование ограничено лишь моральными качествами администратора и лиц, которые могут случайно стать свидетелями его работы. Очевидно, что такая надёжность недопустима.
Первым этапом решения проблемы хранения паролей является их хеширование. Криптографическим хешем (англ. - hash) называется такое преобразование, которое из произвольного объекта получает отпечаток одинакового размера. Тоесть на входе преобразования есть объект любого размера: хоть пустой объект, хоть строка "Привет, Света!", хоть число 42, хоть файл с фильмом - что угодно в цифровом виде. После преобразования (хеширования) будет получен объект фиксированного размера. Как правило, этот размер определяется в битах (например, 128 бит), и представляется в виде шестнадцатиричного числа наподобие B6C628CC2810707ABB825098C3E9F5CC. Хеш-преобразование должно быть однозначным: если входные объекти одинаковы, то и их хеши должны совпадать. Также к крипто-хешам добавляется требование "перемешивания" или "крипто-диффузии", в соответствии с которым хеш объекта должен существенно изменяться при малых изменениях во входящем объекте. Другими словами, если есть два числа "123" и "124", которые отличаются лишь на единицу, то хеш-образы этих двух чисел должны иметь значительно больше отличий. Еще одним требованием к хешам является их необратимость. Не должно существовать ни одной возможности восстановить входящий объект, если известен лишь его хеш. Именно это последнее требование побуждает нас использовать хеш-функции для сохранения паролей.
Существует много разных реализаций хеш-функций. Широко известными являются, например, алгоритмы MD5 (Message Digest - 5) и SHA (Secure Hash Algorithm). Менее известен, но не менее надёжен наш, украинский, стандарт "Купина" (ДСТУ 7564:2014). Можно посвятить много времени проведению сравнительного анализа разных хеш-алгоритмов, однако, во-первых, для задач сохранения паролей они все хорошо подходят, а, во-вторых, наличие нашего стандарта рекомендует использование именно его (хочется сказать, что он является лучшим, но не хочется никого обижать). Например, уникальная возможность Купини - генерировать хеши произвольной длины (в диапазоне от 8 до 1024 бит), например, 100 бит. Большинство других алгоритмов имеют ограниченный перечень длин (128 - 256 - 512 бит). Реализации алгоритма Купина можно посмотреть на нашем репозитории https://github.com/sodes-studio/Kupina
Хотя хеширование паролей значительно повышает надёжность их хранения, остаётся один недостаток, связанный с однозначностью хеш-преобразования. Это означает, что одинаковые пароли будут иметь одинаковый хеш. Взломав один из паролей, злоумышленник также взламывает все те, которые являются одинаковыми по хешу. Более того, можно просчитать хеши наиболее распространённых паролей и искать совпадения с ними в базе данных системы. С целью усложнения таких действий внедряется приём криптографической соли. Солью називают произвольную информацию, которая добавляется к паролю перед вычислением его хеша. Обычно это случайное число, разное для каждого из паролей. Особенности использования соли регулируются стандартом RFC 2898 Password-Based Cryptography. Если упрощённо, то для каждого пароля генерируется соль, она добавляется к паролю (как строка - конкатенацией) и после этого результат хешируется. Получается, что для двух одинаковых паролей сгенерируются разные хеши, потому что к ним была добавлена разная соль. Следует обратить внимание, что соль не защищает пароль от взлома путём перебора разных вариантов, она лишь вынуждает повторять эту процедуру для каждого из паролей. Требование к надёжности пароля остаётся актуальным.
Никому ничего не скажу или схемы без разглашения
Вернёмся к вопросу передачи пароля от компьютера пользователя до сервера системы. Поскольку протокол НТТР является открытым, мы должны предвидеть ситуацию, при которой пакет на запрос аутентификации может быть перехвачен злоумышленником. Если этот пакет содержит пароль, то злоумышленник получит возможность его оттуда изъять. Иногда предлагается хешировать пароль при передаче, но это не улучшает надёжность, поскольку в таком случае злоумышленнику не нужен будет сам пароль, а достаточно будет его хеша, так как именно он принимает участие в аутентификации.
Возникает интересный вопрос: как можно передать сведения о пароле, но, в то же время, не передавать сам пароль? На самом деле существует достаточно много разных протоколов, которые имеют улучшенные показатели безопасности, о большинстве из них можно узнать из книги Брюса Шнайера "Прикладная криптография. Протоколы, алгоритмы и исходный код на C". Среди них особое место занимают протоколы без разглашения (или протоколы с нулевым разглашением - Zero Knowledge Protocols). Если упрощенно, то суть таких протоколов заключается в том, что сервер формирует определённые вопросы, ответ на которые можно дать только зная пароль. В то же время данные о самом пароле не разглашаются.
Как уже становится понятно из предыдущего раздела, в качестве таких вопросов можно использовать соль и перемешивание (хеш-преобразование). Сервер генерирует соль и отправляет её пользователю. Пользователь добавляет соль к паролю и хеширует результат. Этот результат отправляется серверу, на котором выполняется та же самая процедура с сохранённым паролем. Если результаты совпадают, значит пароль бул введён правильно. На новый запрос будет отправлена новая соль, поэтому предыдущие данные уже не являются валидными и, если злоумышленник перехватил один из пакетов, второй раз он ему не поможет.
Более доскональная схема может быть создана с использованием принципа электронной цифровой подписи (ЭЦП). Её содержание очень близко к приведённой выше идее, однако используются более сложные и надёжные алгоритмы "перемешивания". Сервер так же само генерирует случайную соль и передаёт её пользователю. Пользователь подписывает эту соль, используя свой пароль как закрытый ключ подписи. Открытый ключ, сгенерированный при регистрации с того же пароля, хранится на сервере вместо хеша пароля и принимает участие в проверке подписи.
Оставить комментарий
Имя (отображается)E-mail (не отображается)
Несомненно лучший дизайн сайта который я видел.