Аутентификация

1. Введение:

Аутентификация играет фундаментальную роль в большинстве современных приложений, поэтому она также является одним из самых лакомых кусочков для злоумышленников. В контексте веб-приложений аутентификация традиционно осуществляется путем ввода имени пользователя или электронной почты, а также пароля (который, по здравому смыслу, должен быть секретным для каждого пользователя).

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

2. Общие рекомендации:

2.1 Сообщения об ошибках:

Использование общих и одинаковых сообщений об ошибках играет важную роль в защите от утечек информации в системе аутентификации. Разработчику также следует обратить внимание на использование одинаковых кодов ответов HTTP.

Взгляните на следующий пример:

...
// Validating the existence of a user with the specified email.
const existingUser = await User.findOne({ email });
if (!existingUser) {
    return res
        .status(401)
        .json({ errorMessage: "Invalid email." });
}

// Validating the password attributed to that User object with the passwordHash
// from the database.
const passwordCorrect = await bcrypt.compare(password, existingUser.passwordHash);
if (!passwordCorrect) {
    return res
        .status(401)
        .json({ errorMessage: "Password is invalid for the given email." });
}
...

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

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

Очень хорошей альтернативой может быть:

...
// Validating the existence of a user with the specified email.
const existingUser = await User.findOne({ email });
if (!existingUser) {
    return res
        .status(401)
        .json({ errorMessage: "Invalid email or password." });
}

// Validating the password attributed to that User object with the passwordHash
// from the database.
const passwordCorrect = await bcrypt.compare(password, existingUser.passwordHash);
if (!passwordCorrect) {
    return res
        .status(401)
        .json({ errorMessage: "Invalid email or password." });
}
...

2.2. Имя пользователя и электронная почта:

Убедитесь, что ваши имена пользователей/идентификаторы пользователей не зависят от регистра. Пользователь 'bobi' и пользователь 'Bobi' должны относиться к одной и той же учетной записи. Это можно обеспечить с помощью "нижнего регистра" пользовательского ввода перед сохранением его в базе данных. Можно даже назначить случайно сгенерированное имя пользователя, обеспечив тем самым некую безопасность данных вместо заданных пользователем.

Важно НИКОГДА не разрешать вход в конфиденциальные учетные записи (т.е. внутренние учетные записи компании).

Если вам действительно необходимо публично отображать идентификационные данные пользователя, например, имя пользователя, вы можете создать поле Display Name в его профиле/аккаунте, чтобы НИКОГДА не сливать само имя пользователя.

2.2.1. Проверка электронной почты:

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

Сначала проверив формат письма, а затем просмотрев MX-запись для данного домена, мы можем легко создать очень эффективный валидатор электронной почты. Именно здесь играет свою роль ссылка для проверки электронной почты. Чтобы быть на 100 % уверенным в том, что письмо действительно, разработчик должен отправить одно из таких писем "Verify your account", которое затем должно быть проверено с помощью токена, уникального для каждого вновь созданного аккаунта.

import dns.resolver
import re

def checkFormat(email):
  #email regex 
  regex = '\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b'
  if re.search(regex, email):
    return True
  else:
    return False

def checkEmailValid(email):
  if checkFormat(email):
    domain = email.split("@")[1] # 'bobi.io'
    for _ in dns.resolver.query(domain, 'MX'):
      return True
  return False

email = "vlad@bobi.io"
checkEmailValid(email) # True

2.3. Пароли:

Само собой разумеется, что пароли должны быть СТРОГИМИ. Я предполагаю, что вы уже знакомы с большинством правил хорошей практики использования ПАРОЛЕЙ, однако вот список наиболее важных, которые следует соблюдать:

  • Очень важно установить МИНИМАЛЬНУЮ ДЛИНУ - не менее 8-10 символов. Все, что меньше, считается ненадежным. Кроме того, вы можете установить МАКСИМАЛЬНУЮ ДЛИНУ около 60 символов, поскольку, скорее всего, алгоритм хэширования, который вы будете использовать для безопасного хранения пароля, имеет ограничение около 64 символов.

  • Разрешите использовать любые символы (включая Юникод и пробельные символы).

  • Вы даже можете попробовать использовать API HaveIBeenPwned, чтобы проверить, был ли вводимый пароль взломан или нет.

const hibp = require ('haveibeenpwned')();

hibp.pwnedpasswords.byPassword ('securepassword', (err, count) => {
  if (!count) {
    console.log ('Great! Password is not found.');
  } else {
    console.log ('Oops! Password was found ' + count + ' times!');
  }
});

2.3.1. Забыли пароль?

Как уже говорилось ранее, сообщения об ошибках обеспечивают важный уровень защиты от злоумышленников, который часто упускается из виду. Учитывая это, важно рассмотреть следующие ответные сообщения.

Вместо: "Мы только что отправили вам ссылку для сброса пароля".

Вы можете выбрать следующее: "Если этот адрес электронной почты есть в нашей базе данных, мы отправим вам письмо для сброса пароля".

2.4. CAPTCHA:

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

2.5. Ведение журнала и мониторинг:

Включите протоколирование и мониторинг функций аутентификации для обнаружения атак/неудач в режиме реального времени.

  • Убедитесь, что все сбои регистрируются и проверяются.

  • Обеспечьте регистрацию и проверку всех сбоев паролей.

  • Убедитесь, что все блокировки учетных записей регистрируются и проверяются.

3. Выводы:

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

Last updated