Всем привет. Недавно обсуждалась статья об одноразовых паролях. Я бы хотел чуть дополнить ее, потому что нельзя развивать эту тему, забывая о таких не менее важных участках работы с пользователем, как регистрация и смена пароля.
Данный пост будет только предложением в системе авторизации на основе сокетов и пока не может претендовать на реализацию. Хотя я уверен, что подобную вещь уже где-то воплотили, давно до меня, но поразмыслить сообществу над данной темой думаю будет интересно.
Итак, у нас есть ряд задач, которые нужно исполнить:
- Создать механизм автоматической авторизации клиента на сервере.
- Реализовать максимально безопасный механизм передачи пароля во время аутентификации, при котором воровство хэша пароля будет абсолютно бессмысленным.
- Сделать механизм смены пароля безопасным.
- Создать безопасный механизм регистрации клиента.
Данные и условия задачи
У нас есть полностью закрытый сервер, работающий на сокетах/вебсокетах, и есть клиентское приложение, которое может быть поломано и раскрыто. Нам нельзя упираться в какие-то статичные данные в клиентском приложении (ключи, алгоритмы генерации ключей и другие вещи, которые могут быть спокойно предсказаны обычной расшифровкой клиента).
Для максимальной сложности наш клиент пишется на JavaScript в качестве десктопной программы на node-webkit. Т.е., любой желающий может залезть в исходный код клиента и узнать абсолютно всю нужную ему информацию.
1. Механизм автоматической авторизации
Клиенту предоставляется форма авторизации, он вводит свои данные в нее и после нажатия кнопки «Авторизироваться» приложение вытягивает данные логина и пароля. Логин оставляем открытым — это не критическая информация. С паролем начинаем творить небольшие чудеса:
- Хэшируем пароль, чтоб вирус не смог увидеть чистый пользовательский пароль.
- Хэш пароля нужно зашифровать по ключу и сохранить в постоянное хранилище на клиенте. Это делается с целью возможности его повторного использования. Если оставить открытый хэш, то вирус, украв этот хэш, сможет провести авторизацию на сервере, не зная основного пароля. В качестве ключа лучше всего будет взять какую-то системную информацию с компьютера, к примеру mac-адрес сетевухи, производственные номера железа или что-то другое, что может являться постоянным ключом для расшифровки. Это не даст 100% безопасности в случае проникновения вируса, но усложнит работу взломщику.
- При автоматической авторизации зашифрованный хэш будет расшифровываться и солиться некоторыми данными.
Основные проблемы
Увы мы ничего не сможем поделать с кейлогерами и другой насущной заразой, которая будет тырить прямые пароли, но хорошо помещаем злоумшленникам
2. Безопасная авторизация
Итак, у нас есть хэш пароля. Дальше нам нужно произвести подсол этого самого пароля. В связи с тем, что приложение у нас еще и сетевое, то в качестве соли мы будем использовать:
- Хэш пароля
- Внешний IP пользователя
- Временная метка, для подсола пароля
- Логин (не обязательно)
Внешний IP
Начнем с внешнего IP, который мы можем получить легко и просто. На nodeJS это делается следующим образом (исключения и другие вещи упущены).
var socket = require('net').createConnection(80, 'google.com'); // Но лучше направить на наш сервер, так будет надежнее
var outsideCleintIP = socket.address().address;
Одноразовый пароль с текущим временем
Суть одноразовых паролей и так ясна, но мы немного усложним данную систему в связи с распространенной проблемой рассинхронизации времени на сервере и клиенте. Теперь клиент будет запрашивать у сервера текущее время, тем самым открывая сервер, и создавать только временное окно для авторизации. К примеру, мы запросили у сервера текущее время. Сервер отдал текущее время и открыл возможность авторизироваться в течении 30 секунд (к примеру). Если в этот период времени не пришли авторизационные данные, сервер уже не будет давать права авторизоваться до тех пор, пока у него не запросят новое окно.
Мы убиваем сразу 2х зайцев: рассинхронизацию времени и перехват хэша.
Серверная расшифровка
Теперь немного о работе на сервере. Когда к нам приходит хэш, мы должны будем составить такой же идентичный хэш. У нас есть хэш самого пароля (хранится в БД). У нас есть временное окно для авторизации, у нас есть айпишник подключаемого пользователя. Мы можем без проблем составить такой же хэш и провести авторизацию.
Основные проблемы
У злоумышленника будет всего лишь 30 секунд на авторизацию по этому хэшу и из-за того, что мы подсолили наш хэш айпишником, он обязательно должен отправить этот хэш с того же внешнего IP адреса, что очень сильно усложняет ему работу, если он не находится в той же сети с жертвой. Если вор сидит в другом месте и не может выйти в инет с того же айпишника, то ему становится нереально авторизироваться в системе.
Тут у меня уже вопрос к знающим. Может ли злоумышленник перехватить управление потоком связи на себя, чтобы при этом сервер не заметил подмены клиента?
3. Механизм смены пароля
Хоть данное действие и происходит редко, но есть шанс, что новый пароль будет перехвачен именно через его смену. Я долго осматривал данный участок и не смог придумать ничего лучше, чем воспользоваться SSL/TSL соединением для защиты хэша нового пароля.
Можно попробовать сделать механизм смены пароля по СМС. Клиент будет присылать СМС-сообщение на какой-то указанный номер с новым паролем. Если система требует серьезной защиты, то данный механизм можно и использовать.
Основные проблемы
Если не использовать SSL/TSL то новый хэш злоумышленник может и умыкнуть, но воспользовавшись передачей пароля через другие источники связи.
4. Регистрация
Тут такая же проблема, как и со сменной пароля. Нужно как-то передать хэш на сервер, чтоб его не умыкнул злоумышленник.
Можно передать клиенту через СМС/Почту его пароль, который он сможет сменить, основываясь на безопасной смене пароля, или мы снова можем использовать SSL/TSL для защиты.
Основные проблемы
Мы опять уперлись в проблему передачи пароля на сервер, есть только 2 варианта спасения. Это либо шифрованный канал связи, либо альтернативный маршрут передачи пароля на сервер.
Реализация передачи пароля должна происходить в зависимости от сложности задачи. Как известно, если это сложно сломать и результат не будет стоить тех денег, его ломать не будут.
Насущные проблемы
Люди будут упоминать такие вещи как прокси, динамические IP и другие вещи. Сейчас мы разберем их.
1) Динамические IP
Как известно, если IP изменяется, то нарушается соединение с сервером. Оно просто сбрасывается. Нужно снова повторять процедуру авторизации. Вполне реально сделать повторное подключение не заметным для пользователя, реализовав на клиенте повторное подключение и повторно выяснить внешний IP. Так мы будем уверены в том, что клиент сможет без препятствий соединиться с сервером по новой.
2) Прокси
К счастью, прокси никак не смогут нарушить нормальную работу клиента. Если клиент будет пользоваться открытыми проксями, то он подвергает себя опасности перехвата авторизации и будет сам себе злым Буратино.
Хотелось бы, чтоб пользователи хабра добавили сюда еще свои вопросы или проблемы, которые могут возникнуть.
Только для
Данный алгоритм имеет право на жизнь только в системах, где авторизация с сервером происходит по сокетам/вебсокетам. Я еще не смог понять, как можно применить данный алгоритм авторизации к http запросам, потому что мы упираемся в ограничения самого протокола, мы не можем производить общение клиент->сервер->клиент->сервер. Последний узел уже не работает из-за спецификации протокола (хотя может я и не прав).
Продолжение статьи
Если данная статья сможет выдержать критику, я реализую данный механизм на JavaScript (node-webkit) и Python, чтобы можно было уже потыкать данную систему на предмет физических уязвимостей, которые могли быть упущены в данной статье.
Всем спасибо, жду конструктивной критики и замечаний.
Автор: Zerstoren