Гид по технологиям

Сброс пароля в React и Express — пошаговое руководство

6 min read Аутентификация Обновлено 09 Jan 2026
Сброс пароля в React + Express
Сброс пароля в React + Express

Этот пошаговый гид показывает, как реализовать безопасный и удобный сброс пароля в приложении на React с бэкендом на Express. Вы получите рабочий пример: компонент входа, отправка OTP по электронной почте, верификация кода и изменение пароля, а также рекомендации по безопасности и чеклист для выпуска в прод.

Ноутбук на столе с открытым редактором кода

Системы аутентификации важны для удобства и безопасности пользователей. Типичный поток аутентификации включает регистрацию и вход. По мере роста числа сервисов у пользователей появляется много учётных записей и паролей. Это увеличивает риск забыть или перепутать данные входа. Чтобы закрыть эту проблему, приложение должно предоставлять надёжную функцию сброса пароля — удобную для пользователей и безопасную против злоумышленников.

Общая схема рабочего процесса

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

Схема рабочего процесса сброса пароля

Вкратце шаги такие:

  • Пользователь нажимает «Забыли пароль?» на форме входа.
  • Сервер проверяет наличие email в базе.
  • Сервер генерирует одноразовый код (OTP) и отправляет его по email.
  • Пользователь вводит код в приложении — приложение сверяет код.
  • После успешной верификации пользователь указывает новый пароль — сервер обновляет запись в базе.

Важно: этот подход — один из возможных. Подбирайте детали под требования безопасности и UX вашего приложения.

Настройка проекта React

Быстро создайте React-проект и установите Axios для HTTP-запросов:

npm install axios

Далее создайте компоненты, описанные ниже. Код примера доступен в репозитории, ссылка на который указана в исходном материале.

Создаём компонент Login

В каталоге src создайте файл components/Login.js и добавьте код для начала процесса сброса пароля:

import axios from "axios";  
import React, { useState } from "react";  
import { useContext } from "react";  
import { RecoveryContext } from "../App";  
import "./global.component.css";  
  
export default function Login() {  
  const { setPage, setOTP, setEmail } = useContext(RecoveryContext);  
  const [userEmail, setUserEmail] = useState("");   
  
  function sendOtp() {  
    if (userEmail) {  
      axios.get(`http://localhost:5000/check_email?email=${userEmail}`).then((response) => {  
        if (response.status === 200) {  
          const OTP = Math.floor(Math.random() * 9000 + 1000);  
          console.log(OTP);  
          setOTP(OTP);  
          setEmail(userEmail);  
  
          axios.post("http://localhost:5000/send_email", {  
            OTP,  
            recipient_email: userEmail,  
          })  
          .then(() => setPage("otp"))  
          .catch(console.log);  
        } else {  
          alert("User with this email does not exist!");  
          console.log(response.data.message);  
        }}).catch(console.log);  
    } else {  
      alert("Please enter your email");  
    }}

Этот фрагмент создаёт функцию, которая отправляет одноразовый код (OTP) на указанный email. Сначала происходит проверка существования пользователя в базе, затем генерируется и отправляется OTP, и UI переключается на страницу ввода кода.

Завершите компонент, отрисовав форму входа:

  return (  
    

Login

); }

Перевод UI-меток внутри кода оставлен на английском так, как в исходнике. Вы можете локализовать label и кнопки в соответствии с целевой аудиторией.

Компонент верификации OTP

Создайте components/OTPInput.js и вставьте код:

import React, { useState, useContext, useEffect } from "react";  
import { RecoveryContext } from "../App";  
import axios from "axios";  
import "./global.component.css";  
  
export default function OTPInput() {  
  const { email, otp, setPage } = useContext(RecoveryContext);  
  const [OTPinput, setOTPinput] = useState( "");  
  
  function verifyOTP() {  
    if (parseInt(OTPinput) === otp) {  
      setPage("reset");  
    } else {  
      alert("The code you have entered is not correct, try again  re-send the link");  
    }  
  }

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

В этом примере в репозитории также есть реализация таймера истечения кода и функции повторной отправки.

Завершите отрисовку полей ввода:

  return (  
    

Email Verification

We have sent a verification code to your email.

{ setOTPinput(e.target.value) }} /> resendOTP()} > Didn't receive code? {disable ? `Resend OTP in ${timerCount}s` : " Resend OTP"}
);}

