[Информационная безопасность, Разработка веб-сайтов] Концепт — как усилить защиту паролей «12345» от bruteforce атаки (попытка вторая)

Автор Сообщение
news_bot ®

Стаж: 6 лет 4 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
05-Дек-2020 02:31

Всегда хотелось, чтобы хакер не мог взломать перебором чужой пароль на сайте.
Например, если пользователь похвастается хакеру, что его пароль состоит только из цифр, то скоро пользователь потеряет свой аккаунт.
А как быть, если пользователь сообщил по телефону свой пароль жене, а хакер его услышал?
Что?! Хакер знает пароль? Все, это фиаско. Можно ли помочь такому пользователю усложнить угон его аккаунта? Меня всегда волновал этот вопрос и, кажется, я нашел способ как это сделать. Или переоткрыл его, как это часто бывает. Ведь все уже давно придумано до нас.
Вводная.
  • Пользователь хочет на сайте иметь пароль «12345».
  • Хакер может легко подобрать этот пароль.
  • Но пользователь должен войти, а хакер нет. Даже если логин и пароль хакеру известны.
  • и никаких SMS с секретными кодами и посредниками в виде дополнительных сервисов. Только пользователь и ваш сайт со страницей логина.
  • а еще можно будет сравнительно безопасно в троллейбусе сказать своей жене: «Галя, я на сайте site для нашего логина alice поменял пароль на 123456 — говорят, он более популярный, чем наш 12345». И не бояться, что аккаунт взломают за секунду.

Как работает метод? Вся конкретика — под катом.
Что потребуется?
  • концепт объясняет только метод аутентификации
  • реализация требует хранить только "имя пользователя", "пароль", "соль1" и "соль2". Да, две соли.
  • обойдемся без таблиц логирования и счетчиков в redis
  • не будем вести таблицы с IP-адресами
  • не будем использовать SMS
  • не будем блокировать попытки входа в систему. Как известно из моей прошлой неуспешной попытки, бесполезно блокировать вход — даже если хакер упрется в ограничение по времени, он просто начнет подбирать пароли сразу у нескольких пользователей. Кроме того, от ограничений пострадает и сам пользователь. Не звонить же ему в поддержку, чтобы авторизоваться на вашем сайте с прикольными картинками?
  • пользователь может поменять пароль в любое время и сделать его недействительным на остальных устройствах. Это обычное правило, но мне кажется его стоит упомянуть.
  • можно сделать процесс подбора пароля по словарю более тяжелым для хакера (опционально, будет упомянуто ниже).

Суть метода.
Позволить пользователю иметь пароль «12345», а взлом этого пароля должен быть усложнен. Например, как подбирать пароль, который выглядит, как хэш.
Как?
Представьте, если бы в браузере всегда была уникальная соль, которой можно было солить пароль. Каждому пользователю по соли. Зачем она нужна? Чтобы шифровать. Например, если зашифровать строку «12345» с солью «saltsalt» в argon2id, то получится "$argon2id$v=19$m=16,t=2,p=1$c2FsdHNhbHQ$jX94laSi6vo9AhS+bHwbkg". Поменяй соль — и хэш будет другим. Один алгоритм зашифрует одинаковые пароли по-разному, если использовать разную соль для каждого. Годится.
Но где взять эту соль изначально? Да вот же она сидит перед монитором. Пусть выдавит из себя два-три лишних символа и авторизуется уже наконец по-человечески. Рядом кот бегает? Ну, пусть будет cat. Что такое cat? Это наше секретное слово. Мы его сообщим на сервер при регистрации, а он сгенерирует по этому слову соль. А потом эту соль пришлет нам. Все — соль в браузере есть. Теперь пароль. А пароль тоже шифруем и солим той солью, что прислал сервер.
Теперь мы не шлем «12345». Мы шлем хэш, и так-как у каждого пользователя своя соль, хэш получается разный.
Кажется, брутфорсу сейчас поплохеет: мало того, что придется делать дополнительные вычисления и перебирать длинные строки хэшей аргона вместо простых цифр, так еще и у каждого пользователя будет свой хэш — теперь бесполезно пробовать одну и ту же строку в виде пароля для проверки ее у всех пользователей. Допустим, три пользователя выбрали один и тот же пароль: 12345. Но хэш у них получится разный. Потому что у каждого разная соль.
  • Браузер должен вычислять хэш пароля используя соль, которую ему ранее прислал сервер. Отправлять он должен хэш, а не сам пароль.
  • Сервер присылает соль по секретному слову, которое известно только пользователю. Оно может быть простым. Например — «cat».
  • У каждого пользователя должна быть своя соль.
  • Два пользователя, выбравшие одинаковое секретное слово, должны иметь разную соль.
  • Сервер не должен сообщать было ли использовано правильное секретное слово и верна ли соль для этого пользователя — иначе это будет подбор двух простых паролей вместо одного.
  • Если пользователь меняет секретное слово, меняется и соль.

