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.
Layer 1: Secure Cookie Configuration
// 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.