Важно: проверяйте формат email на клиенте и на сервере. Не полагайтесь только на клиентскую валидацию.

Компонент смены пароля

Создайте components/Reset.js и добавьте код:

import React, {useState, useContext} from "react";  
import { RecoveryContext } from "../App";  
import axios from "axios";  
import "./global.component.css";  
  
export default function Reset() {  
  const [password, setPassword] = useState("");  
  const { setPage, email } = useContext(RecoveryContext);  
  
  function changePassword() {  
    if (password) {  
      try {  
        axios.put("http://localhost:5000/update-password", {  
          email:email,  
          newPassword: password,  
        }).then(() => setPage("login"));  
  
        return alert("Password changed successfully, please login!");  
      } catch (error) {console.log(error);}}  
    return alert("Please enter your new Password");  
  }  
  
  return (  
    

Change Password

); }

Этот компонент отправляет новый пароль на сервер, где он сохраняется в базе данных. Не забудьте хешировать пароль на сервере перед сохранением (см. раздел «Безопасность» ниже).

Обновление App.js

В файле src/App.js определите контекст и логику навигации между страницами:

import { useState, createContext } from "react";  
import Login from "./components/Login";  
import OTPInput from "./components/OTPInput";  
import Reset from "./components/Reset";  
import "./App.css";  
export const RecoveryContext = createContext();  
  
export default function App() {  
  const [page, setPage] = useState("login");  
  const [email, setEmail] = useState("");  
  const [otp, setOTP] = useState("");  
  
  function NavigateComponents() {  
    if (page === "login") return ;  
    if (page === "otp") return ;  
    if (page === "reset") return ;  
  }  
  
  return (  
    
); }

Контекст облегчает обмен состоянием между компонентами без глубокой передачи props.

Настройка сервера Express

На бэкенде реализуйте три маршрута: проверка email, отправка письма и обновление пароля.

Установите зависимости:

npm install cors dotenv nodemailer mongoose

Создайте базу MongoDB (локально или в облаке), добавьте connection string в .env и настройте соединение (см. репозиторий для примера).

Определение маршрутов API

Создайте файл userRoutes.js с маршрутизацией:

const express = require('express');  
const router = express.Router();  
const userControllers = require('../controllers/userControllers');  
  
router.get('/check_email', userControllers.checkEmail);  
router.put('/update-password', userControllers.updatePassword);  
router.post('/send_email', userControllers.sendEmail);  
  
module.exports = router;

Контроллеры

В controllers/userControllers.js реализуйте логику отправки писем и обновления пароля. Пример функции отправки письма (использует Nodemailer):

exports.sendEmail = (req, res) => {  
  const transporter = nodemailer.createTransport({  
    service: 'gmail',  
    secure: true,  
    auth: {  
      user: process.env.MY_EMAIL,  
      pass: process.env.APP_PASSWORD,  
    },  
  });  
  
  const { recipient_email, OTP } = req.body;  
  
  const mailOptions = {  
    from: process.env.MY_EMAIL,  
    to: recipient_email,  
    subject: 'PASSWORD RESET',  
    html: `  
               
               

Password Recovery

Use this OTP to reset your password. OTP is valid for 1 minute

${OTP}

`, }; transporter.sendMail(mailOptions, (error, info) => { if (error) { console.log(error); res.status(500).send({ message: "An error occurred while sending the email" }); } else { console.log('Email sent: ' + info.response); res.status(200).send({ message: "Email sent successfully" }); } }); };

В примере используется аккаунт Gmail и app password. В реальном проекте рассмотрите специализированные почтовые сервисы (SendGrid, SES) для повышения надёжности и масштабируемости.

Точка входа сервера

Создайте server.js:

const express = require('express');  
const cors = require('cors');  
const app = express();  
const port = 5000;  
require('dotenv').config();  
const nodemailer = require('nodemailer');  
const connectDB = require('./utils/dbconfig');  
connectDB();  
app.use(express.json());  
app.use(express.urlencoded({ extended: true }));  
app.use(cors());  
const userRoutes = require('./routes/userRoutes');  
app.use('/', userRoutes);  
  
app.listen(port, () => {  
  console.log(`Server is listening at http://localhost:${port}`);  
});

Запустите клиент и сервер и протестируйте сценарий сброса пароля.

Безопасность и рекомендации

