Даже в небольших проектах защита данных пользователей — не опция, а обязанность. И чем раньше разработчик научится работать с паролями правильно, тем устойчивее будет его приложение — независимо от масштаба и задач. Этот материал подойдёт начинающим и тем, кто хочет обновить свои знания по работе с авторизацией в Node.js.
Если вы разрабатываете и размещаете собственные проекты, обратите внимание на аренду выделенных серверов и VPS — это надёжное решение для тех, кто хочет полный контроль над окружением и безопасность данных.
Почему важно правильно хранить пароли в JavaScript
Защита паролей веб-сайтов — важный навык, которым должен обладать любой разработчик. JavaScript предоставляет возможность обеспечить безопасное хранение и обработку паролей или других конфиденциальных данных с помощью алгоритмов хеширования, предоставляемых модулем BcryptJS.
Хранение паролей — одна из самых уязвимых точек в веб-разработке. Ошибка в реализации авторизации может стоить дорого: от утечек данных до репутационных потерь. Особенно это актуально в эпоху регулярных взломов, брутфорс-атак и массовых утечек баз данных.
Обсудим, как безопасно обрабатывать пароли в JavaScript с помощью библиотеки BcryptJS — популярной реализации алгоритма bcrypt, полностью написанной на чистом JavaScript. Он идеально подходит для Node.js-приложений, не требует нативной сборки и прост в использовании. Вы узнаете, как устроены хеширование и соление паролей, зачем они нужны и как реализовать регистрацию и вход с использованием Express и MongoDB. Пошаговое руководство поможет настроить надёжную систему хранения паролей, в которой вместо открытого текста в базу попадает безопасный хеш.
Где применяют MongoDB
- Фронтенд и фулстек-разработчики используют MongoDB в связке с Express и React/Vue в MERN/MEVN-стеке.
- Стартапы и небольшие команды ценят простоту и гибкость документа-ориентированной модели.
- Фриланс и агентства часто выбирают MongoDB в проектах, где не нужен сложный SQL-слой и важна скорость разработки.
- Образование и курсы. MongoDB входит в программы обучения по JavaScript и Node.js.
- MongoDB часто используют в Open Source и pet-проектах в качестве базы «по умолчанию».
Что ограничивает использование MongoDB
Некоторые компании не используют MongoDB Atlas, потому что он размещён за границей и может попасть под санкции. Вместо этого устанавливают MongoDB на своих серверах или выбирают другие базы данных. В крупных организациях и государственных структурах чаще используют PostgreSQL, MySQL, MS SQL или Oracle — из-за требований по безопасности и совместимости.
Данные, подпадающие под закон о персональных данных, хранятся на российских серверах, и выбор базы зависит от требований к сертификации. MongoDB CE (Community Edition) может не всегда соответствовать таким условиям.
Подготовка
Перейдём к делу. Перед началом работы убедитесь, что у вас:
- установлена Node.js версии 12.x или выше;
- установлен Express.js;
- есть локальная или удалённая MongoDB-база данных;
- есть базовые знания JavaScript и понимание работы REST API.
Зачем использовать BcryptJS
Библиотека bcrypt — это алгоритм хеширования, предназначенный для безопасного хранения паролей.
Его особенности:
- Безопасность. Алгоритм специально замедлён для защиты от атак перебора. Это значит, что даже при мощностях современных компьютеров перебрать хеши становится слишком затратным по времени.
- Автоматическое соление. Для каждого пароля создаётся уникальная соль, которая добавляется к исходной строке перед хешированием. Это делает невозможным использование заранее подготовленных радужных таблиц и затрудняет взлом даже одинаковых паролей.
- Простота. Модуль BcryptJS легко интегрируется в JavaScript-проекты, работает без зависимостей на нативные библиотеки и подходит для использования как на сервере, так и в браузере.
- Гибкость. Вы можете настроить сложность хеширования с помощью параметра cost — чем выше значение, тем медленнее будет происходить процесс, но тем надёжнее защита.
- Поддержка асинхронности. Библиотека BcryptJS работает в неблокирующем режиме, что особенно важно в среде Node.js, позволяя обрабатывать запросы без задержек.
Как работает хеширование
Хеширование — это процесс необратимого преобразования данных в фиксированный набор символов. Оно не шифрует пароль, а делает его нечитаемым.
Соление — это добавление случайных данных к паролю перед хешированием, чтобы один и тот же пароль всегда выдавал разный хеш.
Пример:
const bcrypt = require("bcryptjs");
const password = 'sammy';
const salt = bcrypt.genSaltSync(10); // генерация соли
const hash = bcrypt.hashSync(password + salt); // хеширование с солью
console.log(hash);
Этот код показывает, как можно надёжно спрятать пароль. Сначала создаётся случайная соль — дополнительная строка, которая прибавляется к паролю перед хешированием. В результате даже одинаковые пароли превращаются в уникальные хеши. Это затрудняет взлом и защищает систему от типичных атак. В реальных проектах лучше использовать встроенный способ bcrypt.hashSync(password, salt) — он сам правильно добавляет соль, что безопаснее, чем вручную объединять строку и соль.
Установка и структура проекта
Создайте структуру проекта и установите необходимые зависимости:
npm init -y
npm install express mongoose bcryptjs nodemon
Создайте следующие файлы:
- app.js — основной файл сервера,
- db.js — логика подключения к MongoDB,
- auth.js — маршруты для регистрации и входа,
- User.js — модель пользователя для базы данных.
Этот шаг создаёт основу проекта и подключает все необходимые инструменты для серверной разработки. Вы получите минимальную, но функциональную структуру, которая позволяет обрабатывать запросы, работать с базой данных и безопасно хранить пароли.
Подключение к MongoDB (db.js)
Создадим отдельный модуль, чтобы удобно управлять подключением к базе данных:
const mongoose = require("mongoose");
const mongoURI = "mongodb://127.0.0.1:27017/bcrypt_database";
const connectMongo = async () => {
try {
await mongoose.connect(mongoURI);
console.log("Connected to MongoDB!");
} catch (error) {
console.error("Error connecting to MongoDB: ", error.message);
}
};
module.exports = connectMongo;
Этот код выносит подключение к базе данных в отдельный модуль, что упрощает структуру проекта и делает его масштабируемым. Такой подход позволяет централизованно управлять соединением с MongoDB и переиспользовать его в разных частях приложения.
Модель пользователя (User.js)
Модель определяет структуру документа в MongoDB. Здесь два поля — email и password:
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true }
});
module.exports = mongoose.model("user", UserSchema);
Этот код создаёт модель пользователя и задаёт правила для хранения данных в MongoDB. Она определяет, что каждый пользователь должен иметь уникальный email и обязательный пароль, обеспечивая базовую валидацию и структуру данных на уровне базы.
Настройка сервера (app.js)
Создаём Express-приложение и подключаем к нему базу данных и маршруты:
const connectToMongo = require("./db");
const express = require("express");
const app = express();
connectToMongo();
app.use(express.json());
app.use("/auth", require("./auth"));
const port = 3300;
app.listen(port, () => {
console.log(`Listening at http://localhost:${port}`);
});
Этот фрагмент запускает серверное приложение, подключает его к базе данных и настраивает обработку маршрутов. Благодаря этому сервер начинает принимать HTTP-запросы, обрабатывать JSON-данные и перенаправлять их в модуль с логикой регистрации и входа.
Регистрация пользователя (auth.js)
Хешируем пароль перед сохранением его в базе:
const express = require("express");
const router = express.Router();
const User = require("./User");
const bcrypt = require("bcryptjs");
router.post("/signup", async (req, res) => {
const salt = await bcrypt.genSalt(10);
const secPass = await bcrypt.hash(req.body.password, salt);
let user = await User.create({
email: req.body.email,
password: secPass
});
res.json({ user });
});
Что это даёт:
- генерируется соль (genSalt(10));
- пароль хешируется с солью (hash(...));
- в базу сохраняется только хешированный пароль.
Этот код реализует безопасную регистрацию пользователя, при которой пароль никогда не сохраняется в виде открытого текста. На этапе создания учётной записи генерируется уникальная соль для каждого пароля с помощью метода genSalt с параметром 10. Это усложняет подбор пароля, даже если у нескольких пользователей одинаковые значения. Далее пароль хешируется вместе с солью через функцию hash. В результате получается зашифрованная строка, которую невозможно восстановить в исходный текст. В базу данных сохраняется только хешированный пароль, а не реальный ввод пользователя. Такой подход позволяет защитить систему даже в случае утечки данных. Полученные хеши не дадут злоумышленнику реального доступа к аккаунтам, поскольку для их взлома потребуется слишком много времени и ресурсов. Это важный шаг к построению надёжной и современной системы авторизации.
Аутентификация пользователя (auth.js)
Сравниваем введённый пароль с хешем, сохранённым в базе:
router.post("/login", async (req, res) => {
let user = await User.findOne({ email: req.body.email });
if (!user) {
return res.status(400).json({ error: "Login with proper credentials!" });
}
const passwordCompare = await bcrypt.compare(req.body.password, user.password);
if (!passwordCompare) {
return res.status(400).json({ error: "Login with proper credentials!" });
}
res.json({ success: "Authenticated!" });
});
module.exports = router;
Здесь:
- ищем пользователя по email;
- сравниваем введённый пароль с хешем (compare());
- отправляем ответ в зависимости от результата.
Этот код реализует проверку подлинности пользователя при входе в систему. Он сравнивает введённый пароль с тем, что хранится в базе в виде хеша. Сначала приложение ищет пользователя по адресу электронной почты. Если пользователь не найден, возвращается сообщение об ошибке. Если пользователь найден, введённый пароль сравнивается с хешем, сохранённым в базе, с помощью метода compare. Эта функция автоматически применяет ту же соль и алгоритм, что использовались при регистрации. Если хеши совпадают — пользователь успешно аутентифицирован и получает доступ.
Такой подход гарантирует, что проверка выполняется без необходимости знать исходный пароль. Он обеспечивает безопасность и соответствие лучшим практикам: даже при попытке подбора пароля вручную или автоматическими скриптами система не раскрывает, существует ли конкретный email или какой именно параметр был неверным.
Пример запроса на регистрацию:
{
"email": "test@example.com",
"password": "yourpassword"
}
Отправляется POST-запрос на:
http://localhost:3300/auth/signup
Этот пример демонстрирует, как работает ручка регистрации: клиент отправляет POST-запрос на указанный адрес с телом, содержащим email и пароль. Это позволяет протестировать работу сервера и убедиться, что пользователь создаётся, а пароль надёжно хешируется перед сохранением в базу.
Заключение
Вы научились настраивать Express-сервер, подключаться к MongoDB, создавать модель пользователя, хешировать пароли с помощью BcryptJS, безопасно аутентифицировать пользователей. Эти шаги закладывают основу безопасной системы авторизации. В дальнейшем можно реализовать JWT-токены, роли, защиту маршрутов, ограничения на вход и другие элементы полноценной системы безопасности.
Если вы планируете размещать своё приложение на удалённом сервере, рассмотрите аренду VPS — это удобный и надёжный способ развернуть проект в Интернете. Мы предлагаем VPS/VDS с быстрыми NVMe-дисками, защитой от DDoS и бесплатной поддержкой — всё, чтобы вы могли сосредоточиться на разработке.
Читайте в блоге:
- Как подготовить сайт к пиковой нагрузке: настройка, оптимизация и выбор VPS
- Что такое скрипт в IT и для чего он нужен
- Установка и настройка phpMyAdmin на Ubuntu