وقتی کاربری در یک وبسایت ثبتنام میکند، از کجا بفهمیم ایمیلی که وارد کرده واقعاً در اختیار خودش است؟
یکی از روشهای رایج برای این کار، Email Verification یا تأیید ایمیل است.
در این روش، بعد از ثبتنام کاربر یک لینک مخصوص به آدرس ایمیل او ارسال میشود. کاربر با کلیک روی لینک، مالکیت آدرس ایمیل را تأیید میکند و حساب او از حالت تأییدنشده به حالت تأییدشده تغییر میکند.
این قابلیت در بسیاری از سیستمهای عضویت، فروشگاهها، پنلهای کاربری، SaaSها و سرویسهای آنلاین استفاده میشود.
در این مقاله با PHP و PDO یک سیستم Email Verification طراحی میکنیم و با مفاهیمی مانند:
- Verification Token
random_bytes()- Hash کردن Token
- تاریخ انقضا
- Token یکبارمصرف
- جلوگیری از Email Enumeration
- Rate Limiting
- Session
- CSRF
- ارسال ایمیل
- فعالسازی حساب
آشنا میشویم.
# Email Verification چیست؟
Email Verification فرآیندی است که طی آن کاربر بعد از ثبتنام، مالکیت یک آدرس ایمیل را اثبات میکند.
فرآیند ساده آن:
ثبتنام کاربر
↓
ایجاد حساب
↓
حساب در وضعیت Unverified
↓
تولید Token
↓
ذخیره Hash Token
↓
ارسال لینک به Email
↓
کاربر روی لینک کلیک میکند
↓
بررسی Token
↓
تأیید Email
↓
حساب Verified
برای مثال، کاربر هنگام ثبتنام این ایمیل را وارد میکند:
user@example.com
سیستم لینک زیر را برای او ارسال میکند:
https://example.com/verify-email?token=...
پس از کلیک روی لینک، حساب کاربر تأیید میشود.
# چرا Email Verification مهم است؟
تأیید ایمیل چند کاربرد مهم دارد.
1. جلوگیری از ثبت ایمیل اشتباه
ممکن است کاربر هنگام ثبتنام آدرس ایمیل را اشتباه وارد کند.
در این صورت سیستم متوجه میشود که کاربر به آن ایمیل دسترسی ندارد.
2. اثبات مالکیت Email
هدف اصلی این سیستم این است که نشان دهد کاربر به Inbox ایمیل دسترسی دارد.
3. Password Reset
سیستم Password Reset معمولاً به ایمیل متکی است.
اگر ایمیل کاربران تأیید نشده باشد، مدیریت حساب دشوارتر میشود.
4. کاهش حسابهای جعلی
Email Verification میتواند ایجاد تعداد زیادی حساب با ایمیلهای غیرواقعی را سختتر کند؛ البته به تنهایی جلوی Abuse را نمیگیرد.
# وضعیت Verified را کجا ذخیره کنیم؟
در سادهترین حالت میتوانیم در جدول users یک ستون داشته باشیم:
email_verified_at DATETIME NULL
مثلاً:
NULL
یعنی ایمیل هنوز تأیید نشده است.
و:
2026-09-19 20:30:00
یعنی ایمیل در این تاریخ تأیید شده است.
# ساخت جدول کاربران
برای مثال:
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
email_verified_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
در این طراحی:
email_verified_at = NULL
یعنی:
Unverified
و مقدار غیر NULL یعنی:
Verified
# آیا فقط یک ستون کافی است؟
برای ثبت وضعیت تأیید ایمیل، بله.
اما برای مدیریت Token بهتر است یک جدول جداگانه داشته باشیم.
مثلاً:
CREATE TABLE email_verification_tokens (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
token_hash CHAR(64) NOT NULL,
expires_at DATETIME NOT NULL,
used_at DATETIME NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX (user_id),
INDEX (token_hash)
);
# چرا Token را در جدول جدا نگه داریم؟
این طراحی چند مزیت دارد.
میتوانیم:
- چند Token را مدیریت کنیم.
- Tokenهای قدیمی را باطل کنیم.
- تاریخ انقضا داشته باشیم.
- Token مصرفشده را ثبت کنیم.
- درخواست ارسال مجدد را مدیریت کنیم.
- اطلاعات تأیید ایمیل را از جدول User جدا نگه داریم.
# ساخت Token امن
برای تولید Token از:
$token = bin2hex(random_bytes(32));
استفاده میکنیم.
سپس:
$tokenHash = hash('sha256', $token);
در دیتابیس فقط:
token_hash
را ذخیره میکنیم.
Token خام فقط برای لینک ایمیل استفاده میشود.
# چرا از rand() استفاده نکنیم؟
این روش مناسب نیست:
$token = rand(100000, 999999);
همچنین:
$token = mt_rand();
یا:
$token = time();
برای Token امنیتی مناسب نیستند.
برای چنین کاربردی از:
random_bytes()
استفاده کنید.
# چرا Token را Hash کنیم؟
فرض کنید Token تولیدشده:
a9f8d72c...
باشد.
اگر همین مقدار را در دیتابیس ذخیره کنیم، در صورت افشای دیتابیس، Token مستقیماً قابل استفاده خواهد بود.
بهتر است:
Token خام
↓
SHA-256
↓
Database
داشته باشیم.
مثلاً:
$token = bin2hex(random_bytes(32));
$tokenHash = hash(
'sha256',
$token
);
# ثبتنام کاربر
فرض کنید کاربر فرم زیر را ارسال میکند:
<form method="POST" action="/register">
<input
type="email"
name="email"
required
>
<input
type="password"
name="password"
required
>
<button type="submit">
ثبتنام
</button>
</form>
در PHP ابتدا اطلاعات را دریافت میکنیم:
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
سپس اعتبارسنجی:
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
exit('ایمیل نامعتبر است.');
}
if (strlen($password) < 12) {
exit('رمز عبور باید حداقل 12 کاراکتر باشد.');
}
# Hash کردن رمز عبور
رمز عبور را نباید مستقیماً ذخیره کنیم.
روش صحیح:
$passwordHash = password_hash(
$password,
PASSWORD_DEFAULT
);
این موضوع در مقاله امنیت رمز عبور در PHP به شکل کامل بررسی شده است.
# ایجاد User
حالا میتوانیم کاربر را ایجاد کنیم:
$stmt = $pdo->prepare(
'INSERT INTO users
(email, password)
VALUES
(:email, :password)'
);
$stmt->execute([
'email' => $email,
'password' => $passwordHash
]);
در این لحظه:
email_verified_at = NULL
است.
بنابراین حساب هنوز تأیید نشده است.
# دریافت ID کاربر
بعد از Insert:
$userId = $pdo->lastInsertId();
حالا میتوانیم Token مخصوص همین کاربر را ایجاد کنیم.
# تولید Verification Token
$token = bin2hex(
random_bytes(32)
);
سپس:
$tokenHash = hash(
'sha256',
$token
);
# تعیین تاریخ انقضا
فرض کنیم Token به مدت 30 دقیقه معتبر باشد:
$expiresAt = date(
'Y-m-d H:i:s',
time() + 1800
);
یعنی:
1800 ثانیه = 30 دقیقه
# ذخیره Token
$stmt = $pdo->prepare(
'INSERT INTO email_verification_tokens
(user_id, token_hash, expires_at)
VALUES
(:user_id, :token_hash, :expires_at)'
);
$stmt->execute([
'user_id' => $userId,
'token_hash' => $tokenHash,
'expires_at' => $expiresAt
]);
اکنون Token خام در دیتابیس وجود ندارد.
# ساخت لینک تأیید
دامنه سایت را بهتر است از Configuration برنامه بگیریم:
$baseUrl = 'https://example.com';
$verificationUrl =
$baseUrl
. '/verify-email?token='
. urlencode($token);
لینک چیزی شبیه این خواهد شد:
https://example.com/verify-email?token=8d4e...
# ارسال ایمیل
یک ایمیل ساده میتواند چنین محتوایی داشته باشد:
$subject = 'تأیید آدرس ایمیل';
$message = "
سلام،
برای تأیید آدرس ایمیل خود روی لینک زیر کلیک کنید:
$verificationUrl
این لینک تا 30 دقیقه معتبر است.
";
در پروژه واقعی بهتر است ارسال ایمیل را با یک Mailer یا سرویس Email انجام دهیم.
همچنین در پروژههای پرترافیک، ارسال ایمیل بهتر است به Queue منتقل شود.
# چرا ارسال ایمیل نباید همیشه داخل Request انجام شود؟
فرض کنید:
ثبتنام
↓
ارسال ایمیل
↓
پاسخ HTTP
اگر سرویس ایمیل کند باشد، کاربر نیز منتظر میماند.
طراحی بهتر:
ثبتنام
↓
ذخیره User
↓
قرار دادن Email Job در Queue
↓
پاسخ سریع به کاربر
↓
Worker
↓
ارسال Email
این روش مخصوصاً در سیستمهای بزرگتر اهمیت زیادی دارد.
# صفحه تأیید Email
کاربر روی لینک کلیک میکند:
https://example.com/verify-email?token=...
در PHP:
$token = $_GET['token'] ?? '';
اگر Token خالی باشد:
if ($token === '') {
exit('لینک تأیید نامعتبر است.');
}
# Hash کردن Token دریافتی
$tokenHash = hash(
'sha256',
$token
);
حالا میتوانیم آن را در دیتابیس پیدا کنیم.
# پیدا کردن Token
$stmt = $pdo->prepare(
'SELECT
id,
user_id,
token_hash,
expires_at,
used_at
FROM email_verification_tokens
WHERE token_hash = :token_hash
LIMIT 1'
);
$stmt->execute([
'token_hash' => $tokenHash
]);
$verificationToken = $stmt->fetch(
PDO::FETCH_ASSOC
);
# بررسی Token
اگر Token وجود نداشته باشد:
if (!$verificationToken) {
exit('لینک تأیید نامعتبر است.');
}
# بررسی مصرف شدن Token
اگر قبلاً استفاده شده باشد:
if ($verificationToken['used_at'] !== null) {
exit('این لینک قبلاً استفاده شده است.');
}
Token تأیید ایمیل نیز بهتر است یکبارمصرف باشد.
# بررسی تاریخ انقضا
if (
strtotime($verificationToken['expires_at']) < time()
) {
exit('این لینک منقضی شده است.');
}
در این حالت کاربر میتواند درخواست ارسال لینک جدید کند.
# بررسی Token با hash_equals()
در صورت مقایسه مستقیم Hashها:
if (!hash_equals(
$verificationToken['token_hash'],
$tokenHash
)) {
exit('Token نامعتبر است.');
}
البته در این طراحی چون Token Hash شده و Query نیز بر اساس Hash انجام شده است، خود Query بخش اصلی پیدا کردن Token معتبر است.
# تأیید Email کاربر
بعد از اعتبارسنجی Token، باید User را Verified کنیم:
$stmt = $pdo->prepare(
'UPDATE users
SET email_verified_at = NOW()
WHERE id = :user_id
AND email_verified_at IS NULL'
);
$stmt->execute([
'user_id' => $verificationToken['user_id']
]);
شرط:
AND email_verified_at IS NULL
باعث میشود حسابی که قبلاً تأیید شده دوباره به شکل غیرضروری تغییر نکند.
# باطل کردن Token
بعد از تأیید موفق:
$stmt = $pdo->prepare(
'UPDATE email_verification_tokens
SET used_at = NOW()
WHERE id = :id
AND used_at IS NULL'
);
$stmt->execute([
'id' => $verificationToken['id']
]);
اکنون Token مصرف شده است.
# استفاده از Transaction
تأیید User و مصرف Token دو عملیات مرتبط هستند.
بهتر است در یک Transaction انجام شوند:
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare(
'UPDATE users
SET email_verified_at = NOW()
WHERE id = :user_id
AND email_verified_at IS NULL'
);
$stmt->execute([
'user_id' => $verificationToken['user_id']
]);
$stmt = $pdo->prepare(
'UPDATE email_verification_tokens
SET used_at = NOW()
WHERE id = :id
AND used_at IS NULL'
);
$stmt->execute([
'id' => $verificationToken['id']
]);
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
throw $e;
}
این کار باعث میشود وضعیت دیتابیس در صورت خطا ناسازگار نشود.
# بعد از تأیید Email چه اتفاقی بیفتد؟
حالا که:
email_verified_at != NULL
میتوانیم امکاناتی را برای کاربر فعال کنیم.
برای مثال:
ثبتنام
↓
Email Unverified
↓
محدودیت امکانات
↓
Verify Email
↓
فعال شدن حساب
مثلاً میتوانیم اجازه ارسال سفارش، استفاده از API یا دسترسی به بخشهای خاص را فقط به کاربران Verified بدهیم.
البته این تصمیم باید متناسب با منطق کسبوکار پروژه باشد.
# آیا باید کاربر قبل از تأیید Email بتواند Login کند؟
این تصمیم به معماری پروژه بستگی دارد.
دو روش رایج وجود دارد.
روش اول: Login مجاز ولی امکانات محدود
Register
↓
Login
↓
Verify Email
↓
Full Access
در این مدل کاربر میتواند وارد حساب شود، اما مثلاً پیام:
لطفاً ایمیل خود را تأیید کنید.
نمایش داده میشود.
روش دوم: Login فقط بعد از Verification
Register
↓
Verify Email
↓
Login
این روش برای سیستمهایی که تأیید Email برای امنیت یا منطق کسبوکار ضروری است مناسبتر است.
# ارسال مجدد Verification Email
کاربر ممکن است ایمیل را دریافت نکرده باشد.
پس معمولاً یک گزینه مانند:
ارسال مجدد ایمیل تأیید
در نظر میگیریم.
اما نباید اجازه دهیم کاربر بدون محدودیت روی آن کلیک کند.
مثلاً:
ارسال اول
↓
30 ثانیه انتظار
↓
ارسال مجدد
یا محدودیتهایی مانند:
5 درخواست در یک ساعت
اعمال میشود.
عدد دقیق باید بر اساس نیاز پروژه تعیین شود.
# چرا Resend Email به Rate Limit نیاز دارد؟
بدون Rate Limit، مهاجم میتواند:
Resend
Resend
Resend
Resend
Resend
...
انجام دهد.
نتیجه:
- مصرف منابع
- Spam
- هزینه سرویس Email
- سوءاستفاده از سیستم
بنابراین Endpoint ارسال مجدد باید Rate Limited باشد.
# Tokenهای قدیمی چه شوند؟
فرض کنید کاربر سه بار درخواست ارسال ایمیل کرده است:
Token A
Token B
Token C
اگر هر سه فعال باشند، مدیریت سیستم پیچیدهتر میشود.
یک روش ساده این است که هنگام تولید Token جدید، Tokenهای فعال قبلی را باطل کنیم:
$stmt = $pdo->prepare(
'UPDATE email_verification_tokens
SET used_at = NOW()
WHERE user_id = :user_id
AND used_at IS NULL'
);
$stmt->execute([
'user_id' => $userId
]);
سپس Token جدید تولید کنیم.
# Email Verification و User Enumeration
در سیستم ثبتنام، ممکن است کاربر ایمیلی وارد کند که قبلاً ثبت شده است.
پیام:
این ایمیل قبلاً ثبت شده است.
از نظر UX مناسب است، اما از نظر امنیتی ممکن است امکان Email Enumeration ایجاد کند.
برای سیستمهایی که این تهدید اهمیت زیادی دارد، میتوان طراحی پاسخ را با توجه به نیاز امنیتی تغییر داد.
برای مثال:
اگر ثبتنام موفق باشد،
ایمیل تأیید برای شما ارسال خواهد شد.
در سیستمهای معمول، باید بین UX و سطح تهدید موردنیاز تصمیمگیری شود.
# آیا Token باید در URL باشد؟
رایجترین روش:
/reset-email?token=...
است.
اما باید توجه کنیم که Token داخل URL قرار میگیرد و ممکن است در برخی محیطها در History، Log یا Referrer دیده شود.
برای کاهش ریسک:
- HTTPS استفاده کنید.
- Token را Log نکنید.
- Token را کوتاهمدت نگه دارید.
- بعد از استفاده آن را باطل کنید.
- از منابع شخص ثالث غیرضروری در صفحه Verification خودداری کنید.
- برای صفحات حساس سیاست Referrer مناسبی تنظیم کنید.
مثلاً:
Referrer-Policy: no-referrer
# آیا میتوان Token را در Cookie قرار داد؟
از نظر طراحی ممکن است روشهای دیگری نیز وجود داشته باشند، اما برای لینک تأیید Email، Token داخل URL یک الگوی رایج است.
نکته اصلی این است که Token:
Random
Short-lived
Single-use
Secure
باشد.
# آیا Email Verification جای Authentication را میگیرد؟
خیر.
این دو مفهوم متفاوت هستند.
Authentication
یعنی:
> این کاربر چه کسی است؟
مثلاً:
Email + Password
Email Verification
یعنی:
> آیا این کاربر به این Email دسترسی دارد؟
بنابراین:
Authentication
+
Email Verification
میتوانند در کنار هم استفاده شوند.
# ارتباط Email Verification با Password Reset
این دو سیستم شباهت زیادی دارند.
Password Reset:
Forgot Password
↓
Token
↓
New Password
Email Verification:
Register
↓
Token
↓
Verify Email
هر دو معمولاً به:
- Token تصادفی
- Hash Token
- Expiration
- Single-use
- HTTPS
- Rate Limiting
نیاز دارند.
# تفاوت Email Verification و Password Reset
| ویژگی | Email Verification | Password Reset |
|---|---|---|
| هدف | اثبات دسترسی به Email | تغییر رمز |
| زمان استفاده | بعد از ثبتنام | هنگام فراموشی رمز |
| تغییر Password | خیر | بله |
| Token | بله | بله |
| Expiration | بله | بله |
| Single-use | بهتر است | ضروری |
| Rate Limit | بله | بله |
# یک پیادهسازی کامل برای ثبتنام
نمونه ساده:
<?php
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
exit('ایمیل نامعتبر است.');
}
if (strlen($password) < 12) {
exit('رمز عبور باید حداقل 12 کاراکتر باشد.');
}
$passwordHash = password_hash(
$password,
PASSWORD_DEFAULT
);
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare(
'INSERT INTO users
(email, password)
VALUES
(:email, :password)'
);
$stmt->execute([
'email' => $email,
'password' => $passwordHash
]);
$userId = $pdo->lastInsertId();
$token = bin2hex(
random_bytes(32)
);
$tokenHash = hash(
'sha256',
$token
);
$expiresAt = date(
'Y-m-d H:i:s',
time() + 1800
);
$stmt = $pdo->prepare(
'INSERT INTO email_verification_tokens
(user_id, token_hash, expires_at)
VALUES
(:user_id, :token_hash, :expires_at)'
);
$stmt->execute([
'user_id' => $userId,
'token_hash' => $tokenHash,
'expires_at' => $expiresAt
]);
$pdo->commit();
$verificationUrl =
'https://example.com/verify-email?token='
. urlencode($token);
// ارسال ایمیل
} catch (Throwable $e) {
$pdo->rollBack();
throw $e;
}
این نمونه آموزشی است و در پروژه واقعی باید کنترلهای امنیتی و مدیریت خطای مناسب نیز اضافه شوند.
# پیادهسازی Endpoint تأیید
<?php
$token = $_GET['token'] ?? '';
if ($token === '') {
exit('لینک تأیید نامعتبر است.');
}
$tokenHash = hash(
'sha256',
$token
);
$stmt = $pdo->prepare(
'SELECT
id,
user_id,
token_hash,
expires_at,
used_at
FROM email_verification_tokens
WHERE token_hash = :token_hash
LIMIT 1'
);
$stmt->execute([
'token_hash' => $tokenHash
]);
$verificationToken = $stmt->fetch(
PDO::FETCH_ASSOC
);
if (!$verificationToken) {
exit('لینک تأیید نامعتبر است.');
}
if ($verificationToken['used_at'] !== null) {
exit('این لینک قبلاً استفاده شده است.');
}
if (
strtotime($verificationToken['expires_at']) < time()
) {
exit('این لینک منقضی شده است.');
}
if (!hash_equals(
$verificationToken['token_hash'],
$tokenHash
)) {
exit('Token نامعتبر است.');
}
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare(
'UPDATE users
SET email_verified_at = NOW()
WHERE id = :user_id
AND email_verified_at IS NULL'
);
$stmt->execute([
'user_id' => $verificationToken['user_id']
]);
$stmt = $pdo->prepare(
'UPDATE email_verification_tokens
SET used_at = NOW()
WHERE id = :id
AND used_at IS NULL'
);
$stmt->execute([
'id' => $verificationToken['id']
]);
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
throw $e;
}
echo 'آدرس ایمیل شما با موفقیت تأیید شد.';
# اشتباهات رایج در Email Verification
1. استفاده از Token قابل حدس
اشتباه:
$token = time();
یا:
$token = rand(100000, 999999);
از Token تصادفی امن استفاده کنید.
2. ذخیره Token خام
اشتباه:
database
↓
raw token
بهتر:
raw token
↓
SHA-256
↓
database
3. Token بدون Expiration
Token نباید برای همیشه معتبر باشد.
4. استفاده چندباره از Token
بعد از موفقیت:
used_at = NOW()
ثبت کنید.
5. Resend بدون Rate Limit
این کار میتواند باعث Spam و مصرف منابع شود.
6. قرار دادن Token در Log
از ثبت URL کامل Verification در Logهای برنامه خودداری کنید.
7. استفاده از HTTP
لینک تأیید باید از HTTPS استفاده کند.
8. فعال کردن حساب بدون Verification
اگر Email Verification برای دسترسی به بخش خاصی ضروری است، نباید صرفاً با ثبتنام حساب را Verified در نظر بگیریم.
# چکلیست Email Verification امن
قبل از انتشار این سیستم بررسی کنید:
- [ ] Token با
random_bytes()تولید میشود. - [ ] Token خام در دیتابیس ذخیره نمیشود.
- [ ] Hash Token ذخیره میشود.
- [ ] Token تاریخ انقضا دارد.
- [ ] Token یکبارمصرف است.
- [ ] Tokenهای قبلی مدیریت میشوند.
- [ ] Resend دارای Rate Limit است.
- [ ] لینک با HTTPS ساخته میشود.
- [ ] Token در Log ذخیره نمیشود.
- [ ] Password با
password_hash()ذخیره میشود. - [ ] فرم ثبتنام CSRF Protection دارد.
- [ ] ارسال Email در پروژههای بزرگ Queue میشود.
- [ ] عملیات مهم با Transaction مدیریت میشود.
- [ ] وضعیت
email_verified_atبعد از تأیید ثبت میشود. - [ ] دسترسیهای حساس بر اساس وضعیت Verification کنترل میشوند.
# FAQ
Email Verification چقدر اعتبار داشته باشد؟
مدت اعتبار باید بر اساس معماری سیستم تعیین شود. بازههایی مانند 15 تا 60 دقیقه برای Tokenهای موقت قابل استفاده هستند.
اگر لینک تأیید منقضی شود چه کنیم؟
کاربر باید بتواند با رعایت Rate Limit درخواست ارسال Token جدید کند.
آیا Token خام را در دیتابیس ذخیره کنیم؟
بهتر است فقط Hash آن ذخیره شود.
آیا کاربر بعد از Verification خودکار Login شود؟
میتوان چنین طراحیای داشت، اما الزام نیست. در بسیاری از سیستمها کاربر پس از تأیید به صفحه Login هدایت میشود.
آیا Email Verification برای همه سایتها ضروری است؟
ضرورت آن به نوع سیستم و نیازهای کسبوکار و امنیتی بستگی دارد. برای سیستمهایی که ایمیل بخشی از هویت یا مسیر بازیابی حساب است، معمولاً بسیار مفید است.
آیا Email Verification همان 2FA است؟
خیر.
Email Verification برای اثبات دسترسی به Email در فرآیندهایی مانند ثبتنام استفاده میشود، اما 2FA یک لایه اضافی احراز هویت هنگام ورود یا عملیات حساس ایجاد میکند.
# جمعبندی
Email Verification یکی از اجزای مهم سیستمهای عضویت مدرن است.
یک طراحی مناسب میتواند به این شکل باشد:
Register
↓
Create User
↓
email_verified_at = NULL
↓
Generate Secure Token
↓
Hash Token
↓
Save Token
↓
Send Email
↓
User Clicks Link
↓
Validate Token
↓
Check Expiration
↓
Verify Email
↓
Invalidate Token
↓
Grant Verified Access
نکته مهم این است که امنیت Email Verification فقط به تولید یک Token محدود نمیشود. باید Token تصادفی و کوتاهعمر باشد، در دیتابیس به صورت Hash ذخیره شود، یکبارمصرف باشد و Endpoint ارسال مجدد نیز Rate Limit داشته باشد.
همچنین همانطور که در Password Reset دیدیم، استفاده از HTTPS، جلوگیری از ثبت اطلاعات حساس در Logها، مدیریت Session، CSRF Protection و ارسال Email به شکل مناسب، بخش مهمی از امنیت کل سیستم هستند.
بعد از پیادهسازی Email Verification، مرحله بعدی برای افزایش امنیت Authentication میتواند اضافه کردن Two-Factor Authentication یا 2FA باشد؛ یعنی کاربر علاوه بر رمز عبور، یک عامل دوم مانند کد یکبارمصرف را نیز برای ورود یا عملیات حساس ارائه کند.





