Безопасность · Урок 24

Авторизация и безопасность

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

Пароли: храним только хэш

Пароль в открытом виде — катастрофа. Храним не его, а хэш с «солью» — случайной строкой на каждого пользователя. Надёжный пакет — bcrypt.

npm install bcrypt

const bcrypt = require("bcrypt");
const hash = await bcrypt.hash(plainPassword, 10);   // соль и хэш
const ok   = await bcrypt.compare(plainPassword, hash); // проверка
⚠️
Никогда не используйте для паролей обычный MD5/SHA — они считаются мгновенно и взламываются перебором. Bcrypt намеренно «медленный», поэтому подбор одного пароля занимает годы.

Сессии и JWT

После входа сервер должен «помнить» пользователя. Два основных способа: сессии (ид в cookie + хранилище на сервере) и JWT — криптографическая подпись в самом токене.

const jwt = require("jsonwebtoken");

// выдача токена при входе
const token = jwt.sign({ id: user.id }, SECRET, { expiresIn: "7d" });
// клиент кладёт его в localStorage/cookie и шлёт с каждым запросом

// проверка на защищённых маршрутах
function auth(req, res, next) {
  const token = req.headers.authorization?.split(" ")[1];
  try { req.user = jwt.verify(token, SECRET); next(); }
  catch { res.status(401).json({ error: "Нет доступа" }); }
}
app.get("/profile", auth, (req, res) => res.json({ user: req.user }));

XSS: чужие скрипты на вашей странице

Если вставлять в HTML данные пользователя как есть, можно «запустить» чужой код в чужом браузере. Пример: комментарий со злым <script>. Лечится экранированием вывода.

// опасно:
element.innerHTML = userComment;

// безопасно: textContent сам экранирует <, >, "
element.textContent = userComment;

// на сервере — библиотека escape-html
const escapeHtml = require("escape-html");
res.send("<p>" + escapeHtml(userComment) + "</p>");

CSP и основы

Content-Security-Policy — заголовок, который запрещает браузеру исполнять что-то, чего вы не разрешили (инлайн-скрипты, чужие домены). Плюс HTTPS по умолчанию (урок 20), helmet для набора безопасных заголовков.

npm install helmet
app.use(helmet()); // безопасные заголовки одной строкой
💡
Чек-лист минимума: bcrypt для паролей, экранирование любого вывода, helmet, HTTPS, секреты в переменных окружения (process.env), а не в коде.