PHP ile Güveli Session ve Cookie (Beni Hatırla) Sistemi

Fırat Kaya 9 Haziran 2020 22:26

Herkese merhaba, öncelikle blogumun ilk yazısına hoş geldiniz. Bu yazımda sizlere PHP ile güvenli bir Session ve Cookie sistemi nasıl yapılır, bu sistemin altında yatan mantık nedir gibi sorulara cevap bulacağız.

Öncelikle zaafiyeti tanıyalım

PHP’de beni hatırla sistemi yaptığımızda eğer önlem almazsak bazı zaafiyetler ortaya çıkmaktadır.

Seneryomuz şu şekilde olsun: Beni hatırla sistemi için her kullanıcıya unique (benzersiz) bir token oluşturuyorsunuz ve bunu veritabanında tutuyorsunuz. Kullanıcı giriş yaptığında bu tokeni bir cookie (çerez) olarak kullanıcının tarayıcısına kaydediyorsunuz ve bir session oluşturuyorsunuz. Bu session süresi bittiğinde cookieyi kontrol edip tekrar bir session oluşturarak bir döngüye sokuyorsunuz. Buraya kadar her şey tamam, peki kullanıcıya ait olan bu cookie, kötü niyetli şahısların eline geçerse?

İşte burada çözüm arayışına giriyoruz. Peki bu sorunu nasıl çözeriz?

Çözüm: Beni Hatırla/Session Sistemini Tanıyalım, Temel Mantığı Nedir?

Öncelikle cookie’yi kullanan kişinin o kişi mi olup olmadığına bakmamız gerek. Peki bunu nasıl yapacağız? Kullanıcıyı IP adresinden tanıyabiliriz. Yani bir kullanıcı bir cookie oluşturduğu zaman ve bu cookieyi veritabanından çektiğimiz zaman cookie oluşturulurkenki IP adresine de bakıp karşılaştırmamız gerek. Eğer cookie oluştururkenki IP adresi ile şu anki IP adresi eşleşiyorsa bu kullanıcının yeni sesion oluşturmasına izin verilir. Peki bir kullanıcı sadece bir IP adresinden mi siteye erişebilcek? Hayır bunun için bir veritabanı tablosu tasarlayacağız ve bu tabloda bir kullanıcı birden fazla session oluşturabilecek.

Çözüm: Hadi kodlamaya başlayalım!

Veritabanı Tasarımı

Öncelikle bir veritabanı tasarımı yapmamız gerek. Kullanıcıların oturumları için oluşturduğumuz tokenleri, kullanıcıların session’u oluştururkenki IP adreslerini ve tokenlerin son kullanım tarihlerini veritabanına kaydetmemiz gerekli.

Sizlere veritabanı tasarımını anlaşılır bir şekilde göstermek için dbdiagram.io adresinden oluşturduğum diyagramı ekledim. Faydalı bir araç kullanmanızı tavsiye ederim. (Çerez bilgileri de böyle aralara sıkıştırmak faydalı oluyor.)

php session database

Kullanıcı Kaydı

function createDuration($duration = 0) {
  return date("Y-m-d H:i:s", (strtotime(date("Y-m-d H:i:s")) + ($duration * 86400)));
}

Öncelikle bu fonksiyon elimizde bulunsun, son kullanım tarihi için gerekli. “createDuration(x)” ile belirlediğiniz gün sonrasını çıktı olarak veriyor örneğin: createDuration(30) dediğimizde şu anki güne 30 gün ekleyip yeni tarihi DATETIME formatında çıktı olarak veriyor.

Kullanıcıyı kayıt ettikten sonra son eklenen ID’yi bir değişkene atamamız gerekiyor.

$accountID = $db->lastInsertId();
$loginToken = md5(uniqid(mt_rand(), true));
$insertSessions = $db->prepare("INSERT INTO Sessions (accountID, loginToken, creationIP, expiryDate, creationDate) VALUES (?, ?, ?, ?, ?)");
$insertSessions->execute(array($accountID, $loginToken, getIP(), createDuration(0.01666666666), date("Y-m-d H:i:s")));

Bu komut ile 24 dakikalık bir session oluşturuyoruz, bu PHP’nin varsayılan session kullanım süresidir.

Kullanıcı Girişi

Şimdi kullanıcılar giriş yapınca beni hatırla kutusunu seçip seçmediğini kontrol edip ona göre kullanıcıda cookie oluşturmamız gerekli.

Eğer beni hatırlayı seçerse 1 yıllık bir session oluşturacak.

$loginToken = md5(uniqid(mt_rand(), true));
$insertSessions = $db->prepare("INSERT INTO Sessions (accountID, loginToken, creationIP, expiryDate, creationDate) VALUES (?, ?, ?, ?, ?)");
$insertSessions->execute(array($readAccount["id"], $loginToken, getIP(), createDuration(((isset($_POST["rememberMe"])) ? 365 : 0.01666666666)), date("Y-m-d H:i:s")));