Важно учитывать угрозы и покрыть их на ранних этапах:

  • Хеширование паролей. Никогда не храните пароли в открытом виде. Используйте bcrypt или Argon2.
  • Срок жизни OTP. Устанавливайте короткий TTL (например, минуты). Храните метку времени и проверяйте истечение на сервере.
  • Ограничение попыток. Блокируйте повторные попытки ввода OTP после N неуспехов.
  • Ограничение частоты отправки писем. Наложите rate limiting на endpoint отправки писем.
  • Использование токенов вместо прямой отправки пароля. Альтернативный подход — отправить ссылку с одноразовым токеном для страницы сброса.
  • Логи и аудит. Логируйте события сброса (без чувствительных данных) для расследований.
  • Защита от утечки email. Не раскрывайте в UI, существует ли email в системе: вместо этого показывайте нейтральные сообщения (например, “Если почта зарегистрирована, вы получите письмо”).

Короткие рекомендации по хешированию паролей и токенов:

  • Хешируйте на сервере с солью и достаточным work factor (bcrypt saltRounds ≥ 10 или Argon2 параметры).
  • Если используете токены для сброса — подписывайте их и храните в БД с привязкой к пользователю и TTL.

Когда этот подход не подходит

  • Если вы работаете в высокозащищённой среде (финансы, медицина), одного OTP по email может быть недостаточно — требуйте 2FA или ручную проверку.
  • Если у вас миллионы пользователей, использовать Gmail напрямую через Nodemailer неэффективно. Переходите на сервис с очередями и ретраями.

Альтернативные подходы

  • Ссылка для сброса пароль с одноразовым JWT-подписью и сроком действия.
  • Отправка SMS — удобна, но дороже и требует защиты от SIM‑swap атак.
  • WebAuthn / аппаратные ключи — для максимальной безопасности.

Ментальные модели и эвристики при проектировании

  • Принцип минимального доверия: не доверяйте клиенту, все проверки — на сервере.
  • Fail closed: при сомнении блокируйте доступ, а не открывайте его.
  • UX-first: упростите процесс так, чтобы пользователю было очевидно, что делать дальше.

Чеклист перед запуском в прод

  • Хеширование паролей реализовано и протестировано.
  • TTL для OTP и механизм истечения работают.
  • Ограничение частоты отправки писем настроено.
  • Логи настроены без утечки личных данных.
  • Сообщения UI нейтральны и не раскрывают наличие аккаунтов.
  • Тесты на случайные и злонамеренные вводы пройдены.
  • Используется надёжный почтовый провайдер для массовых отправок.

Риск‑матрица (качественная)

  • Низкий риск: UX-ошибки (пользователь не понимает процесс) — смягчение: улучшенный текст и подсказки.
  • Средний риск: перехват OTP (протоколы почты/прихват почты) — смягчение: короткий TTL, ограничение попыток.
  • Высокий риск: уязвимости на сервере/утечка БД — смягчение: шифрование, RBAC, регулярные аудиты.

Примеры тестов и критерии приёмки

  • При вводе корректного email и получении OTP — пользователь проходит к форме смены пароля.
  • Ввод неверного OTP блокирует доступ после 3 попыток.
  • Повторная отправка OTP ограничена (например, не чаще чем раз в 30–60 секунд).
  • Пароль успешно обновляется и пользователь может войти с новым паролем.

Короткий SOP для инцидентов

  1. Если поступают жалобы на массовую рассылку OTP — временно приостановите endpoint отправки писем.
  2. Проведите проверку логов на аномальную активность.
  3. При подтверждении утечки — сбросьте сессии и уведомьте пользователей с инструкциями смены пароля.

Итог и дальнейшие шаги

Реализация сброса пароля — баланс UX и безопасности. Базовый поток (проверка email, отправка OTP, верификация и обновление пароля) покрывает большинство сценариев. Для продакшна добавьте хеширование, ограничение частоты, мониторинг и используйте специализированный почтовый сервис.

Ключевые рекомендации:

  • Всегда хешируйте пароли.
  • Ограничивайте частоту отправки и количество попыток.
  • Рассмотрите альтернативы (ссылки с токеном, 2FA) для повышенной безопасности.

Спасибо, что дошли до конца. Если нужно, могу подготовить готовый checklist для CI/CD или пример миграции с вашего текущего метода на этот процесс.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство