PHP Intermediate

PHP Sessions and Cookies Management

CodingerWeb
CodingerWeb
21 views 60 min read

PHP Sessions and Cookies

Sessions and cookies are essential for maintaining state and user data across multiple page requests in web applications.

Understanding Sessions

Sessions store data on the server and use a session ID to identify users:

<?php
// Start a session (must be called before any output)
session_start();

// Store data in session
$_SESSION["username"] = "john_doe";
$_SESSION["user_id"] = 123;
$_SESSION["role"] = "admin";

// Access session data
echo "Welcome, " . $_SESSION["username"];

// Check if session variable exists
if (isset($_SESSION["user_id"])) {
    echo "User ID: " . $_SESSION["user_id"];
}

// Remove specific session variable
unset($_SESSION["role"]);

// Destroy entire session
session_destroy();
?>

Session Configuration

Configure session settings for security and performance:

<?php
// Configure session before starting
ini_set("session.cookie_lifetime", 3600); // 1 hour
ini_set("session.cookie_httponly", 1);    // Prevent JavaScript access
ini_set("session.cookie_secure", 1);      // HTTPS only (in production)
ini_set("session.use_strict_mode", 1);    // Prevent session fixation

// Or set in php.ini:
// session.cookie_lifetime = 3600
// session.cookie_httponly = 1
// session.cookie_secure = 1
// session.use_strict_mode = 1

session_start();

// Regenerate session ID for security
session_regenerate_id(true);
?>

Session-Based Authentication

Implement user login and authentication:

<?php
// login.php
session_start();

function authenticateUser($username, $password) {
    // In real application, check against database
    $users = [
        "admin" => password_hash("admin123", PASSWORD_DEFAULT),
        "user" => password_hash("user123", PASSWORD_DEFAULT)
    ];
    
    if (isset($users[$username]) && 
        password_verify($password, $users[$username])) {
        return true;
    }
    return false;
}

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $username = $_POST["username"] ?? "";
    $password = $_POST["password"] ?? "";
    
    if (authenticateUser($username, $password)) {
        // Regenerate session ID to prevent session fixation
        session_regenerate_id(true);
        
        $_SESSION["logged_in"] = true;
        $_SESSION["username"] = $username;
        $_SESSION["login_time"] = time();
        
        header("Location: dashboard.php");
        exit;
    } else {
        $error = "Invalid username or password";
    }
}
?>

<form method="POST">
    <?php if (isset($error)): ?>
        <div class="error"><?= $error ?></div>
    <?php endif; ?>
    
    <input type="text" name="username" placeholder="Username" required>
    <input type="password" name="password" placeholder="Password" required>
    <button type="submit">Login</button>
</form>

Session Security

Protect sessions from common attacks:

<?php
// auth_functions.php
function isLoggedIn() {
    return isset($_SESSION["logged_in"]) && $_SESSION["logged_in"] === true;
}

function requireLogin() {
    if (!isLoggedIn()) {
        header("Location: login.php");
        exit;
    }
}

function checkSessionTimeout($timeout = 1800) { // 30 minutes
    if (isset($_SESSION["last_activity"])) {
        if (time() - $_SESSION["last_activity"] > $timeout) {
            session_destroy();
            header("Location: login.php?timeout=1");
            exit;
        }
    }
    $_SESSION["last_activity"] = time();
}

function validateSessionFingerprint() {
    $fingerprint = md5($_SERVER["HTTP_USER_AGENT"] . $_SERVER["REMOTE_ADDR"]);
    
    if (!isset($_SESSION["fingerprint"])) {
        $_SESSION["fingerprint"] = $fingerprint;
    } elseif ($_SESSION["fingerprint"] !== $fingerprint) {
        // Possible session hijacking
        session_destroy();
        header("Location: login.php?security=1");
        exit;
    }
}

// protected_page.php
session_start();
require_login();
checkSessionTimeout();
validateSessionFingerprint();
?>

Understanding Cookies

Cookies store data on the client side:

<?php
// Set a cookie
setcookie("username", "john_doe", time() + 3600, "/"); // Expires in 1 hour

// Set cookie with additional options
setcookie("preferences", "dark_theme", [
    "expires" => time() + (30 * 24 * 3600), // 30 days
    "path" => "/",
    "domain" => ".example.com",
    "secure" => true,     // HTTPS only
    "httponly" => true,   // No JavaScript access
    "samesite" => "Strict" // CSRF protection
]);

