Session Loglama Sistemi

Concurrent Session - Detaylı Aktivite Takibi & Güvenlik

19 Aralık 2025
Implementation Plan
Security & Compliance

Basit Anlatım (Herkes İçin)

Ne yapacağız, neden önemli, kullanıcıya faydası ne?

Ne Yapıyoruz?

Muzibu'da bir kullanıcı telefondan giriş yaptıktan sonra bilgisayardan tekrar giriş yaptığında, telefonundaki oturum otomatik kapanıyor. Şimdi bu olayı kayıt altına alacak bir sistem kuruyoruz.

Günlük Hayattan Örnek: Banka hesabınıza farklı bir cihazdan giriş yapıldığında size SMS/email gelir. "Hesabınıza yeni cihazdan giriş yapıldı: iPhone, İstanbul, 19.12.2025 22:15" gibi. Aynı sistemi Muzibu'ya kuruyoruz!

Neden Önemli?

Güvenlik

Hesabınız çalınırsa kim, ne zaman, nereden giriş yaptı görebilirsiniz. Hırsızı yakalama şansınız artar!

Bildirim

Başka cihazdan giriş yapıldığında email/SMS ile hemen haberdar olursunuz. Gerçek zamanlı uyarı!

Geçmiş

Hangi tarihte hangi cihazlardan giriş yaptığınızı görebilirsiniz. Aktivite geçmişi tamamen sizin kontrolünüzde.

Destek

Müşteri desteğine başvurduğunuzda, hangi cihazdan sorun yaşadığınız kayıtlarda olur. Hızlı çözüm!

Kaydedilecek Bilgiler

Yeni Giriş (Kick Eden)

  • 📱 Cihaz: iPhone 13, Chrome
  • 🌍 Konum: İstanbul, Türkiye
  • 🌐 IP: 185.123.45.67
  • 🕐 Saat: 19.12.2025 22:15:30

Eski Oturum (Atılan)

  • 💻 Cihaz: Windows 10, Firefox
  • 🌍 Konum: Ankara, Türkiye
  • 🌐 IP: 78.234.56.89
  • 🕐 Son Aktivite: 19.12.2025 22:10:15

Teknik Detaylar (Geliştiriciler İçin)

Database yapısı, implementasyon, teknolojiler

Database Schema: session_termination_logs

