Sessions & Cookies

Encrypted session management with file and database storage, plus secure cookie handling

Automatic Encryption: All session data and cookies are automatically encrypted using XChaCha20-Poly1305 AEAD cipher. No middleware configuration needed.

Overview

Larafony's session and cookie system provides:

Session Management

Basic Session Usage

use Larafony\Framework\Storage\Session\SessionManager;

// Create and start session (auto-selects handler from config)
$session = SessionManager::create();

// Store data (automatically encrypted)
$session->set('user_id', 42);
$session->set('cart', ['item1', 'item2', 'item3']);
$session->set('preferences', [
    'theme' => 'dark',
    'language' => 'en'
]);

// Retrieve data (automatically decrypted)
$userId = $session->get('user_id'); // 42
$cart = $session->get('cart', []); // ['item1', 'item2', 'item3']

// Check existence
if ($session->has('user_id')) {
    // User is logged in
}

// Get all session data
$allData = $session->all();

// Remove specific item
$session->remove('cart');

// Clear all session data
$session->clear();

// Get session ID
$sessionId = $session->getId();

Session Security

// Regenerate session ID (best practice after login)
$session->regenerateId(deleteOldSession: true);

// Destroy session (logout)
$session->destroy();
Security Best Practice: Always regenerate the session ID after authentication to prevent session fixation attacks.

Session Storage Handlers

File-Based Sessions

Default handler, stores encrypted session data as files:

// config/session.php
return [
    'handler' => \Larafony\Framework\Storage\Session\Handlers\FileSessionHandler::class,
    'path' => sys_get_temp_dir() . '/sessions',
    // or: storage_path('framework/sessions')
];

Features:

Database-Backed Sessions

Recommended for production and multi-server setups:

// config/session.php
return [
    'handler' => \Larafony\Framework\Storage\Session\Handlers\DatabaseSessionHandler::class,
];

Features:

Setup Database Sessions

Step 1: Generate migration

php bin/console table:session

Step 2: Run migration

php bin/console migrate

Step 3: Configure handler in config/session.php

return [
    'handler' => \Larafony\Framework\Storage\Session\Handlers\DatabaseSessionHandler::class,

    'cookie_params' => [
        'lifetime' => 7200, // 2 hours
        'path' => '/',
        'domain' => '',
        'secure' => true, // HTTPS only
        'httponly' => true, // No JavaScript access
        'samesite' => 'Strict', // CSRF protection
    ],
];

Database Schema

The sessions table stores:

CREATE TABLE sessions (
    id VARCHAR(255) PRIMARY KEY,           -- Session ID
    payload TEXT NOT NULL,                 -- Encrypted session data
    last_activity INT NOT NULL,            -- Unix timestamp
    user_ip VARCHAR(45) NULL,              -- IPv4 or IPv6
    user_agent TEXT NULL,                  -- Browser info
    user_id BIGINT UNSIGNED NULL,          -- Linked user (if authenticated)
    INDEX idx_last_activity (last_activity),
    INDEX idx_user_id (user_id)
);

Cookie Management

Basic Cookie Usage

use Larafony\Framework\Storage\CookieManager;
use Larafony\Framework\Storage\CookieOptions;

$cookies = new CookieManager();

// Set encrypted cookie with defaults
$cookies->set('user_preferences', [
    'theme' => 'dark',
    'language' => 'en',
    'notifications' => true
]);

// Retrieve and decrypt automatically
$preferences = $cookies->get('user_preferences');
// Returns: ['theme' => 'dark', 'language' => 'en', 'notifications' => true]

// Get with default value
$settings = $cookies->get('settings', ['default' => 'value']);

// Get all cookies (decrypted)
$allCookies = $cookies->all();

// Remove cookie
$cookies->remove('user_preferences');

Cookie Options

use Larafony\Framework\Storage\CookieOptions;

// Custom cookie options
$options = new CookieOptions(
    expires: time() + 86400,  // 24 hours from now
    path: '/admin',           // Available only under /admin
    domain: '.example.com',   // Available on all subdomains
    secure: true,             // HTTPS only
    httponly: true,           // No JavaScript access
    samesite: 'Strict'        // Strict CSRF protection
);

$cookies->set('admin_token', 'secret_value', $options);

Secure Defaults

CookieOptions provides secure defaults automatically:

// Default options (without parameters):
new CookieOptions()
// Equivalent to:
new CookieOptions(
    expires: time() + 3600,           // 1 hour
    path: '/',
    domain: '',
    secure: (HTTPS detected),         // Auto-detects HTTPS
    httponly: true,                   // Always true by default
    samesite: 'Lax'                   // Balanced security
)
Production Tip: Always use secure: true and samesite: 'Strict' in production to maximize security.

Session Configuration

Complete Configuration Example

// config/session.php
use Larafony\Framework\Config\Environment\EnvReader;
use Larafony\Framework\Storage\Session\Handlers\DatabaseSessionHandler;
use Larafony\Framework\Storage\Session\Handlers\FileSessionHandler;

