- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
234 lines
8.0 KiB
PHP
234 lines
8.0 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace Tests\Framework\Security;
|
||
|
||
use App\Framework\Auth\Auth;
|
||
use App\Framework\Http\MiddlewareContext;
|
||
use App\Framework\Http\Middlewares\AuthMiddleware;
|
||
use App\Framework\Http\Next;
|
||
use App\Framework\Http\Request;
|
||
use App\Framework\Http\RequestStateManager;
|
||
use App\Framework\Http\Session\SessionInterface;
|
||
use PHPUnit\Framework\TestCase;
|
||
|
||
/**
|
||
* Critical Security Tests for Authentication
|
||
* Tests gegen OWASP A07:2021 – Identification and Authentication Failures
|
||
*/
|
||
final class AuthenticationSecurityTest extends TestCase
|
||
{
|
||
private SessionInterface $session;
|
||
|
||
private Auth $auth;
|
||
|
||
private AuthMiddleware $middleware;
|
||
|
||
private RequestStateManager $stateManager;
|
||
|
||
protected function setUp(): void
|
||
{
|
||
$this->session = $this->createMock(SessionInterface::class);
|
||
$this->auth = $this->createMock(Auth::class);
|
||
$this->middleware = new AuthMiddleware($this->auth);
|
||
$this->stateManager = $this->createMock(RequestStateManager::class);
|
||
}
|
||
|
||
/**
|
||
* Test: Nicht-authentifizierte Benutzer werden blockiert
|
||
* OWASP A07:2021 – Identification and Authentication Failures
|
||
*/
|
||
public function test_blocks_unauthenticated_access(): void
|
||
{
|
||
// Arrange: Benutzer nicht authentifiziert
|
||
$this->auth->method('isAuthenticated')->willReturn(false);
|
||
|
||
$request = $this->createMock(Request::class);
|
||
$context = new MiddlewareContext($request);
|
||
$next = $this->createMock(Next::class);
|
||
$next->expects($this->never())->method('__invoke');
|
||
|
||
// Act & Assert: Exception erwartet
|
||
$this->expectException(\App\Framework\Http\Exception\HttpException::class);
|
||
|
||
$this->middleware->__invoke($context, $next, $this->stateManager);
|
||
}
|
||
|
||
/**
|
||
* Test: Authentifizierte Benutzer werden durchgelassen
|
||
*/
|
||
public function test_allows_authenticated_access(): void
|
||
{
|
||
// Arrange: Benutzer authentifiziert
|
||
$this->auth->method('isAuthenticated')->willReturn(true);
|
||
|
||
$request = $this->createMock(Request::class);
|
||
$context = new MiddlewareContext($request);
|
||
$next = $this->createMock(Next::class);
|
||
$next->expects($this->once())->method('__invoke')->willReturn($context);
|
||
|
||
// Act
|
||
$result = $this->middleware->__invoke($context, $next, $this->stateManager);
|
||
|
||
// Assert
|
||
$this->assertSame($context, $result);
|
||
}
|
||
|
||
/**
|
||
* Test: Session Hijacking Schutz
|
||
* Validiert Session-Fingerprinting
|
||
*/
|
||
public function test_session_fingerprint_validation(): void
|
||
{
|
||
// Arrange: Authentifiziert aber Session-Fingerprint geändert
|
||
$this->auth->method('isAuthenticated')->willReturn(true);
|
||
$this->auth->method('validateSessionFingerprint')->willReturn(false);
|
||
|
||
$request = $this->createMock(Request::class);
|
||
$context = new MiddlewareContext($request);
|
||
$next = $this->createMock(Next::class);
|
||
|
||
// Act & Assert: Session Hijacking erkannt
|
||
$this->expectException(\RuntimeException::class);
|
||
$this->expectExceptionMessage('Session fingerprint validation failed');
|
||
|
||
$this->middleware->__invoke($context, $next, $this->stateManager);
|
||
}
|
||
|
||
/**
|
||
* Test: Concurrent Login Detection
|
||
* Erkennt verdächtige gleichzeitige Logins
|
||
*/
|
||
public function test_concurrent_login_detection(): void
|
||
{
|
||
// Arrange: Benutzer von verschiedenen IPs eingeloggt
|
||
$this->auth->method('isAuthenticated')->willReturn(true);
|
||
$this->auth->method('validateSessionFingerprint')->willReturn(true);
|
||
$this->auth->method('detectConcurrentSessions')->willReturn(true);
|
||
|
||
$request = $this->createMock(Request::class);
|
||
$context = new MiddlewareContext($request);
|
||
$next = $this->createMock(Next::class);
|
||
|
||
// Act & Assert: Concurrent Session erkannt
|
||
$this->expectException(\RuntimeException::class);
|
||
$this->expectExceptionMessage('Concurrent session detected');
|
||
|
||
$this->middleware->__invoke($context, $next, $this->stateManager);
|
||
}
|
||
|
||
/**
|
||
* Test: Session Timeout Validierung
|
||
*/
|
||
public function test_session_timeout_validation(): void
|
||
{
|
||
// Arrange: Session abgelaufen
|
||
$this->auth->method('isAuthenticated')->willReturn(true);
|
||
$this->auth->method('isSessionExpired')->willReturn(true);
|
||
|
||
$request = $this->createMock(Request::class);
|
||
$context = new MiddlewareContext($request);
|
||
$next = $this->createMock(Next::class);
|
||
|
||
// Act & Assert: Session Timeout
|
||
$this->expectException(\RuntimeException::class);
|
||
$this->expectExceptionMessage('Session expired');
|
||
|
||
$this->middleware->__invoke($context, $next, $this->stateManager);
|
||
}
|
||
|
||
/**
|
||
* Test: Rate Limiting für Authentication Failures
|
||
* Verhindert Brute Force Attacks
|
||
*/
|
||
public function test_authentication_rate_limiting(): void
|
||
{
|
||
// Arrange: Zu viele fehlgeschlagene Login-Versuche
|
||
$this->auth->method('isAuthenticated')->willReturn(false);
|
||
$this->auth->method('isRateLimited')->willReturn(true);
|
||
|
||
$request = $this->createMock(Request::class);
|
||
$context = new MiddlewareContext($request);
|
||
$next = $this->createMock(Next::class);
|
||
|
||
// Act & Assert: Rate Limit erreicht
|
||
$this->expectException(\RuntimeException::class);
|
||
$this->expectExceptionMessage('Authentication rate limit exceeded');
|
||
|
||
$this->middleware->__invoke($context, $next, $this->stateManager);
|
||
}
|
||
|
||
/**
|
||
* Test: IP Whitelist Validation
|
||
* Admin-Bereiche nur von bestimmten IPs
|
||
*/
|
||
public function test_ip_whitelist_validation(): void
|
||
{
|
||
// Arrange: IP nicht in Whitelist
|
||
$this->auth->method('isAuthenticated')->willReturn(true);
|
||
$this->auth->method('isIpWhitelisted')->willReturn(false);
|
||
|
||
$request = $this->createRequestWithIp('192.168.1.100');
|
||
$context = new MiddlewareContext($request);
|
||
$next = $this->createMock(Next::class);
|
||
|
||
// Act & Assert: IP nicht erlaubt
|
||
$this->expectException(\RuntimeException::class);
|
||
$this->expectExceptionMessage('Access from this IP address is not allowed');
|
||
|
||
$this->middleware->__invoke($context, $next, $this->stateManager);
|
||
}
|
||
|
||
/**
|
||
* Test: Account Lockout nach fehlgeschlagenen Versuchen
|
||
*/
|
||
public function test_account_lockout_after_failed_attempts(): void
|
||
{
|
||
// Arrange: Account gesperrt nach zu vielen Versuchen
|
||
$this->auth->method('isAuthenticated')->willReturn(false);
|
||
$this->auth->method('isAccountLocked')->willReturn(true);
|
||
|
||
$request = $this->createMock(Request::class);
|
||
$context = new MiddlewareContext($request);
|
||
$next = $this->createMock(Next::class);
|
||
|
||
// Act & Assert: Account gesperrt
|
||
$this->expectException(\RuntimeException::class);
|
||
$this->expectExceptionMessage('Account is locked due to too many failed attempts');
|
||
|
||
$this->middleware->__invoke($context, $next, $this->stateManager);
|
||
}
|
||
|
||
/**
|
||
* Test: Two-Factor Authentication Validation
|
||
*/
|
||
public function test_two_factor_authentication_required(): void
|
||
{
|
||
// Arrange: 2FA erforderlich aber nicht bereitgestellt
|
||
$this->auth->method('isAuthenticated')->willReturn(true);
|
||
$this->auth->method('requires2FA')->willReturn(true);
|
||
$this->auth->method('is2FAValid')->willReturn(false);
|
||
|
||
$request = $this->createMock(Request::class);
|
||
$context = new MiddlewareContext($request);
|
||
$next = $this->createMock(Next::class);
|
||
|
||
// Act & Assert: 2FA erforderlich
|
||
$this->expectException(\RuntimeException::class);
|
||
$this->expectExceptionMessage('Two-factor authentication required');
|
||
|
||
$this->middleware->__invoke($context, $next, $this->stateManager);
|
||
}
|
||
|
||
// Helper Methods
|
||
|
||
private function createRequestWithIp(string $ip): Request
|
||
{
|
||
$request = $this->createMock(Request::class);
|
||
$request->method('getClientIp')->willReturn($ip);
|
||
|
||
return $request;
|
||
}
|
||
}
|