// Read cookies
if (isset($_COOKIE["username"])) {
    echo "Welcome back, " . $_COOKIE["username"];
}

// Delete cookie (set expiration to past)
setcookie("username", "", time() - 3600, "/");
?>

Remember Me Functionality

<?php
// login.php with remember me
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $username = $_POST["username"];
    $password = $_POST["password"];
    $remember = isset($_POST["remember"]);
    
    if (authenticateUser($username, $password)) {
        session_regenerate_id(true);
        $_SESSION["logged_in"] = true;
        $_SESSION["username"] = $username;
        
        if ($remember) {
            // Generate secure token
            $token = bin2hex(random_bytes(32));
            
            // Store token in database with user ID and expiration
            storeRememberToken($username, $token, time() + (30 * 24 * 3600));
            
            // Set cookie with token
            setcookie("remember_token", $token, [
                "expires" => time() + (30 * 24 * 3600),
                "path" => "/",
                "secure" => true,
                "httponly" => true,
                "samesite" => "Strict"
            ]);
        }
        
        header("Location: dashboard.php");
        exit;
    }
}

// Check remember me token on subsequent visits
function checkRememberMe() {
    if (!isLoggedIn() && isset($_COOKIE["remember_token"])) {
        $token = $_COOKIE["remember_token"];
        $user = validateRememberToken($token);
        
        if ($user) {
            session_regenerate_id(true);
            $_SESSION["logged_in"] = true;
            $_SESSION["username"] = $user["username"];
            
            // Generate new token for security
            $newToken = bin2hex(random_bytes(32));
            updateRememberToken($user["id"], $newToken);
            
            setcookie("remember_token", $newToken, [
                "expires" => time() + (30 * 24 * 3600),
                "path" => "/",
                "secure" => true,
                "httponly" => true,
                "samesite" => "Strict"
            ]);
        } else {
            // Invalid token, remove cookie
            setcookie("remember_token", "", time() - 3600, "/");
        }
    }
}
?>

User Preferences

<?php
// Save user preferences
function savePreference($key, $value, $duration = 30) {
    $preferences = $_COOKIE["preferences"] ?? "{}";
    $preferences = json_decode($preferences, true) ?: [];
    
    $preferences[$key] = $value;
    
    setcookie("preferences", json_encode($preferences), [
        "expires" => time() + ($duration * 24 * 3600),
        "path" => "/",
        "httponly" => true,
        "samesite" => "Lax"
    ]);
}

// Get user preference
function getPreference($key, $default = null) {
    $preferences = $_COOKIE["preferences"] ?? "{}";
    $preferences = json_decode($preferences, true) ?: [];
    
    return $preferences[$key] ?? $default;
}

// Usage
if ($_POST["theme"]) {
    savePreference("theme", $_POST["theme"]);
}

$theme = getPreference("theme", "light");
echo "<body class="theme-$theme">";
?>

Shopping Cart with Sessions

Implement a shopping cart using sessions:

<?php
// cart.php
session_start();

class ShoppingCart {
    public function __construct() {
        if (!isset($_SESSION["cart"])) {
            $_SESSION["cart"] = [];
        }
    }
    
    public function addItem($id, $name, $price, $quantity = 1) {
        if (isset($_SESSION["cart"][$id])) {
            $_SESSION["cart"][$id]["quantity"] += $quantity;
        } else {
            $_SESSION["cart"][$id] = [
                "name" => $name,
                "price" => $price,
                "quantity" => $quantity
            ];
        }
    }
    
    public function removeItem($id) {
        unset($_SESSION["cart"][$id]);
    }
    
    public function updateQuantity($id, $quantity) {
        if ($quantity <= 0) {
            $this->removeItem($id);
        } else {
            $_SESSION["cart"][$id]["quantity"] = $quantity;
        }
    }
    
    public function getItems() {
        return $_SESSION["cart"];
    }
    
    public function getTotal() {
        $total = 0;
        foreach ($_SESSION["cart"] as $item) {
            $total += $item["price"] * $item["quantity"];
        }
        return $total;
    }
    
    public function getItemCount() {
        $count = 0;
        foreach ($_SESSION["cart"] as $item) {
            $count += $item["quantity"];
        }
        return $count;
    }
    
    public function clear() {
        $_SESSION["cart"] = [];
    }
}

// Usage
$cart = new ShoppingCart();

