Получение пароля от пользователя и базовая валидация

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

Типичная HTML-разметка:

<form id="loginForm">
  <label for="password">Пароль</label>
  <input type="password" id="password" name="password" autocomplete="current-password" />
  <button type="submit">Войти</button>
</form>

Обработка на клиенте в Jav * aScript:

const form = document.getElementById('loginForm');

form.addEventListener('submit', (event) => {
  event.preventDefault();

  const password = document.getElementById('password').value;

  handlePassword(password);
});

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


Получение пароля на стороне сервера

При архитектуре клиент–сервер пароль обычно отправляется через HTTPS-запрос и принимается сервером. Пример на Node.js с использованием Express:

import express from 'express';

const app = express();

app.use(express.json());

app.post('/login', (req, res) => {
  const password = req.body.password;

  handlePassword(password);

  res.sendStatus(200);
});

Важно учитывать, что пароль приходит в открытом виде внутри защищённого канала. Сервер должен обработать его немедленно и не сохранять в промежуточных структурах без необходимости.


Базовая обработка строки пароля

Перед использованием библиотеки хеширования (например, password-hash) выполняется предварительная проверка. Она не заменяет криптографическую защиту, но отсекает очевидно некорректные значения.

Основные цели базовой обработки:

  • исключение пустых строк;
  • ограничение длины;
  • нормализация входных данных;
  • защита от очевидных ошибок ввода.

Простейшая функция валидации:

function normalizePassword(password) {
  return password.trim();
}

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


Проверка на пустое значение

Одним из самых частых сценариев является отправка пустого пароля. Это необходимо отсеивать до любых дальнейших операций.

function validatePasswordNotEmpty(password) {
  return typeof password === 'string' && password.length > 0;
}

Более строгий вариант:

function validatePasswordNotEmpty(password) {
  return typeof password === 'string' && password.trim().length > 0;
}

Пустые или состоящие только из пробелов строки не должны попадать в систему хранения или проверки.


Ограничение длины пароля

Слишком длинные строки могут использоваться для атак на отказ в обслуживании или просто создавать избыточную нагрузку на систему хеширования.

Рекомендуемые ограничения:

  • минимальная длина: 6–8 символов;
  • максимальная длина: 64–256 символов (в зависимости от системы).

Пример проверки:

function validatePasswordLength(password) {
  const min = 8;
  const max = 128;

  if (password.length < min) return false;
  if (password.length > max) return false;

  return true;
}

Ограничение максимальной длины особенно важно, так как алгоритмы хеширования (bcrypt, argon2 и аналогичные) имеют вычислительную стоимость, пропорциональную размеру входа.


Базовая проверка состава символов

На уровне минимальной безопасности часто вводятся требования к составу пароля. Это не заменяет криптографическую стойкость, но повышает устойчивость к простым атакам перебора.

Пример проверки:

function validatePasswordComposition(password) {
  const hasLetter = /[a-zA-Z]/.test(password);
  const hasNumber = /\d/.test(password);

  return hasLetter && hasNumber;
}

Расширенный вариант:

function validatePasswordStrength(password) {
  const hasLower = /[a-z]/.test(password);
  const hasUpper = /[A-Z]/.test(password);
  const hasDigit = /\d/.test(password);
  const hasSpecial = /[^a-zA-Z0-9]/.test(password);

  return hasLower && hasUpper && hasDigit && hasSpecial;
}

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


Нормализация Unicode и скрытые символы

Пароли могут содержать символы разных языков и кодировок. Это создаёт риск визуально одинаковых, но технически разных строк.

Пример проблемы:

  • pаssword (латинская a)
  • password (кириллическая а)

Для базовой нормализации можно использовать:

function normalizeUnicode(password) {
  return password.normalize('NFKC');
}

Это приводит строку к стандартной форме, уменьшая риск скрытых различий.


Защита от логирования паролей

Даже на этапе получения и валидации критически важно исключить попадание пароля в логи.

Плохая практика:

console.log(password);

Корректный подход:

console.log('password received');

или полное отсутствие логирования значения.


Интеграция с библиотекой password-hash

После первичной валидации пароль может быть передан в библиотеку хеширования. Например, в password-hash:

import passwordHash from 'password-hash';

function hashPassword(password) {
  return passwordHash.generate(password);
}

В этом контексте важно понимать: библиотека не должна получать «грязные» данные. Все проверки выполняются до вызова хеширования.

Пример полного пайплайна:

function processPassword(rawPassword) {
  const password = normalizePassword(rawPassword);

  if (!validatePasswordNotEmpty(password)) {
    throw new Error('Empty password');
  }

  if (!validatePasswordLength(password)) {
    throw new Error('Invalid password length');
  }

  if (!validatePasswordComposition(password)) {
    throw new Error('Weak password format');
  }

  return passwordHash.generate(password);
}

Обработка пароля в памяти приложения

После получения пароль должен существовать в памяти минимальное время. В JavaScript нельзя полностью контролировать очистку памяти, но можно уменьшить время жизни переменной.

Практика:

let password = normalizePassword(inputPassword);

const hashed = passwordHash.generate(password);

password = null;

Это не гарантирует физическое удаление данных из памяти, но снижает вероятность случайного повторного использования.


Типичные ошибки при получении пароля

  • использование GET вместо POST, из-за чего пароль попадает в URL;
  • хранение пароля в localStorage;
  • логирование входящих запросов без фильтрации;
  • отсутствие проверки длины;
  • пропуск нормализации Unicode;
  • выполнение хеширования до валидации.

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


Разделение ответственности при обработке

Корректная архитектура предполагает разделение этапов:

  1. получение строки пароля;
  2. нормализация;
  3. базовая валидация;
  4. бизнес-валидация (политики сложности);
  5. хеширование через password-hash или аналогичную библиотеку.

Такой порядок предотвращает преждевременную обработку некорректных данных и снижает нагрузку на криптографические операции.