То есть, для защиты своего простого пароля, пользователь должен придумать еще одно очень простое слово. Он вводит это слово везде, где хочет пройти аутентификацию, а потом потребуется вводить только пароль. Пока он не почистит куки.
  • зашел на сайт
  • ввел логин и секретное слово
  • ввел пароль
  • готово

Пароль и секретное слово могут быть очень простыми. Один или два символа. Например, пароль 12345 и секретное слово 42. И если кто-то еще придумает секретное слово 42, то это будет не страшно.
Как это работает. Пошаговый концепт.
У нас есть следующие элементы:
  • веб-сервер
  • база данных и таблица users:
    • login
    • password_hash
    • salt_unique_for_each_user
    • salt_for_password
  • браузер пользователя
  • браузер хакера
  • страницы логина и регистрации на сайте
  • скрипт, который перехватывает событие submit для формы логина

Далее нам понадобятся два разных алгоритма, которые могут быть реализованы даже на одной шифровальной системе просто с разными параметрами:
  • ALG1 — асимметричный алгоритм шифрования, который генерирует хэш из строки и соли. ALG1(str, salt) = hash1. Этот алгоритм используется только на сервере.
  • ALG2 — асимметричный алгоритм шифрования, который генерирует хэш из строки и соли. ALG2(str, salt) = hash2. Этот алгоритм используется публично и должна быть возможность его реализации на клиенте (в нашем примере на javascript).

Кроме того нам понадобится еще два алгоритма попроще:
  • ALG_SALT — алгоритм, который вычисляет случайную соль в виде строки символов. ALG_SALT() = salt. Этот алгоритм используется только на сервере.
  • ALG_PASS — алгоритм, который генерирует случайный простой пароль. ALG_PASS() = pass. Этот алгоритм используется только на сервере.

События пошагово:
  • Пользователь переходит на страницу регистрации, так-как у него пока нет логина.
  • Сервер показывает форму с двумя полями: логин + простое секретное слово.
  • Пользователь выбирает логин — alice
  • Пользователь выбирает секретное слово — cat
  • Пользователь нажимает кнопку “Отправить”.

Cервер проверяет и удостоверяется, что пользователь alice отсутствует в БД.
Сервер вычисляет следующие значения:
$salt_unique_for_each_user = ALG_SALT(); // строка "saltsalt"

$salt_for_password = ALG1("cat", $salt_unique_for_each_user); // строка "$argon2id$v=19$m=16,t=2,p=1$c2FsdHNhbHQ$jX94laSi6vo9AhS+bHwbkg"

$user_simple_password = ALG_PASS(); // строка "12345"

$user_simple_password_hashed = ALG2($user_simple_password , $salt_for_password); // строка "$argon2id$v=19$m=16,t=2,p=1$JGFyZ29uMmlkJHY9MTkkbT0xNix0PTIscD0xJGMyRnNkSE5oYkhRJGpYOTRsYVNpNnZvOUFoUytiSHdia2c$b+6ROJVsZ62UXA7hEAg0AQ"

Сервер создает в таблице пользователей запись и сохраняет данные:
INSERT INTO `users`
(
login,
password_hashed,
salt_unique_for_each_user,
salt_for_password
)
VALUES
(
"alice",
"$argon2id$v=19$m=16,t=2,p=1$JGFyZ29uMmlkJHY9MTkkbT0xNix0PTIscD0xJGMyRnNkSE5oYkhRJGpYOTRsYVNpNnZvOUFoUytiSHdia2c$b+6ROJVsZ62UXA7hEAg0AQ",
"saltsalt",
"$argon2id$v=19$m=16,t=2,p=1$c2FsdHNhbHQ$jX94laSi6vo9AhS+bHwbkg"
).

Сервер показывает пользователю страницу успеха регистрации с сообщением: «Пользователь alice успешно создан. Используйте временный пароль 12345 для входа.»
Пользователь радостно кричит: “Ура, я зарегистрировался на сайте site под ником alice и мне дали пароль 12345. Какой смешной и простой пароль!“. Но у квартиры пользователя очень плохая звукоизоляция, и его хакер-сосед все услышал.
  • Хакер вбивает адрес сайта в своем браузере.
  • Браузер хакера отсылает пустые куки.
  • Сервер проверяет запрос хакера — есть ли кука “salt”. Не находит ее.
  • Прежде, чем хакер пришлет украденные логин и пароль, браузер должен знать соль, чтобы ей зашифровать пароль.
  • Браузер хакера пока не хранит соль в куке «salt».
  • Сервер присылает форму логина с двумя полями: логин + секретное слово, чтобы дать пользователю возможность получить соль.