if ($_POST["action"] == "add") {
    $cart->addItem($_POST["id"], $_POST["name"], $_POST["price"], $_POST["quantity"]);
}

if ($_POST["action"] == "remove") {
    $cart->removeItem($_POST["id"]);
}

if ($_POST["action"] == "update") {
    $cart->updateQuantity($_POST["id"], $_POST["quantity"]);
}
?>

<h2>Shopping Cart (<?= $cart->getItemCount() ?> items)</h2>

<?php foreach ($cart->getItems() as $id => $item): ?>
    <div class="cart-item">
        <h3><?= htmlspecialchars($item["name"]) ?></h3>
        <p>Price: $<?= number_format($item["price"], 2) ?></p>
        <p>Quantity: <?= $item["quantity"] ?></p>
        <p>Subtotal: $<?= number_format($item["price"] * $item["quantity"], 2) ?></p>
        
        <form method="POST" style="display: inline;">
            <input type="hidden" name="action" value="remove">
            <input type="hidden" name="id" value="<?= $id ?>">
            <button type="submit">Remove</button>
        </form>
    </div>
<?php endforeach; ?>

<h3>Total: $<?= number_format($cart->getTotal(), 2) ?></h3>

Session Storage Alternatives

Store sessions in database for scalability:

<?php
// Custom session handler
class DatabaseSessionHandler implements SessionHandlerInterface {
    private $pdo;
    
    public function __construct($pdo) {
        $this->pdo = $pdo;
    }
    
    public function open($savePath, $sessionName) {
        return true;
    }
    
    public function close() {
        return true;
    }
    
    public function read($id) {
        $stmt = $this->pdo->prepare("SELECT data FROM sessions WHERE id = ? AND expires > NOW()");
        $stmt->execute([$id]);
        
        $result = $stmt->fetchColumn();
        return $result ? $result : "";
    }
    
    public function write($id, $data) {
        $stmt = $this->pdo->prepare("REPLACE INTO sessions (id, data, expires) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 1 HOUR))");
        return $stmt->execute([$id, $data]);
    }
    
    public function destroy($id) {
        $stmt = $this->pdo->prepare("DELETE FROM sessions WHERE id = ?");
        return $stmt->execute([$id]);
    }
    
    public function gc($maxlifetime) {
        $stmt = $this->pdo->prepare("DELETE FROM sessions WHERE expires < NOW()");
        return $stmt->execute();
    }
}

// Set custom session handler
$handler = new DatabaseSessionHandler($pdo);
session_set_save_handler($handler, true);
session_start();
?>

Best Practices

Security and performance recommendations:

<?php
// Session security checklist
session_start();

// 1. Regenerate session ID on privilege changes
if ($userJustLoggedIn) {
    session_regenerate_id(true);
}

// 2. Set secure session configuration
ini_set("session.cookie_httponly", 1);
ini_set("session.cookie_secure", 1); // HTTPS only
ini_set("session.use_strict_mode", 1);

// 3. Implement session timeout
if (isset($_SESSION["last_activity"]) && 
    (time() - $_SESSION["last_activity"] > 1800)) {
    session_destroy();
    header("Location: login.php");
    exit;
}
$_SESSION["last_activity"] = time();

// 4. Validate session data
function validateSession() {
    if (!isset($_SESSION["user_id"]) || 
        !isset($_SESSION["username"]) ||
        !is_numeric($_SESSION["user_id"])) {
        return false;
    }
    return true;
}

// 5. Clean up sessions
function cleanupSessions() {
    // Remove expired sessions from database
    $stmt = $pdo->prepare("DELETE FROM sessions WHERE expires < NOW()");
    $stmt->execute();
}

// 6. Secure logout
function logout() {
    $_SESSION = [];
    
    if (ini_get("session.use_cookies")) {
        $params = session_get_cookie_params();
        setcookie(session_name(), "", time() - 42000,
            $params["path"], $params["domain"],
            $params["secure"], $params["httponly"]
        );
    }
    
    session_destroy();
}
?>

Practice Exercise

Create a complete user management system with:

  • User registration and login
  • Session-based authentication
  • Remember me functionality
  • User preferences (theme, language)
  • Shopping cart functionality
  • Proper security measures

Key Takeaways

  • Sessions store data on server, cookies on client
  • Always regenerate session ID on privilege changes
  • Implement session timeouts and validation
  • Use secure cookie settings in production
  • Consider database storage for scalable applications