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

Введение

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

Узел JS

Node JS — это кроссплатформенная серверная среда выполнения с открытым исходным кодом, которая позволяет разработчикам создавать быстрые, масштабируемые и высокопроизводительные приложения. Он построен на основе JavaScript-движка Chrome V8 и использует управляемую событиями неблокирующую модель ввода-вывода, что делает его идеальным для создания приложений реального времени.

Преимущества использования Node JS включают его скорость, масштабируемость и способность обрабатывать большие объемы трафика, что делает его популярным выбором для разработки веб-приложений.

Безопасный вход и регистрация в Node JS

Есть несколько мер, которые разработчики могут предпринять для обеспечения безопасного входа и регистрации в Node JS. Одной из таких мер является использование bcrypt для хеширования паролей. Bcrypt — это функция хеширования паролей, разработанная так, чтобы быть медленной и дорогостоящей в вычислительном отношении, что затрудняет взлом паролей злоумышленниками.

Еще одна мера — внедрить паспорт.js для аутентификации. Passport.js — это промежуточное ПО, которое обеспечивает аутентификацию для приложений Node JS. Он поддерживает различные методы аутентификации, включая локальную аутентификацию, OAuth и OpenID.

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

Лучшие практики для безопасного входа и регистрации в Node JS

В дополнение к мерам, упомянутым выше, есть и другие рекомендации, которым разработчики могут следовать, чтобы обеспечить безопасный вход и регистрацию в Node JS. Одной из таких практик является использование HTTPS. HTTPS — это протокол, обеспечивающий безопасную связь через Интернет путем шифрования передаваемых данных.

Внедрение двухфакторной аутентификации — еще одна практика, которая может повысить безопасность. Двухфакторная аутентификация требует, чтобы пользователи предоставили две формы аутентификации, такие как пароль и одноразовый код, отправленный на их телефон, что значительно усложняет злоумышленникам доступ к учетным записям пользователей.

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

Для разработки безопасного входа и регистрации API в Node JS мы будем использовать MongoDB.

Начните с установки необходимых пакетов с помощью следующей команды:

npm install mongoose bcryptjs validator express-rate-limit jsonwebtoken cookie-parser

bcryptjs — это библиотека, которая позволяет хэшировать и сравнивать пароли. validator — это библиотека, которая позволяет проверять и очищать строки. express-rate-limit — это промежуточное ПО, облегчающее ограничение скорости запросов. jwt — это библиотека, которая упрощает создание и проверку веб-токенов JSON.

Создайте модель мангуста для коллекции User

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const validator = require('validator');
const jwt = require('jsonwebtoken');
const rateLimit = require('express-rate-limit');

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true,
    trim: true,
    validate: [validator.isEmail, 'Please provide a valid email address']
  },
  password: {
    type: String,
    required: true,
    minlength: [8, 'Password must be at least 8 characters long'],
    maxlength: [128, 'Password must be less than 128 characters long']
  },
  loginCount: {
    type: Number,
    default: 0
  }
},{
 timestamps: true 
});

// Hash password before saving to database
userSchema.pre('save', async function() {
  const user = this;
  if (!user.isModified('password')) {
    return;
  }
  const salt = await bcrypt.genSalt(10);
  user.password = await bcrypt.hash(user.password, salt);
});

// Compare password with hashed password in database
userSchema.methods.comparePassword = async function(password) {
  return await bcrypt.compare(password, this.password);
};

// Increment login count when user logs in
userSchema.methods.incrementLoginCount = async function() {
  this.loginCount += 1;
  return await this.save();
};

// Generate a JWT token
userSchema.methods.generateAuthToken = function () {
  const token = jwt.sign({ _id: this._id }, process.env.JWT_SECRET, { expiresIn: '1d' });
  return token;
};

userSchema.statics.findByToken = async function (token) {
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    return await this.findOne({ _id: decoded._id });
  } catch (err) {
    throw new Error(`Error verifying token: ${err.message}`);
  }
};

const User = mongoose.model('User', userSchema);

module.exports = User;

// Separate module for password validation
module.exports.validatePassword = function(password) {
  const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_\-+={}[\]\\|:;'<>,.?/])[a-zA-Z\d!@#$%^&*()_\-+={}[\]\\|:;'<>,.?/]{8,}$/;
  return regex.test(password);
};

// Separate module for rate limiting
module.exports.loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit each IP to 5 requests per windowMs
  message: 'Too many login attempts from this IP, please try again later'
});

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

Есть также несколько методов, включенных в пользовательскую модель. Метод comparePassword сравнивает заданный пароль с хешированным паролем, хранящимся в базе данных, с помощью bcrypt. Метод incrementLoginCount увеличивает количество входов пользователя в систему и сохраняет пользователя в базе данных. Метод generateAuthToken создает для пользователя веб-маркер JSON (JWT). Наконец, метод findByToken — это статический метод, который принимает JWT в качестве аргумента, проверяет его с помощью переменной среды JWT_SECRET и возвращает пользователя, связанного с декодированным токеном.

Создайте маршрут входа и контроллер с помощью следующего кода:

login.controller.js

const LoginController = async (req, res) => {
  // User authentication logic
}

module.exports = LoginController;

попытки.middleware.js

const rateLimit = require("express-rate-limit");

const ApiRateLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 5, // 5 requests
  message: "Too many attempts, please try again later.",
});

module.exports = ApiRateLimiter;

user.router.js

const express = require("express");
const LoginController = require("../controllers/login.controller");
const ApiRateLimiter = require("../middleware/attempts.middleware");

const router = express.Router();

router.post("/login", ApiRateLimiter, LoginController);

module.exports = router;

LoginController и ApiRateLimiter были перемещены в отдельные файлы и импортированы в файл user.router.js. Этот подход следует принципу разделения интересов. Кроме того, мы также следовали принципу инверсии зависимостей, импортируя LoginController и ApiRateLimiter вместо их встроенного определения. Новый код также является модульным и его легче тестировать.

exports.login = async (req, res) => {
  try {
    const { username, password } = req.body;
    const user = await User.findOne({ username });

    if (!user) {
      return res.status(401).json({ message: 'Invalid username or password' });
    }

    const isMatch = await user.comparePassword(password);

    if (!isMatch) {
      return res.status(401).json({ message: 'Invalid username or password' });
    }

    const token = user.generateAuthToken();
    await user.incrementLoginCount();

    res.cookie('token', token, { httpOnly: true, sameSite: 'strict', secure: false });

    res.json({ message: 'Login successful', status: 1 });
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Server error' });
  }
}

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

const VerifyToken = async (req, res, next) => {
  try {
    const token = req.cookies.token;
    const user = await User.findByToken(token);
    if (!user) {
      throw new Error("Unauthorized");
    }
    req.user = user;
    next();
  } catch (err) {
    res.status(401).json({ message: err.message });
  }
};

module.exports = VerifyToken;
router.get("/profile", VerifyToken, Profile)

Заключение

В заключение, безопасный вход и регистрация в Node JS имеют решающее значение для защиты личной информации пользователей и предотвращения несанкционированного доступа. Применяя такие меры, как bcrypt для хеширования паролей, внедряя password.js для аутентификации, интегрируя управление сеансами и следуя передовым методам, таким как использование HTTPS, внедрение двухфакторной аутентификации и применение политик паролей, разработчики могут гарантировать, что данные их пользователей безопасный.