return [
    // Session handler: file or database
    'handler' => EnvReader::read('SESSION_DRIVER') === 'database'
        ? DatabaseSessionHandler::class
        : FileSessionHandler::class,

    // File storage path (for FileSessionHandler)
    'path' => EnvReader::read('SESSION_PATH', sys_get_temp_dir() . '/sessions'),

    // Cookie configuration
    'cookie_params' => [
        'lifetime' => (int) EnvReader::read('SESSION_LIFETIME', '7200'),
        'path' => EnvReader::read('SESSION_PATH_COOKIE', '/'),
        'domain' => EnvReader::read('SESSION_DOMAIN', ''),
        'secure' => EnvReader::read('SESSION_SECURE_COOKIE') === 'true'
            || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'),
        'httponly' => EnvReader::read('SESSION_HTTP_ONLY', 'true') === 'true',
        'samesite' => EnvReader::read('SESSION_SAME_SITE', 'Lax'),
    ],
];

Environment Variables

# .env file

# Session driver: file or database
SESSION_DRIVER=database

# Session lifetime (seconds)
SESSION_LIFETIME=7200

# File storage path (if using file driver)
SESSION_PATH=/var/www/storage/sessions

# Cookie settings
SESSION_PATH_COOKIE=/
SESSION_DOMAIN=
SESSION_SECURE_COOKIE=true
SESSION_HTTP_ONLY=true
SESSION_SAME_SITE=Strict

How Encryption Works

Session Encryption

Sessions are encrypted at the storage layer:

// SessionSecurity class (used by both handlers)
class SessionSecurity
{
    public function encrypt(string $data): string
    {
        $encryptor = new EncryptionService();
        return $encryptor->encrypt($data);
    }

    public function decrypt(string $encrypted): string
    {
        $encryptor = new EncryptionService();
        return $encryptor->decrypt($encrypted);
    }
}

// FileSessionHandler writes encrypted data
public function write(string $id, string $data): bool
{
    $encrypted = $this->security->encrypt($data);
    file_put_contents($this->getFilePath($id), $encrypted);
    return true;
}

// DatabaseSessionHandler writes encrypted data
public function write(string $id, string $data): bool
{
    $encrypted = $this->security->encrypt($data);
    $session->payload = $encrypted;
    $session->save();
    return true;
}

Cookie Encryption

Cookies are transparently encrypted/decrypted:

// CookieManager automatically encrypts
public function set(string $name, mixed $value, CookieOptions $options): void
{
    $encrypted = new EncryptionService()->encrypt($value);
    setcookie($name, $encrypted, $options->toArray());
}

// CookieManager automatically decrypts
public function get(string $name, mixed $default = null): mixed
{
    $value = $_COOKIE[$name] ?? null;

    if ($value === null) {
        return $default;
    }

    return new EncryptionService()->decrypt($value);
}

Comparison with Other Frameworks

Feature Larafony Laravel Symfony
Session Encryption Automatic Optional (middleware) Manual (bundles)
Cookie Encryption Automatic Middleware required Manual (bundles)
Storage Handlers File, Database File, DB, Redis, Array File, PDO, Redis, Memcached
Configuration Simple config file .env + config files YAML/PHP config
Cipher XChaCha20-Poly1305 AES-256-CBC + HMAC Varies
Clock Integration PSR-20 Carbon (not PSR) DateTime/Clock

Use Cases

User Authentication

// Login
public function login(User $user): void
{
    $session = SessionManager::create();

    // Regenerate ID to prevent session fixation
    $session->regenerateId(deleteOldSession: true);

    // Store user data
    $session->set('user_id', $user->id);
    $session->set('user_role', $user->role);
    $session->set('authenticated_at', time());
}

// Check authentication
public function isAuthenticated(): bool
{
    $session = SessionManager::create();
    return $session->has('user_id');
}

// Logout
public function logout(): void
{
    $session = SessionManager::create();
    $session->destroy();
}

Shopping Cart

class CartManager
{
    private SessionManager $session;

    public function __construct()
    {
        $this->session = SessionManager::create();
    }

    public function addItem(int $productId, int $quantity): void
    {
        $cart = $this->session->get('cart', []);
        $cart[$productId] = ($cart[$productId] ?? 0) + $quantity;
        $this->session->set('cart', $cart);
    }

    public function getItems(): array
    {
        return $this->session->get('cart', []);
    }

    public function clear(): void
    {
        $this->session->remove('cart');
    }
}

Remember Me Cookie

use Larafony\Framework\Storage\CookieManager;
use Larafony\Framework\Storage\CookieOptions;

// Set remember me token (30 days)
public function setRememberToken(User $user): void
{
    $token = bin2hex(random_bytes(32));

    // Store in database
    $user->remember_token = hash('sha256', $token);
    $user->save();

    // Store in encrypted cookie
    $cookies = new CookieManager();
    $cookies->set('remember_token', $token, new CookieOptions(
        expires: time() + (30 * 86400), // 30 days
        secure: true,
        httponly: true,
        samesite: 'Strict'
    ));
}

// Check remember me token
public function checkRememberToken(): ?User
{
    $cookies = new CookieManager();
    $token = $cookies->get('remember_token');

    if (!$token) {
        return null;
    }

    $hashedToken = hash('sha256', $token);
    return User::query()
        ->where('remember_token', '=', $hashedToken)
        ->first();
}

Best Practices

Do's:
Don'ts:

Next Steps

Learn More

This implementation is explained in detail with step-by-step tutorials, tests, and best practices at masterphp.eu

Demo App: See encrypted sessions and cookies in production with file and database storage handlers. The demo application showcases automatic encryption, PSR-20 clock integration, and secure cookie defaults with HttpOnly and SameSite protection.

View on Packagist View on GitHub