2 Ekim 2026 - 10:18
PHP Üyelik Sisteminde E-posta Aktivasyonu Nasıl Kurulur? Görseli
PHP

PHP Üyelik Sisteminde E-posta Aktivasyonu Nasıl Kurulur?

Yorumlar

Üyelik formuna bir e-posta adresi yazılması, o adresin başvuran kişiye ait olduğunu göstermez. Aktivasyon bağlantısı adresin posta kutusuna erişimi doğrular. Kullanıcının gerçek kimliğini, yaşını veya bir kurumda çalıştığını tek başına doğrulamaz. Bu ayrım, doğrulanmamış hesaba hangi işlemleri açacağınıza karar verirken önemlidir.

Bu yazıdaki akışta hesap oluşturulur, e-posta onaylanana kadar sınırlı durumda tutulur ve onaydan sonra ilgili özellikler açılır. Doğrulama ile “hesabı askıya alma” durumunu ayrı alanlarda izliyoruz. Böylece adresini doğrulayan fakat yönetici tarafından askıya alınmış kullanıcı yanlışlıkla yeniden etkinleşmez.

Hesap durumunu tek bir status alanına sıkıştırmayın

Örnek kullanıcı tablosunda id, email, email_verified_at, is_active ve auth_version alanlarını kullanıyoruz. Yeni hesapta email_verified_at=NULL olur. is_active hesabın askıda olup olmadığını belirtir. E-posta doğrulaması yalnızca email_verified_at alanını değiştirir.

Tokenı belirli kullanıcıya ve belirli e-posta adresine bağlayın. Aksi hâlde kullanıcı adresini değiştirdikten sonra eski posta kutusuna gönderilen bağlantı yeni adresi de doğrulayabilir. Aşağıdaki tablo örnek uygulamanıza aittir; users.id türünü ve email uzunluğunu kendi şemanızla eşleştirin.

SQL
CREATE TABLE email_verifications (
    token_hash CHAR(64) CHARACTER SET ascii COLLATE ascii_bin PRIMARY KEY,
    user_id BIGINT UNSIGNED NOT NULL,
    email VARCHAR(254) NOT NULL,
    expires_at DATETIME NOT NULL,
    created_at DATETIME NOT NULL,
    INDEX idx_verification_user (user_id),
    INDEX idx_verification_expiry (expires_at)
) ENGINE=InnoDB;

Bağlantı üretirken kullanıcı satırını esas alın

Tokenı bin2hex(random_bytes(32)) ile oluşturun, tablodaki token_hash alanına hash('sha256', $token) yazın. Kayıtta yer alan e-posta, kullanıcı tablosunda saklanan adresin aynısı olmalıdır. Adres normalleştirme politikasını kayıt aşamasında belirleyin; doğrulama sırasında farklı bir kuralla adresi değiştirmeyin.

Yeniden gönderimde kullanıcı satırını işlem içinde kilitleyin, hesabın henüz doğrulanmadığını kontrol edin, önceki aktivasyon kayıtlarını silin ve yeni kaydı ekleyin. Örneğin 24 saatlik süre seçebilirsiniz; bu uygulamanın ürün kararıdır. Tarihleri UTC saklayıp bütün karşılaştırmaları aynı saat diliminde yapın. E-posta kuyruğuna işi commit sonrasında aktarın; kuyruk güvenilirliği gerekiyorsa işlemle beraber yazılan bir outbox tablosu kullanın.

Bağlantı adresi sunucu yapılandırmasından gelsin: https://example.com/eposta-dogrula?token=.... Kullanıcıdan gelen host bilgisiyle URL kurmayın. Yeniden gönderim ekranında hesap ve IP bazında sınır uygulayın. Kayıtlı olmayan adrese farklı yanıt vermek yerine “Uygun bir hesap varsa bağlantı gönderildi” gibi ortak mesaj kullanın.

E-postadaki bağlantının açılması hesabı hemen değiştirmesin

Posta servisleri ve kurumsal güvenlik ürünleri bağlantıları kullanıcıdan önce tarayabilir. GET isteğiyle tokenı tüketmek, kullanıcıya daha ilk tıklamada “bağlantı geçersiz” hatası gösterebilir. GET isteği yalnızca açıklama ve “E-posta adresimi doğrula” düğmesi içeren sayfayı açsın. İşlem, oturuma bağlı CSRF değeriyle gelen POST isteğinde tamamlansın.

Sayfada üçüncü taraf izleme betikleri kullanmayın. Cache-Control: no-store ve Referrer-Policy: no-referrer başlıklarını gönderin. Tokenın erişim loglarında sorgu parametresi olarak kalmasını önleyin. CSRF kontrolü e-postaya erişimin yerine geçmez; formun beklenen tarayıcı akışı içinde gönderildiğini denetleyen ayrı bir katmandır.

Doğrulamayı işlem içinde tamamlayın

