Реалізація реєстрації користувача на веб-сайті
Реєстрація — перша взаємодія користувача з системою аутентифікації. Тут закладається структура таблиці users, логіка верифікації email, політика паролів та захист від автоматичних реєстрацій. Більшість проблем безпеки, які вспливають пізніше, тягнуться звідсіля.
Структура таблиці користувачів
Мінімальна схема, яка покриває більшість сценаріїв:
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255), -- NULL при OAuth-реєстрації
name VARCHAR(255),
email_verified_at TIMESTAMP,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
remember_token VARCHAR(100),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status ON users(status);
Поле password — nullable, тому що користувач може зареєструватися через соціальний провайдер без пароля. status приймає значення pending (email не підтверджений), active, banned, deleted (soft delete).
Хешування пароля
bcrypt з cost factor 12 — поточний стандарт. У PHP: password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]). У Node.js: bcrypt.hash(password, 12). Argon2id переважніший за безпекою, але bcrypt достатній та повсюдно підтримується.
Ніколи не зберігати пароль у відкритому вигляді, не логувати дані форми, не передавати пароль у URL-параметрах. Це очевидно, але нарушується регулярно.
Валідація на бекенді
Валідація на фронті — для UX. Валідація на бекенді — для безпеки. Обидві обов'язкові, вони не замінюють одна одну.
// Laravel FormRequest
class RegisterRequest extends FormRequest
{
public function rules(): array
{
return [
'email' => ['required', 'email:rfc,dns', 'max:255', 'unique:users,email'],
'password' => ['required', 'min:8', 'max:72', 'confirmed', Password::defaults()],
'name' => ['required', 'string', 'max:255'],
];
}
}
email:rfc,dns — перевіряє формат за RFC та існування MX-запису домена. Це відсіює неіснуючі домени ще до відправки листа. max:72 для пароля — обмеження bcrypt (обрізує рядки довше 72 байт).
Для політики паролів у Laravel: Password::min(8)->letters()->mixedCase()->numbers(). Не переусердствуйте з вимогами — NIST SP 800-63B рекомендує довжину важливішою за складність.
Верифікація email
Без верифікації можна зареєструватися з чужим адресом, отримати сповіщення на чужу ящик, забити базу мусором. Верифікація обов'язкова везде, де email використовується як ідентифікатор.
Токен верифікації — це підписана ссилка з TTL:
// Генерація ссилки
$verifyUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addHours(24),
['id' => $user->id, 'hash' => sha1($user->email)]
);
// Обробка переходу
public function verify(Request $request): RedirectResponse
{
if (! hash_equals(sha1($request->user()->email), $request->hash)) {
abort(403);
}
$request->user()->markEmailAsVerified();
return redirect('/dashboard')->with('status', 'email-verified');
}
Тимчасова підписана ссилка краще зберігання токену у БД — не потрібна окремішня таблиця, ссилка самодостатня й закінчується автоматично.
Захист від ботів та зловживання
Rate limiting: не більше 5 спроб реєстрації з однієї IP за 10 хвилин. У Laravel:
RateLimiter::for('register', function (Request $request) {
return Limit::perMinutes(10, 5)->by($request->ip());
});
Honeypot: приховане поле форми, яке заповнюють боти, а люди ні:
<input type="text" name="website" style="display:none" tabindex="-1" autocomplete="off">
На бекенді: якщо website не пусто — мовчки відхилити.
CAPTCHA: reCAPTCHA v3 (score-based, без взаємодії користувача) або hCaptcha. Включати при аномальній активності, не за умовчанням — CAPTCHA знижує конверсію.
Реєстрація через соціальні сети
OAuth-реєстрація через Google, GitHub, VK — користувачі віддають перевагу, не потрібно придумувати пароль. Логіка обробки:
public function handleOAuthCallback(string $provider): RedirectResponse
{
$socialUser = Socialite::driver($provider)->user();
$user = User::where('email', $socialUser->getEmail())->first();
if ($user) {
// Уже існує — привязуємо провайдер
$user->oauthProviders()->updateOrCreate(
['provider' => $provider],
['provider_id' => $socialUser->getId()]
);
} else {
// Новий користувач
$user = User::create([
'email' => $socialUser->getEmail(),
'name' => $socialUser->getName(),
'email_verified_at' => now(), // email уже верифіковано OAuth-провайдером
'status' => 'active',
]);
}
Auth::login($user);
return redirect('/dashboard');
}
Важливо: якщо email від OAuth збігається з існуючим аккаунтом з паролем — не створювати дубль, а привязувати провайдера до існуючого аккаунту.
Пост-реєстраційний флоу
Після успішної реєстрації типовий сценарій:
- Відправити приветственне письмо з верифікаційною ссилкою
- Створити початкові дані користувача (профіль, налаштування за замовчанням)
- Перенаправити на дашборд або сторінку «перевірте пошту»
- Опціонально: onboarding wizard при першому вході
Письмо відправляється через чергу, не синхронно — інакше затримка SMTP блокує відповідь користувачу.
Типовий час реалізації базової реєстрації з верифікацією email — 1–2 робочих дні. З OAuth-провайдерами — ще 1 день на кожного.