Хакер озадачен. Пока оставим его.
  • Пользователь возвращается на страницу логина.
  • Браузер пользователя отсылает пустые куки.
  • Сервер проверяет запрос пользователя — есть ли кука «salt». Не находит ее.
  • Прежде, чем пользователь пришлет логин и пароль, браузер должен знать соль, чтобы ей зашифровать пароль.
  • Браузер пользователя пока не хранит соль в куке «salt».
  • Сервер присылает форму логина с двумя полями: логин + секретное слово, чтобы дать пользователю возможность получить соль.
  • Пользователь вводит login — alice, secret — cat и нажимает кнопку "Отправить".

Сервер получает запрос и видит, что вместо пароля прислали секретное слово.
  • Сервер выбирает запись из базы данных с логином — alice и берет значения `salt_unique_for_each_user` -> $db_salt_unique_for_each_user и `salt_for_password -> $db_salt_for_password`.
  • Сервер делает вычисления схожие с теми, что он делал при регистрации. Вычисляет значение: $salt_for_password = ALG1(«cat», $db_salt_unique_for_each_user).
  • Сервер отсылает значение соли $salt_for_password в ответе пользователю. Эта соль правильная. Если с ее помощью зашифровать пароль 12345, получится хэш, который сейчас хранится в БД. В заголовках ответа от сервера указано — `установить куку salt = $db_salt_for_password`. Также давайте сохраним и логин: `установить куку login = «alice»`.

Пояснение: Сервер никак не уведомляет какая соль была отправлена — правильная или нет. Результат ее использования будет ясен, когда с ней попытаются авторизоваться с правильными логином и паролем.
  • Пользователь получает ответ сервера. Его страница либо перегружается, либо сразу динамически меняется.
  • Браузер пользователя отсылает куки: login = alice, salt = "$argon2id$v=19$m=16,t=2,p=1$c2FsdHNhbHQ$jX94laSi6vo9AhS+bHwbkg".
  • Сервер проверяет запрос пользователя — есть ли кука “salt”. Находит ее.
  • Браузер уже имеет соль, чтобы ей зашифровать пароль.
  • Сервер присылает форму логина с двумя полями: логин (уже имеет значение alice) + пароль.
  • Пользователь вводит свой простой пароль 12345 и нажимает кнопку "Отправить".
  • Браузер перехватывает событие onSubmit.
  • Вычисляет $password_hashed = ALG2(«12345», "$argon2id$v=19$m=16,t=2,p=1$c2FsdHNhbHQ$jX94laSi6vo9AhS+bHwbkg").
  • Отправляет данные «alice»/$argon2id$v=19$m=16,t=2,p=1$JGFyZ29uMmlkJHY9MTkkbT0xNix0PTIscD0xJGMyRnNkSE5oYkhRJGpYOTRsYVNpNnZvOUFoUytiSHdia2c$b+6ROJVsZ62UXA7hEAg0AQ, а сам пароль 12345 никуда не шлет.

Сервер получает запрос на аутентификацию:
  • Данные логин+пароль: «alice»/$password_hashed
  • Идет в БД, достает значение `password_hashed` -> $db_password_hashed.
  • Сравнивает $db_password_hashed === $password_hashed?
  • Хэши совпадают, авторизация успешна.

Примечание: В моем примере сервер сличает хэши напрямую. Но хранить в БД строки, которые по факту уже являются паролями, нельзя. Их можно украсть и потом использовать в форме логина-пароля. Поэтому нужно хэшировать хэши — как бы странно это не звучало. Это значит, что понадобится третья соль. Но хранить ее надо не в БД, а в переменной окружения. Впрочем, это уже детали реализации, которые я пропустил для упрощения.
Тем временем наш хакер решает проверить эту странную форму входа:
  • Хакер вводит login — alice, secret — dog и нажимает кнопку "Отправить".
  • Сервер получает запрос хакера и видит, что вместо пароля прислали секретное слово.
  • Сервер выбирает запись из базы данных с логином — alice и берет значения `salt_unique_for_each_user` -> $db_salt_unique_for_each_user и `salt_for_password` -> $salt_for_password.

  • Сервер вычисляет значение соли и выдает ее, но она неправильная, потому что кодовое слово чужое: $result_fake_salt = ALG1(«dog», $db_salt_unique_for_each_user). Впрочем, сервер об этом тактично умалчивает.

Сервер отсылает вычисленное значение соли обратно в браузер пользователя. В заголовках указано — `установить куку salt = $result_fake_salt`. Также сохраняется и логин: `установить куку login = «alice»`.
Пояснение: Чтобы помочь хакеру в деле нелегкого труда, сервер отправляет ему соль. Но определить со стороны: правильное ли было секретное слово или нет — невозможно.
  • Хакер получает ответ сервера. Его страница либо перегружается, либо сразу динамически меняется.
  • Браузер хакера отсылает куки: login = alice, salt = $result_fake_salt.
  • Сервер проверяет запрос пользователя — есть ли кука «salt». Находит ее.
  • Браузер хакера уже имеет соль, чтобы ей зашифровать пароль.
  • Сервер присылает форму логина с двумя полями: логин (уже имеет значение alice) + пароль.
  • Хакер вводит украденный простой пароль 12345 и нажимает кнопку "Отправить".
  • Браузер перехватывает событие onSubmit.
  • Вычисляет $password_hashed = ALG2(«12345», $result_fake_salt).
  • Отправляет данные «alice»/$password_hashed.

Сервер получает запрос на аутентификацию — «alice»/$password_hashed.
Идет в БД, достает значение `password_hashed` -> $db_password_hashed.
Сравнивает: $password_hashed === $db_password_hashed? Nope.
Хэши этих изначально одинаковых паролей не совпадают. Потому что их солили по-разному.
Хакер не сдается и идет регистрировать другого пользователя на сайте.
Совершенно случайно он вводит то же самое секретное слово, что и пользователь за стеной — cat.
Хакер получает валидную соль для пароля к новому аккаунту, и пробует ее подставить в скрипт для хэширования.
К счастью, генерация соли для паролей использовала вторую соль (`salt_unique_for_each_user`), которая для каждого пользователя генерируется по-новому. Так что разные пользователи даже с одинаковыми паролями и — что самое главное — секретными словами, будут иметь разные соли. И соль пользователя с тем же секретным словом, не совпадет с солью другого. И совпадение паролей тоже не будет являться проблемой.
Теперь, что касается усложнения перебора паролей по словарю. Если мы модифицируем ALG2, который является общим и для сервера и для клиента, и сделаем его трудозатратным, это серьезно осложнит перебор для хакера. Напомню, ALG2 это процесс получения хэша пароля, который отправляется на сервер. На сервере этот хэш уже вычислен и хранится в БД:
  • сервер будет выполнять операцию ALG2 только один раз при записи пароля в БД или смене пароля на новый
  • клиент будет выполнять операцию ALG2 только во время аутентификации (которую нужно не путать с авторизацией). Допустим, клиент ошибся пару раз при вводе пароля — это не страшно.
  • Хакер будет делать это постоянно для каждого пароля, с чем его и можно будет поздравить. Особенно цинично, что будут затрачиваться титанические усилия на пароли типа 123/1234/12345.

На слабых машинах операция может выполняться значительно дольше, чем на быстрых. Это может стать проблемой. Так что можно не делать усложнение алгоритма.
Завершу описание концепта бочкой дегтя:
  • Если пользователь случайно неправильно введет секретное слово, он попадет в ситуацию, когда он не сможет войти по своему паролю. Придется сбросить секретное слово (в нашем случае удалить куки) и послать запрос заново. Это можно реализовать прозрачно по нажатию одной кнопки, но до этого пользователь должен еще догадаться. Можно сбрасывать принудительно при 5 неправильных попытках входа.
  • Два пользователя на одном компьютере вынуждены будут постоянно сбрасывать соль друг друга.
  • Два разных компьютера будут получать одну и ту же соль для пароля
  • Если соль сменится на сервере через один компьютер, другой компьютер со старой солью не будет знать, что ее нужно поменять
  • Можно украсть соль с компьютера и с ее помощью осуществить очень быструю атаку на аккаунт, зная что пароль очень простой.

… и ложкой меда:
  • пользователь может иметь несколько секретных слов для выполнения различных задач. Например, "cat" это зайти на почту, а "termorectal" — это показать фейковую страницу с ничем не примечательными письмами. Конечно, два пароля можно организовать в любой системе. Но второй пароль должен быть таким же сложным, как и первый. Здесь же можно помнить два простых пароля любого вида, используя удобочитаемые слова.
  • Возможна интеграция секретного слова в уже существующие системы аутентификации. Если у пользователя значение в БД `salt_for_password` не пустое, значит, что пользователь придумал секретное слово, и можно применять новый метод аутентификации. В противном случае использовать старый аутентификатор.

===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_informatsionnaja_bezopasnost (Информационная безопасность), #_razrabotka_vebsajtov (Разработка веб-сайтов), #_prostye_paroli (простые пароли), #_bezopasnost (безопасность), #_kontsept (концепт), #_informatsionnaja_bezopasnost (
Информационная безопасность
)
, #_razrabotka_vebsajtov (
Разработка веб-сайтов
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 02-Июл 13:48
Часовой пояс: UTC + 5