detectSessionHijack( sessionIp: '192.168.1.100', requestIp: '203.0.113.42', // Different IP sessionUserAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', requestUserAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' ); if (!$hijackAttempt) { throw new \RuntimeException('Session hijacking from different IP not detected'); } echo "✅ Session hijacking from different IP detected\n"; // Simulate hijack attempt from different User-Agent $hijackAttempt = $this->detectSessionHijack( sessionIp: '192.168.1.100', requestIp: '192.168.1.100', sessionUserAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', requestUserAgent: 'curl/7.68.0' // Different User-Agent ); if (!$hijackAttempt) { throw new \RuntimeException('Session hijacking from different User-Agent not detected'); } echo "✅ Session hijacking from different User-Agent detected\n"; } /** * Test session fixation prevention */ public function testPreventsSessionFixation(): void { // Attacker provides session ID session_id('attacker_controlled_session_id'); session_start(); $oldSessionId = session_id(); // User logs in - session should be regenerated $this->regenerateSessionOnLogin(); $newSessionId = session_id(); if ($oldSessionId === $newSessionId) { throw new \RuntimeException('Session ID not regenerated after login - vulnerable to session fixation'); } echo "✅ Session ID regenerated after login (fixation prevention)\n"; } /** * Test session timeout */ public function testEnforcesSessionTimeout(): void { session_start(); $_SESSION['last_activity'] = time() - 3600; // 1 hour ago $isExpired = $this->checkSessionTimeout( lastActivity: $_SESSION['last_activity'], timeoutSeconds: 1800 // 30 minute timeout ); if (!$isExpired) { throw new \RuntimeException('Expired session not detected'); } echo "✅ Session timeout enforced (30 minute inactivity)\n"; } /** * Test session data integrity */ public function testValidatesSessionDataIntegrity(): void { session_start(); $_SESSION['user_id'] = 123; $_SESSION['role'] = 'user'; // Simulate tampering $_SESSION['role'] = 'admin'; // Privilege escalation attempt $isTampered = $this->detectSessionTampering( originalData: ['user_id' => 123, 'role' => 'user'], currentData: ['user_id' => 123, 'role' => 'admin'] ); if (!$isTampered) { throw new \RuntimeException('Session tampering not detected'); } echo "✅ Session data tampering detected\n"; } /** * Test session cookie security attributes */ public function testSessionCookieSecurityAttributes(): void { $cookieParams = session_get_cookie_params(); $issues = []; // Check HttpOnly flag if (!$cookieParams['httponly']) { $issues[] = 'HttpOnly flag not set (vulnerable to XSS)'; } // Check Secure flag if (!$cookieParams['secure']) { $issues[] = 'Secure flag not set (session can be transmitted over HTTP)'; } // Check SameSite attribute if (empty($cookieParams['samesite']) || !in_array($cookieParams['samesite'], ['Strict', 'Lax'])) { $issues[] = 'SameSite attribute not properly set (vulnerable to CSRF)'; } if (!empty($issues)) { throw new \RuntimeException( "Session cookie security issues:\n" . implode("\n", $issues) ); } echo "✅ Session cookie has proper security attributes (HttpOnly, Secure, SameSite)\n"; } /** * Test concurrent session limit */ public function testEnforcesConcurrentSessionLimit(): void { $userId = 123; $maxSessions = 3; // Simulate active sessions $activeSessions = [ 'session1' => ['created' => time() - 100], 'session2' => ['created' => time() - 200], 'session3' => ['created' => time() - 300], ]; // Try to create 4th session $canCreateSession = $this->checkConcurrentSessionLimit( userId: $userId, activeSessions: $activeSessions, maxSessions: $maxSessions ); if ($canCreateSession) { throw new \RuntimeException('Concurrent session limit not enforced'); } echo "✅ Concurrent session limit enforced (max {$maxSessions} sessions)\n"; } /** * Test session destruction on logout */ public function testProperSessionDestruction(): void { session_start(); $_SESSION['user_id'] = 123; $_SESSION['authenticated'] = true; // Logout $this->destroySession(); if (isset($_SESSION['user_id']) || isset($_SESSION['authenticated'])) { throw new \RuntimeException('Session data not properly destroyed on logout'); } if (session_status() === PHP_SESSION_ACTIVE) { throw new \RuntimeException('Session still active after logout'); } echo "✅ Session properly destroyed on logout\n"; } /** * Run all session security tests */ public function runAllTests(): array { $results = []; try { $this->testPreventsSessionHijacking(); $results['session_hijacking'] = 'PASS'; } catch (\Exception $e) { $results['session_hijacking'] = 'FAIL: ' . $e->getMessage(); } try { $this->testPreventsSessionFixation(); $results['session_fixation'] = 'PASS'; } catch (\Exception $e) { $results['session_fixation'] = 'FAIL: ' . $e->getMessage(); } try { $this->testEnforcesSessionTimeout(); $results['session_timeout'] = 'PASS'; } catch (\Exception $e) { $results['session_timeout'] = 'FAIL: ' . $e->getMessage(); } try { $this->testValidatesSessionDataIntegrity(); $results['data_integrity'] = 'PASS'; } catch (\Exception $e) { $results['data_integrity'] = 'FAIL: ' . $e->getMessage(); } try { $this->testSessionCookieSecurityAttributes(); $results['cookie_security'] = 'PASS'; } catch (\Exception $e) { $results['cookie_security'] = 'FAIL: ' . $e->getMessage(); } try { $this->testEnforcesConcurrentSessionLimit(); $results['concurrent_sessions'] = 'PASS'; } catch (\Exception $e) { $results['concurrent_sessions'] = 'FAIL: ' . $e->getMessage(); } try { $this->testProperSessionDestruction(); $results['session_destruction'] = 'PASS'; } catch (\Exception $e) { $results['session_destruction'] = 'FAIL: ' . $e->getMessage(); } return $results; } private function detectSessionHijack( string $sessionIp, string $requestIp, string $sessionUserAgent, string $requestUserAgent ): bool { // IP mismatch detection if ($sessionIp !== $requestIp) { return true; } // User-Agent mismatch detection if ($sessionUserAgent !== $requestUserAgent) { return true; } return false; } private function regenerateSessionOnLogin(): void { session_regenerate_id(true); } private function checkSessionTimeout(int $lastActivity, int $timeoutSeconds): bool { $inactiveTime = time() - $lastActivity; return $inactiveTime > $timeoutSeconds; } private function detectSessionTampering(array $originalData, array $currentData): bool { // Simplified tampering detection foreach ($originalData as $key => $value) { if (!isset($currentData[$key]) || $currentData[$key] !== $value) { return true; } } return false; } private function checkConcurrentSessionLimit( int $userId, array $activeSessions, int $maxSessions ): bool { return count($activeSessions) < $maxSessions; } private function destroySession(): void { $_SESSION = []; session_destroy(); } }