diff --git a/src/Cms/Controllers/Auth/LoginController.php b/src/Cms/Controllers/Auth/LoginController.php index e755a10..c782ac6 100644 --- a/src/Cms/Controllers/Auth/LoginController.php +++ b/src/Cms/Controllers/Auth/LoginController.php @@ -22,6 +22,11 @@ class LoginController extends Content private SessionManager $_SessionManager; private CsrfTokenManager $_CsrfManager; + /** + * Default redirect URL after login + */ + private const DEFAULT_REDIRECT_URL = '/admin/dashboard'; + public function __construct( ?Application $app = null ) { parent::__construct( $app ); @@ -44,6 +49,36 @@ public function __construct( ?Application $app = null ) $this->_CsrfManager = new CsrfTokenManager( $this->_SessionManager ); } + /** + * Validate redirect URL to prevent open redirect vulnerabilities + * + * @param string $url The URL to validate + * @return bool True if the URL is valid, false otherwise + */ + private function isValidRedirectUrl( string $url ): bool + { + // Only allow internal relative paths + if( empty( $url ) ) + { + return false; + } + + // Reject URLs with schemes (http://, https://, javascript:, etc.) + if( preg_match( '/^[a-zA-Z][a-zA-Z0-9+.-]*:/', $url ) ) + { + return false; + } + + // Reject protocol-relative URLs (//example.com) + if( str_starts_with( $url, '//' ) ) + { + return false; + } + + // Must start with / for internal path + return str_starts_with( $url, '/' ); + } + /** * Show login form */ @@ -52,17 +87,22 @@ public function showLoginForm( array $Parameters ): string // If already logged in, redirect to dashboard if( $this->_AuthManager->check() ) { - header( 'Location: /admin/dashboard' ); + header( 'Location: ' . self::DEFAULT_REDIRECT_URL ); exit; } + $requestedRedirect = $_GET['redirect'] ?? self::DEFAULT_REDIRECT_URL; + $redirectUrl = $this->isValidRedirectUrl( $requestedRedirect ) + ? $requestedRedirect + : self::DEFAULT_REDIRECT_URL; + $ViewData = [ 'Title' => 'Login | ' . $this->getName(), 'Description' => 'Login to ' . $this->getName(), 'CsrfToken' => $this->_CsrfManager->getToken(), 'Error' => $this->_SessionManager->getFlash( 'error' ), 'Success' => $this->_SessionManager->getFlash( 'success' ), - 'RedirectUrl' => $_GET['redirect'] ?? '/admin/dashboard' + 'RedirectUrl' => $redirectUrl ]; return $this->renderHtml( @@ -112,8 +152,11 @@ public function login( array $Parameters ): string $this->_SessionManager->flash( 'success', 'Welcome back!' ); // Redirect to intended URL or dashboard - $RedirectUrl = $_POST['redirect_url'] ?? '/admin/dashboard'; - header( 'Location: ' . $RedirectUrl ); + $requestedRedirect = $_POST['redirect_url'] ?? self::DEFAULT_REDIRECT_URL; + $redirectUrl = $this->isValidRedirectUrl( $requestedRedirect ) + ? $requestedRedirect + : self::DEFAULT_REDIRECT_URL; + header( 'Location: ' . $redirectUrl ); exit; }