if (isset($_POST["rememberMe"])) {
  setcookie("rememberMe", $loginToken, time()+86400*365, "/");
}
$_SESSION["login"] = $loginToken;

Kullanıcının Session Kaydını Kontrol Etme

getIP fonksiyonu kullanıcının IP adresini döndürmektedir.

Burada yaptığımız işlem kullanıcının cookie kaydı varsa bunu kontrol edip php ile session oluşturmak. Eğer php session kaydı var ise bu session’a ait kullanıcı hesabı var mı bunu kontrol eder.

function getIP() {
  if (getenv("HTTP_CLIENT_IP")) {
    $ip = getenv("HTTP_CLIENT_IP");
  }
  else if (getenv("HTTP_X_FORWARDED_FOR")) {
    $ip = getenv("HTTP_X_FORWARDED_FOR");
    if (strstr($ip, ",")) {
      $tmp = explode (",", $ip);
      $ip = trim($tmp[0]);
    }
  }
  else {
    $ip = getenv("REMOTE_ADDR");
  }
  return $ip;
}
function go($url) {
  header("Location: $url");
  exit();
}

if (isset($_COOKIE["rememberMe"]) || isset($_SESSION["login"])) {
  $loginToken = ((isset($_COOKIE["rememberMe"])) ? $_COOKIE["rememberMe"] : ((isset($_SESSION["login"])) ? $_SESSION["login"] : null));
  $accountSearch = $db->prepare("SELECT A.*, ASe.loginToken FROM Accounts A INNER JOIN Sessions ASe ON A.id = ASe.accountID WHERE ASe.loginToken = ? AND ASe.creationIP = ? AND ASe.expiryDate > ?");
  $accountSearch->execute(array($loginToken, getIP(), date("Y-m-d H:i:s")));
  $readAccount = $accountSearch->fetch();
  if ($accountSearch->rowCount() > 0) {
    $_SESSION["login"] = $readAccount["loginToken"];
    if (!isset($_COOKIE["rememberMe"])) {
      $updateAccountsSessions = $db->prepare("UPDATE Sessions SET expiryDate = ? WHERE accountID = ? AND loginToken = ?");
      $updateAccountsSessions->execute(array(createDuration(0.01666666666), $readAdmin["id"], $loginToken));
    }
  }
  else {
    if (isset($_COOKIE["rememberMe"])) {
      setcookie("rememberMe", "", time()-86400*365, '/');
    }
    session_destroy();
    go("/login");
  }
}

Kullanıcının Şifre Değiştirmesi

Kullanıcı şifre değiştirdiğinde o anki sessionları geçersiz olmalıdır. Yoksa şifre değişmesine rağmen diğer cihazlardan giriş yapmaya devam eder. Bu yüzden şifre değiştirildiğinde güncel session kayıtları silinir ve şifre değiştiren kullanıcıya yeni bir kayıt oluşturulur.

$deleteAccountSessions = $db->prepare("DELETE FROM Sessions WHERE accountID = ?");
$deleteAccountSessions->execute(array($readAccount["id"]));
$insertAccountSessions = $db->prepare("INSERT INTO Sessions (accountID, loginToken, creationIP, expiryDate, creationDate) VALUES (?, ?, ?, ?, ?)");
$insertAccountSessions->execute(array($readAccount["id"], $loginToken, getIP(), createDuration(((isset($_COOKIE["rememberMe"])) ? 365 : 0.01666666666)), date("Y-m-d H:i:s")));

Sonuç:

Evet ilk yazımın sonuna geldik. Artık PHP ile güvenli Session ve Cookie sistemi nasıl yapılır bunu biliyorsunuz. Aktif olduğum büyük projelerde bu yöntemi her zaman kullanırım. Artık daha güvenli ve sürdürülebilir bir session (oturum) sisteminiz olacak. Aklınıza takılan tüm soruları yorumlar kısmından bana sorabilirsiniz. Bir sonraki yazımda görüşmek üzere, esen kalın 🙂

İş teklifleri için İletişim sayfasından bana ulaşabilirsiniz.

php beni hatırla php cookie php session auth

Bir cevap yazın

Yorumlar (1)

Emre Can Avatar

selam pdo çalışmalar yapıyorum vermiş olduğunuz Session Kaydı örneği ile işlemlerini yaparken kayit.php ile girişte sessin oluşuyor ve veri tabanına kayıt ediyor lakin giris.php sayfasına entegre edemedim pdo ile giriş kaydında Session varmı yokmu gibisinden nasıl yapabilirim veya bir giriş formu ile kod yazabilirmisin rica etsem