Aşağıdaki fonksiyon POST denetleyicisinde, CSRF kontrolünden sonra çağrılır. PDO bağlantısı PDO::ERRMODE_EXCEPTION kullanmalıdır. Kullanıcı satırı önce, token satırı sonra kilitlenir. Yeniden gönderim ve e-posta değiştirme akışları da bu sırayı izlemelidir.

PHP
<?php
declare(strict_types=1);
function verifyEmail(PDO $pdo, string $token): bool
{
    if (!preg_match('/\A[a-f0-9]{64}\z/D', $token)) {
        return false;
    }
    $digest = hash('sha256', $token);
    $find = $pdo->prepare(
        'SELECT user_id FROM email_verifications WHERE token_hash = ?'
    );
    $find->execute([$digest]);
    $userId = $find->fetchColumn();
    if ($userId === false) {
        return false;
    }
    $pdo->beginTransaction();
    try {
        $users = $pdo->prepare(
            'SELECT id, email, email_verified_at, is_active
             FROM users WHERE id = ? FOR UPDATE'
        );
        $users->execute([$userId]);
        $user = $users->fetch(PDO::FETCH_ASSOC);
        $tokens = $pdo->prepare(
            'SELECT user_id, email, expires_at FROM email_verifications
             WHERE token_hash = ? FOR UPDATE'
        );
        $tokens->execute([$digest]);
        $row = $tokens->fetch(PDO::FETCH_ASSOC);
        $now = gmdate('Y-m-d H:i:s');
        if (!$user || !$row || (int) $user['is_active'] !== 1
            || (int) $row['user_id'] !== (int) $userId
            || $user['email_verified_at'] !== null
            || $user['email'] !== $row['email']
            || $row['expires_at'] <= $now) {
            $pdo->rollBack();
            return false;
        }
        $pdo->prepare(
            'UPDATE users SET email_verified_at = ? WHERE id = ?'
        )->execute([$now, $userId]);
        $pdo->prepare(
            'DELETE FROM email_verifications WHERE user_id = ?'
        )->execute([$userId]);
        $pdo->commit();
        return true;
    } catch (Throwable $error) {
        if ($pdo->inTransaction()) {
            $pdo->rollBack();
        }
        throw $error;
    }
}

Bu işlem giriş yetkisi oluşturmaz; yalnızca e-posta doğrulama durumunu kaydeder. Doğrulama başarıyla bittikten sonra kullanıcıya giriş bağlantısı gösterin. Mevcut oturum varsa uygulama hesabın doğrulama durumunu veritabanından yeniden okumalıdır. Oturumda günlerce saklanan eski verified=false değeri, doğrulanmış kullanıcıyı yanlışlıkla kısıtlamaya devam edebilir.

Adres değişince hangi kayıtlar geçersizleşir?

Kullanıcı giriş yapmışken e-postasını değiştirmek istiyorsa yakın zamanda yeniden kimlik doğrulaması isteyin. Kritik hesaplarda yeni adresi ayrı bir pending_email alanında tutup eski doğrulanmış adresi yeni adres doğrulanana kadar korumak daha kontrollü bir akıştır. Buradaki kod ilk kayıt doğrulaması içindir; adres değiştirmede pending_email eşleşmesini ve taşıma adımını ayrıca uygulayın.

İlk kayıt sırasında kullanıcı henüz doğrulanmamış adresini düzeltirse, kullanıcı satırındaki adresi değiştirirken eski aktivasyon tokenlarını da silin. Kodda yer alan adres eşleşmesi ikinci savunma katmanıdır. Token A adresine düzenlenmişken hesapta B adresi bulunuyorsa doğrulama reddedilmelidir.

Parola sıfırlama ile e-posta doğrulama tokenlarını ayrı tablolarda ve ayrı rotalarda tutuyoruz. Bir tokenın iki farklı işleme yetki vermesini böylece önlüyoruz. Parola değiştirme tarafındaki benzer işlem yapısını tek kullanımlık sıfırlama bağlantısı yazısında görebilirsiniz.

“Aktivasyon gelmiyor” şikâyetini ikiye ayırın

Önce uygulamanın gerçekten token oluşturup e-posta işini kuyruğa yazdığını kontrol edin. SMTP sunucusunun mesajı kabul etmesi, mesajın gelen kutusuna yerleştiğini göstermez. Gönderim kaydını mesaj kimliğiyle izleyin; ham tokenı kaydetmeyin. Link açılıyor fakat geçersiz görünüyorsa süre, yeniden gönderimden sonra kullanılan eski e-posta ve adres değişikliği kontrol edilir.

Başvuru: OWASP kimlik doğrulama rehberi ve PHP random_bytes.

Testte bağlantıyı GET ile iki kez açın: hesap hâlâ doğrulanmamış olmalı. Doğru CSRF ile ilk POST hesabı doğrulamalı; aynı tokenla ikinci POST yeni işlem yapmamalı. Yeniden gönderimden önceki bağlantı, adres değişikliğinden önceki bağlantı ve askıya alınmış hesabın bağlantısı da reddedilmelidir.

Benzer Yazılar

Yorumlar ()

Henüz yorum yok. İlk yorum yapan sen ol!

Yorum Yap