| <назад> |
Безопасность при аутентификации (идентификации) пользователя в приложениях |
При разработке какой-либо
многопользовательской программы одна из
основных её составляющих - система
аутентификации пользователя. Обычно
особого внимания этой сисеме не уделяется,
так как есть более важные задачи, поэтому
система безопасности остаётся в "зачаточном"
состоянии (о чём менеджеры клиенту, конечно
же, не рассказывают).
В общем случае система аутентификации - это
комплекс, состоящий из следующих основных
элементов:
- Операционная система
- Система защиты Приложения
- Сетевая и Антивирусная защита
- Организационные меры
Нас интересует "наша" часть - система
защиты приложения.
Начнём с общих (иногда не сразу очевидных)
моментов:
1. Системный администратор имеет доступ
ко всему. (Такая у него специфика работы).
2. Учётная запись пользователя - его
частная собственность, войти в систему под
учётной записью пользователя должен иметь
возможность только сам пользователь, в не
зависимости от прав. Пароль пользователя
или другой идентифицирующий его ключ -
основа учётной записи, его не должен знать
никто, даже "вездесущий" администратор.
3. Пользователь совершает какие-либо
действия в приложении только под своей
учётной записью, что отображается во
внутренних журналах приложения, таким
образом, в случае ошибок или противоправных
действий пользователя можно утверждать,
что эти действия совершил именно он.
Выводы: если пользователь (в частном
случае администратор) может работать в
системе под чужой учётной записью, он может
совершить действия, ответственым за
которые будет хозяин учётной записи.
Варианты: а) хозяин учётной записи сам
сообщил ключ (пароль) другому пользователю.
Таким образом, сознательно или нет, он взял
на себя ответственность за чужие действия в
программе. Данный вопрос решается
организационными мерами и здесь не
рассматривается.
б) чужой ключ (пароль) был получен помимо
желания хозяина, который при этом остаётся
в неведении. Получить пароль можно
различными способами:
- подсмотреть на бумажке, куда пользователь
его записал (в записной книжке, в телефоне,
подсмотреть в момент набора пароля
пользователем и т.п.). Организационная
ошибка.
- заполучить пароль с помощью вирусов,
троянцев (кейлоггеров). Скопировать ключ с
носителя с помощью удалённого доступа. Ошибки
системного администрирования и
антивирусной безопасности.
- получить пароль (ключ) пользователя
непосредственно из приложения (из файлов,
базы данных и т.п.). Ошибки разработчиков.
Наша задача - минимизировать
возможность получения подобных данных из
приложения.
В "Общих моментах" кажущееся
противоречие между п.1 и п.2 устраняется
шифрованием и его вариациями. Для
разработчика, не имевшего дело с
шифрованием, есть полный интернет
всевозможных алгоритмов, от простейших до
вошедших в стандарты. Но, учитывая
обширность темы, некоторые "тонкие"
моменты упускаются.
Пример: пароль пользователя шифруем и
сохраняем в файле данных приложения (или
базе данных). Вроде бы всё хорошо, пароль
зашифрован, просто так не подсмотришь.
Тонкие моменты
1. (основной) используется постоянный
ключ шифрования. Приложению приходится
сохранять где-либо ключ чтоб расшифровать
пароль и сверить его с введённым
пользователем.
2. Алгоритм шифрования. Простой алгоритм
может быть вскрыт даже наугад, а чуть более
сложные - перебором. Мощность современных
машин позволяет перебирать сотни тысяч -
миллионы вариантов в простых алгоритмах.
Основные алгоритмические ошибки:
1) по криптотексту (в данном случае -
зашифрованному паролю) можно определить
размер открытого текста. Для крупных блоков
открытого текста это не важно, но для пароля
его длинна - это часть информации о нём.
2) в пароле можно подбирать каждый символ
отдельно от других. Подсмотрев (или
предположив) какой-либо нажатый
пользователем символ (или несколько) можно
начать подбор всего пароля.
Определив алгоритм шифрования
пользователь получает доступ ко всем
учётным записям.
3. При сетевой работе (подключение пользователя к серверу) пароль, введённый пользователем, передаётся на сервер в открытом виде, где сравнивается с расшифрованным. Таким образом можно перехватить пароль пользователя дампом сетевых пакетов.
4. Полученный пароль пользователя может совпадать (и часто совпадает) с паролем к другим системам (приложениям). Таким образом, под угрозой оказывается не только приложение, из которого пароль извлечён.
5. Разработчику известен ключ и
алгоритм шифрования, таким образом,
разработчик может узнать пароль любого
пользователя.
Решение
Первый вопрос: а зачем приложению
знать пароль пользователя? Если у
разработчика такого вопроса не возникло,
значит он ничего не читал про идентификацию
пользователей, например, в операционных
системах.
Ответ: приложению не нужен пароль.
Ему нужно быть уверенным, что учётная
запись принадлежит именно пользователю,
логин (имя) которого использован. Ещё в
системе UNIX для этого используется
гениальное по простоте решение - система
хранит Хэш пароля пользователя. Хэш -
односторонняя и однозначная функция. Она не
позволяет никаким образом расшифровать
пароль, тем не менее каждому паролю
соответствует уникальное значение хэша.
Таким образом, простейший вариант
идентификации пользователя:
1. клиентская часть:
- пользователь вводит логин и пароль
- клиентская часть приложения вычисляет хэш-функцию
введённых данных и отправляет на сервер
пару логин (имя) + хэш
2. сервер сравнивает присланный хэш и хэш
сохранённый в карточке пользователя и
принимает решение об идентичности.
В операционных системах используются более
сложные, многоступенчатые схемы,
призванные защитить от перехвата сетевого
траффика.
Но даже эта простейшая схема уже избегает
всех недостатков, описанных в "Тонких
моментах".
Вскрыть хорошо написанную хэш-функцию (примеров
в интернете огромное множество, есть
стандартные, проверенные временем и т.п.)
можно только полным перебором вариантов,
причём пароль нельзя перебирать по частям -
хэш-функция зависит от каждого символа
пароля.
Чтоб затруднить перебор вариантов, следует
выбирать длинну хэш-значения достаточно
большой. Таким образом будет значительно
увеличено количество операций на его
вычисление, что будет не заметно при
разовых вычислениях в приложении, но очень
повлияет на скорость перебора вариантов
при подборе. На современных системах
хранение для каждого пользователя даже
килобайтного хэша - это пренебрежительно
малые затраты. Хотя слишком увлекатся не
стоит, просто нет смысла.
Можно предположить, что хэш длинной 100 - 1000
байт будет достаточен для большинства
приложений.
Практические моменты
1. Хэш-функцию можно реализовать на
основе алгоритма шифрования. Достаточно,
чтоб хешируемый пароль содержался как в
ключе шифрования, так и в самом шифротексте,
т.е. если есть функция вида Шифр(ключ-шифрования,
текст-который-надо-зашифровать)
то Хэш = Шифр(пароль, пароль)
получим криптотекст, для расшифровки
которого надо знать сам открытый текст,
который хорошо подходит в качестве хэш-функции.
Тонкий момент: у разных пользователей с
одинаковыми паролями будет одинаковый хэш.
Чтоб этого избежать, используются
уникальные для пользователя модификаторы.
Например, в UNIX использовались два случайно
сгенерированных байта, которые хранились
вместе с хэшем.
Можно пойти по примеру UNIX, а можно
использовать что-либо уникальное для
пользователя, например, его логин или
системный номер.
Например:
Хэш = Шифр(логин+пароль, логин+пароль)
или
Хэш = Шифр(номер_пользователя+пароль, номер_пользователя+пароль)
Варианты (главное, чтоб пароль участвовал в
как в ключе так и в шифруемом тексте):
Хэш = Шифр(пароль, логин+пароль)
Хэш = Шифр(логин+пароль, пароль)
2. Вопрос: А если надо сохранить в
приложении именно логин и пароль, а не их
хеш? например, для доступа к серверу баз
данных?
Ответ: Тогда шифровать.
Помнить следующие моменты:
- приложение расшифровывает данные один раз,
переборщик - миллионы раз. Не жадничайте на
длинну криптотекста, храните логин с
паролем (десятки байт) в файлах, длинной в
килобайты.
- лучше, конечно, шифровать нефиксированным
ключём, например, пользователь вводит какой-либо
пароль, а уже этим паролем расшифровывается
истинный логин и пароль для доступа к
серверу баз данных. Таким образом,
пользователь может и не знать паролей
доступа к серверу, что исключает
возможность работы с БД в обход приложения.
Пример аутентификации для
двухуровневой системы Приложение - сервер
БД
Сервер БД: имеется одна или несколько
заведённых учётных записей,
предназначенных для подключения к серверу
приложения
Приложение: имеется большое количество
пользователей. Они работают через
одинаковые учётные записи сервера БД, но
разные учётные записи Приложения.
Идентификация и начало работы:
1. Старт приложения: пользователь вводит
Логин и Пароль учётной записи Приложения.
2. Приложение по Логину считывает профайл
пользователя, в котором содержится
информация учётной записи БД (его можно
хранить как в общедоступном файле, так и в
БД с открытым доступом), расшифровывает его
по Логин+Пароль пользователя и получает
Логин + Пароль учётной записи сервера БД.
3. Приложение соединяется с БД по полученым
данным.
4. Приложение считывает профиль
пользователя, выполняет дополнительные
проверки, например, перепроверяет
действительность пары Логин+Пароль
пользователя по хэшу.
5. Приложение принимает решение о
успешности логина пользователя.
Пример шифрования и хэширования можно
скачать
тут (D7)
Вопросы можно попытаться задать: tonnys(а)ukr.net
© Tonny S. 2006