Поскольку популярность Node.js продолжает расти, становится все более важным обеспечить безопасность API, созданных с использованием этой платформы. Два эффективных способа сделать это — использовать веб-токены JSON (JWT) для аутентификации и ограничения скорости для предотвращения атак методом грубой силы и других вредоносных действий.

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

Что такое веб-токены JSON?

Веб-токены JSON — это стандарт для безопасного представления претензий между двумя сторонами. JWT состоят из трех частей: заголовка, полезной нагрузки и подписи. Заголовок и полезная нагрузка представляют собой строки JSON в кодировке Base64Url, а подпись используется для проверки целостности токена.

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

Как реализовать аутентификацию на основе JWT в Node.js

Чтобы реализовать аутентификацию на основе JWT в Node.js, вам потребуется установить библиотеку jsonwebtoken. Эта библиотека предоставляет функции для создания и проверки JWT. Вот пример того, как создать JWT:

const jwt = require('jsonwebtoken');

const payload = {
  user: 'johndoe'
};

const secret = 'mysecret';

const token = jwt.sign(payload, secret, { expiresIn: '1h' });

console.log(token);

В этом примере мы создаем JWT с полезной нагрузкой, содержащей свойство user, для которого установлено значение johndoe. Мы используем секретный ключ mysecret для подписи токена и устанавливаем срок действия 1 час, используя опцию expiresIn.

Чтобы проверить JWT, вы можете использовать функцию verify библиотеки jsonwebtoken:

const jwt = require('jsonwebtoken');

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiam9obmRvZSJ9.-MQPfE60laMNGxyZsUbIb3AsA0y0FVmqr2k7_UimN_g';

const secret = 'mysecret';

jwt.verify(token, secret, (err, decoded) => {
  if (err) {
    console.error(err);
  } else {
    console.log(decoded);
  }
});

В этом примере мы проверяем JWT с помощью функции verify. Мы передаем JWT и секретный ключ, используемый для подписи токена. Если токен действителен, декодированные полезные данные будут зарегистрированы в консоли.

Что такое ограничение скорости?

Ограничение скорости — это метод, используемый для предотвращения злоупотребления API, путем ограничения количества запросов, которые могут быть сделаны в течение заданного периода времени. Это может помочь предотвратить атаки грубой силы, DDoS-атаки и другие формы злонамеренной деятельности.

Существует много стратегий ограничения скорости, но один из распространенных подходов заключается в ограничении количества запросов на IP-адрес за период времени. Например, вы можете ограничить каждый IP-адрес 100 запросами в час.

Как реализовать ограничение скорости в Node.js

Чтобы реализовать ограничение скорости в вашем приложении NodeJS, вы можете использовать такой пакет, как «express-rate-limit». Этот пакет предоставляет промежуточное программное обеспечение, которое можно использовать для ограничения количества запросов с определенного IP-адреса в течение определенного периода времени. Вот пример:

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

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

// apply to all requests
app.use(limiter);

В приведенном выше коде мы создаем ограничитель скорости, который разрешает максимум 100 запросов на IP-адрес в течение 15-минутного окна. Затем это промежуточное ПО применяется ко всем запросам с использованием app.use(limiter).

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

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

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

function jwtVerify(req, res, next) {
  // Check if the request has a JWT token in the Authorization header
  const token = req.headers.authorization;

  if (!token) {
    return res.status(401).json({ message: "Authorization header not found" });
  }

  // Verify the JWT token and extract the payload
  jwt.verify(token, secretKey, (err, payload) => {
    if (err) {
      return res.status(401).json({ message: "Invalid token" });
    }

    // Check the rate limit for the incoming request
    const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
    limiter.get(ip, (err, limit) => {
      if (err) {
        return next(err);
      }

      // Check if the request has exceeded the rate limit
      if (limit.remaining === 0) {
        return res.status(429).json({ message: "Too many requests" });
      }

      // Update the rate limit for the incoming request
      res.set("X-RateLimit-Limit", limit.total);
      res.set("X-RateLimit-Remaining", limit.remaining - 1);

      // Save the JWT payload in the request object for future use
      req.user = payload;

      // Call the next middleware in the chain
      next();
    });
  });
}

// Apply the rate limiter and JWT middleware to all API endpoints
app.use(limiter);
app.use("/api", jwtVerify);

В приведенном выше коде мы изменили наше промежуточное ПО jwtVerify, чтобы также проверять ограничение скорости для входящего запроса с использованием метода limiter.get(). Если запрос превысил ограничение скорости, мы возвращаем ответ 429 с сообщением «Слишком много запросов». В противном случае мы обновляем заголовки ограничения скорости в ответе и сохраняем полезную нагрузку JWT в объекте запроса для использования в будущем.

Наконец, мы применяем ограничитель скорости и промежуточное ПО JWT ко всем конечным точкам API, используя app.use(). Теперь наш NodeJS API защищен как аутентификацией JWT, так и ограничением скорости, чтобы предотвратить злоупотребления и обеспечить его доступность для законных пользователей.