Understanding JekCMS Session and Auth Security

Session fixation, CSRF tokens, remember-me rotation, per-site cookie isolation, and jekcms's deliberately long 30-day session lifetime — every layer of the auth system, explained honestly.

Understanding JekCMS Session and Auth Security

Session fixation, CSRF tokens, remember-me rotation, per-site cookie isolation, and jekcms's deliberately long 30-day session lifetime — every layer of the auth system, explained honestly.

Why Session Security Is Hard

PHP sessions seem simple: session_start(), store data in $_SESSION, done. But that simplicity hides a minefield of security content issues. Session fixation, session hijacking, cross-site session leakage, CSRF attacks — each one can give an attacker full admin access to your CMS.

JekCMS implements seven layers of session security. Here is each one with the specific attack it prevents.

// Session.php constructor
ini_set('session.cookie_httponly', 1);   // Prevent JavaScript access
ini_set('session.cookie_secure', IS_PRODUCTION ? 1 : 0); // HTTPS only in production
ini_set('session.cookie_samesite', 'Lax');  // Prevent CSRF via cross-origin requests
ini_set('session.use_strict_mode', 1);   // Reject uninitialized session IDs
ini_set('session.use_only_cookies', 1);  // No session ID in URLs

Attack prevented: httponly stops XSS from stealing session cookies. secure prevents session cookies from being sent over unencrypted HTTP. samesite=Lax blocks most CSRF attacks by not sending cookies with cross-origin POST requests.

Layer 2: Session Fixation Prevention

Session fixation is when an attacker sets a known session ID before the victim logs in. After login, the attacker uses the same session ID to access the authenticated session.

// After successful login
session_regenerate_id(true); // Generate new ID, delete old session
$_SESSION['created_at'] = time();
$_SESSION['user_id'] = $user['id'];

session_regenerate_id(true) creates a new session ID and destroys the old session data. The attacker's known session ID becomes invalid.

Layer 3: CSRF Token Validation

// Generate token (once per session)
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Validate on form submission
function validate_csrf(string $token): bool {
    return hash_equals($_SESSION['csrf_token'] ?? '', $token);
}

Every form includes a hidden csrf_token field. POST requests without a valid token are rejected with a 403.

Layer 4: Remember-Me Token Rotation

The "remember me" feature uses a separate token stored in a cookie and the database. Each time the token is used to authenticate, it is rotated — the old token is invalidated and a new one is issued:

public function validateRememberToken(string $token): ?array
{
    $record = $this->db->fetch(
        "SELECT * FROM remember_tokens WHERE token = ? AND expires_at > NOW()",
        [hash('sha256', $token)]
    );

    if ($record) {
        // Rotate: delete old, create new
        $this->db->delete('remember_tokens', 'id = ?', [$record['id']]);
        $newToken = $this->createRememberToken($record['user_id']);
        return ['user_id' => $record['user_id'], 'new_token' => $newToken];
    }

    return null;
}

Token rotation means a stolen remember-me cookie can only be used once. If the legitimate user visits the site before the attacker, the attacker's token is already invalid.

Layer 5: Multi-Site Session Isolation

A single server can host many jekcms installations. Without isolation, logging into Site A could give you access to Site B if they share the same PHP session storage. jekcms prevents this with a unique session cookie name per site, generated from the site's own configuration, so each installation's sessions are stored under a distinct name and never overlap with another site's on the same server:

// Unique session name per site
session_name(SESSION_NAME); // derived per-site, set in Session.php

Layer 6: Session Lifetime

jekcms's session lifetime is set to 30 days of inactivity (SESSION_LIFETIME), not a short absolute timeout — the product favours "stay logged in until you actually log out" for the admin/customer experience over frequent re-authentication. If your deployment needs a shorter idle timeout for compliance reasons, that constant is where you'd change it:

define('SESSION_LIFETIME', 2592000); // 30 gun
ini_set('session.gc_maxlifetime', (string) SESSION_LIFETIME);
ini_set('session.cookie_lifetime', (string) SESSION_LIFETIME);

Layer 7: IP Binding (Optional)

For high-security deployments, sessions can be bound to the client's IP address. If the IP changes mid-session, the session is invalidated. This is disabled by default because mobile users frequently change IPs, but it can be enabled in config.

Order Today

One-time payment, lifetime access. Setup in 30 minutes.

View Pricing
  • Setup and live in 30 minutes
  • 14+ professional themes
  • n8n automation integration
  • Automatic SEO — Sitemap, Schema.org
  • iyzico payment support

Be the first to know

New features, release notes & CMS guides — a couple of emails a month, no spam.