CREATE TABLE session_termination_logs (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,

    -- Kullanıcı Bilgisi
    user_id BIGINT UNSIGNED NOT NULL,
    user_email VARCHAR(255) NOT NULL,
    user_name VARCHAR(255) NOT NULL,

    -- Termination Nedeni
    termination_reason ENUM('lifo', 'manual', 'timeout', 'logout', 'admin') NOT NULL,

    -- YENİ OTURUM (Kick Eden)
    new_session_id VARCHAR(255) NOT NULL,
    new_login_token VARCHAR(64) NOT NULL,
    new_ip_address VARCHAR(45) NOT NULL,
    new_user_agent TEXT NOT NULL,
    new_device_type VARCHAR(20) NOT NULL, -- desktop, mobile, tablet
    new_device_name VARCHAR(100) NOT NULL,
    new_browser VARCHAR(50) NOT NULL,
    new_platform VARCHAR(50) NOT NULL,
    new_country VARCHAR(100) NULL, -- Geolocation
    new_city VARCHAR(100) NULL,
    new_latitude DECIMAL(10, 8) NULL,
    new_longitude DECIMAL(11, 8) NULL,
    new_fingerprint VARCHAR(64) NULL, -- Browser fingerprint

    -- ESKİ OTURUM (Atılan)
    old_session_id VARCHAR(255) NOT NULL,
    old_login_token VARCHAR(64) NULL,
    old_ip_address VARCHAR(45) NULL,
    old_user_agent TEXT NULL,
    old_device_type VARCHAR(20) NULL,
    old_device_name VARCHAR(100) NULL,
    old_browser VARCHAR(50) NULL,
    old_platform VARCHAR(50) NULL,
    old_country VARCHAR(100) NULL,
    old_city VARCHAR(100) NULL,
    old_last_activity TIMESTAMP NULL,

    -- Meta Bilgiler
    notification_sent BOOLEAN DEFAULT FALSE,
    notification_method VARCHAR(20) NULL, -- email, sms, push
    notification_sent_at TIMESTAMP NULL,
    admin_viewed BOOLEAN DEFAULT FALSE,
    admin_viewed_at TIMESTAMP NULL,
    admin_viewed_by BIGINT UNSIGNED NULL,

    -- Timestamps
    terminated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    created_at TIMESTAMP NULL,
    updated_at TIMESTAMP NULL,

    -- Indexes
    INDEX idx_user_id (user_id),
    INDEX idx_terminated_at (terminated_at),
    INDEX idx_termination_reason (termination_reason),
    INDEX idx_notification_sent (notification_sent),

    -- Foreign Keys
    CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    CONSTRAINT fk_admin_viewer FOREIGN KEY (admin_viewed_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Termination Reasons (Kapanma Nedenleri)

LIFO (Last In, First Out)

Yeni cihazdan giriş yapıldı, en eski oturum otomatik kapandı. En yaygın senaryo.

Manual (Kullanıcı Kapattı)

Kullanıcı "Diğer Cihazlardan Çıkış Yap" butonuna bastı. Bilinçli aksiyon.

Timeout (Zaman Aşımı)

60 dakika aktivite yoksa session DB'den silinir. Otomatik temizlik.

Logout (Normal Çıkış)

Kullanıcı "Çıkış Yap" butonuna bastı. Temiz sonlanma.

Admin (Yönetici Müdahalesi)

Admin şüpheli aktivite tespit edip kullanıcının session'ını kapattı. Güvenlik önlemi.

DeviceService.php - Log Kaydetme

// DeviceService.php:468 - enforceDeviceLimit() metoduna eklenecek

protected function enforceDeviceLimit(User $user, string $currentSessionId): void
{
    $limit = $this->getDeviceLimit($user);

    $sessions = DB::table($this->table)
        ->where('user_id', $user->id)
        ->orderBy('last_activity', 'asc')
        ->get();

    $count = $sessions->count();

    if ($count > $limit) {
        $toRemove = $count - $limit;

        $oldSessions = $sessions
            ->filter(fn($s) => $s->session_id !== $currentSessionId)
            ->take($toRemove);

        // Yeni session bilgisi
        $newSession = DB::table($this->table)
            ->where('session_id', $currentSessionId)
            ->first();

        foreach ($oldSessions as $old) {
            // 🔥 LOG KAYDET (termination_reason: lifo)
            $this->logSessionTermination(
                user: $user,
                reason: 'lifo',
                newSession: $newSession,
                oldSession: $old
            );

            // Session'ı sil
            DB::table($this->table)->where('id', $old->id)->delete();
        }
    }
}

SessionTerminationLogger.php (Yeni Service)

namespace Modules\Muzibu\App\Services;

class SessionTerminationLogger
{
    public function logTermination(
        User $user,
        string $reason,
        $newSession,
        $oldSession
    ): void {
        $logData = [
            // User
            'user_id' => $user->id,
            'user_email' => $user->email,
            'user_name' => $user->name,
            'termination_reason' => $reason,

            // New Session
            'new_session_id' => $newSession->session_id,
            'new_login_token' => $newSession->login_token,
            'new_ip_address' => $newSession->ip_address,
            'new_user_agent' => $newSession->user_agent,
            'new_device_type' => $newSession->device_type,
            'new_device_name' => $newSession->device_name,
            'new_browser' => $newSession->browser,
            'new_platform' => $newSession->platform,

            // Geolocation (IP'den)
            'new_country' => $this->getCountryFromIp($newSession->ip_address),
            'new_city' => $this->getCityFromIp($newSession->ip_address),

            // Old Session
            'old_session_id' => $oldSession->session_id,
            'old_login_token' => $oldSession->login_token ?? null,
            'old_ip_address' => $oldSession->ip_address,
            'old_user_agent' => $oldSession->user_agent,
            'old_device_type' => $oldSession->device_type,
            'old_device_name' => $oldSession->device_name,
            'old_browser' => $oldSession->browser,
            'old_platform' => $oldSession->platform,
            'old_last_activity' => $oldSession->last_activity,

            'terminated_at' => now(),
        ];

        DB::table('session_termination_logs')->insert($logData);

        // Notification gönder
        $this->sendNotification($user, $logData);
    }

    protected function sendNotification(User $user, array $logData): void
    {
        // Email/SMS notification
        // Event dispatch: SessionTerminatedEvent
    }
}

Geolocation Service (IP → Konum)

IP adresinden şehir/ülke bilgisi çekmek için ücretsiz/ücretli servisler:

ÜCRETSİZ ip-api.com

  • ✅ 45 request/dakika (ücretsiz)
  • ✅ Şehir, ülke, koordinat
  • ❌ HTTPS yok (ücretli)

ÜCRETLİ ipstack.com

  • ✅ 10.000 req/ay (ücretsiz)
  • ✅ HTTPS destekli
  • ✅ ISP, timezone bilgisi
// Geolocation örnek
protected function getLocationFromIp(string $ip): array
{
    try {
        $response = Http::get("http://ip-api.com/json/{$ip}");
        $data = $response->json();

        return [
            'country' => $data['country'] ?? null,
            'city' => $data['city'] ?? null,
            'latitude' => $data['lat'] ?? null,
            'longitude' => $data['lon'] ?? null,
        ];
    } catch (\Exception $e) {
        return ['country' => null, 'city' => null];
    }
}

Önerilerim & Ekstra Özellikler

1. Browser Fingerprinting

Kullanıcının cihazını benzersiz şekilde tanımlama (IP değişse bile tanır)

  • FingerprintJS: Tarayıcı özellikleri, font listesi, canvas hash → Benzersiz ID
  • Kullanım: VPN ile IP değiştirse bile aynı cihazı tanır
  • ⚠️ Privacy: KVKK uyumlu kullanılmalı, kullanıcıya bilgi verilmeli

2. Gerçek Zamanlı Bildirim

Başka cihazdan giriş yapıldığında anında uyarı

  • 📧 Email: "Yeni cihazdan giriş: iPhone, İstanbul, 22:15"
  • 📱 SMS: "Muzibu hesabınıza yeni cihazdan erişildi" (opsiyonel)
  • 🔔 Push Notification: Mobil app varsa anlık bildirim

3. Admin Dashboard

Yöneticiler için log görüntüleme ve filtreleme paneli

  • 📊 Grafik: Günlük/aylık termination istatistikleri
  • 🔍 Filtre: Kullanıcıya göre, tarihe göre, IP'ye göre arama
  • 🚨 Alert: Şüpheli aktivite tespiti (aynı IP'den çok sayıda termination)

4. Kullanıcı Aktivite Geçmişi

Kullanıcı kendi aktivitelerini görebilmeli

  • 📜 Geçmiş: "Son 30 günde 5 farklı cihazdan giriş yaptınız"
  • ⚠️ Uyarı: "Tanımadığınız bir giriş mi? Şifrenizi değiştirin"
  • 🔒 Aksiyon: "Tüm cihazlardan çıkış yap" butonu

5. GDPR/KVKK Uyumu

Kişisel veri koruma yasalarına uyum

  • 📅 Retention: Logları max 90 gün saklayın (GDPR önerisi)
  • 🗑️ Auto-Delete: 90 gün sonra otomatik silme cron job
  • 📄 Export: Kullanıcı kendi verilerini CSV/PDF olarak indirebilmeli

6. Şüpheli Aktivite Tespiti

Otomatik güvenlik kontrolleri

  • 🚨 Rate Limit: 1 saatte 10+ termination → Admin uyarısı
  • 🌍 Geo Alert: Farklı ülkeden giriş → Email uyarısı
  • Time Alert: Gece 02:00'de giriş → Kullanıcıya bildirim

Implementation Adımları (Step by Step)

1

Migration: session_termination_logs Tablosu

Tenant database'e yeni tablo oluştur

php artisan make:migration create_session_termination_logs_table --path=database/migrations/tenant
2

Service: SessionTerminationLogger

Log kaydetme servisi oluştur

Modules/Muzibu/app/Services/SessionTerminationLogger.php
3

DeviceService.php Güncelleme

enforceDeviceLimit(), terminateSession() metodlarına log ekleme

$this->logger->logTermination(...)
4

Event: SessionTerminatedEvent

Event/Listener sistemi ile notification gönderme

event(new SessionTerminatedEvent($user, $logData))
5

Notification: Email/SMS Template

Kullanıcıya gönderilecek bildirim şablonu

resources/views/emails/session-terminated.blade.php
6

Admin Dashboard: Log Görüntüleme

Admin panel Livewire component

/admin/session-logs
7

Cron Job: Auto-Delete Old Logs

90 gün sonra logları otomatik sil (GDPR)

Schedule::daily()->deleteOldSessionLogs()