From 673f2d36f60a96fda0a47775e57b3e5d1345c60f Mon Sep 17 00:00:00 2001 From: VitorVieira20 Date: Thu, 5 Dec 2024 09:27:02 +0000 Subject: [PATCH] fix: add uri and params validations in guest-reservation, view-schedule and view-calendar routes to prevent XSS attacks --- Pages/Reservation/GuestReservationPage.php | 13 ++ Pages/ViewCalendarPage.php | 3 + Pages/ViewSchedulePage.php | 3 + .../URI/IParamsValidatorMethods.php | 98 +++++++++++ .../Validators/URI/IURIScriptValidator.php | 16 ++ lib/Common/Validators/URI/ParamsValidator.php | 116 +++++++++++++ .../Validators/URI/ParamsValidatorMethods.php | 153 ++++++++++++++++++ .../Validators/URI/URIScriptValidator.php | 27 ++++ lib/Common/Validators/namespace.php | 5 + lib/Server/ParamsValidatorKeys.php | 19 +++ lib/Server/RouteParamsKeys.php | 45 ++++++ lib/Server/namespace.php | 2 + 12 files changed, 500 insertions(+) create mode 100644 lib/Common/Validators/URI/IParamsValidatorMethods.php create mode 100644 lib/Common/Validators/URI/IURIScriptValidator.php create mode 100644 lib/Common/Validators/URI/ParamsValidator.php create mode 100644 lib/Common/Validators/URI/ParamsValidatorMethods.php create mode 100644 lib/Common/Validators/URI/URIScriptValidator.php create mode 100644 lib/Server/ParamsValidatorKeys.php create mode 100644 lib/Server/RouteParamsKeys.php diff --git a/Pages/Reservation/GuestReservationPage.php b/Pages/Reservation/GuestReservationPage.php index cba69c09c..7d5f9cc62 100644 --- a/Pages/Reservation/GuestReservationPage.php +++ b/Pages/Reservation/GuestReservationPage.php @@ -30,6 +30,8 @@ class GuestReservationPage extends NewReservationPage implements IGuestReservati { public function PageLoad() { + $this->RouteValidation(); + if (Configuration::Instance()->GetSectionKey(ConfigSection::PRIVACY, ConfigKeys::PRIVACY_ALLOW_GUEST_BOOKING, new BooleanConverter())) { $this->presenter = $this->GetPresenter(); $this->presenter->PageLoad(); @@ -79,4 +81,15 @@ public function GetTermsOfServiceAcknowledgement() { return $this->GetCheckbox(FormKeys::TOS_ACKNOWLEDGEMENT); } + + protected function RouteValidation() + { + URIScriptValidator::validate($_SERVER['REQUEST_URI'], '/dashboard.php'); + + if (preg_match('/(?:\?|&)(redirect)=([^&]+)/', $_SERVER['REQUEST_URI'])) { + ParamsValidator::validate(RouteParamsKeys::GUEST_RESERVATION_FROM_CALENDAR, $_SERVER['REQUEST_URI'], '/view-calendar.php', false); + } else { + ParamsValidator::validate(RouteParamsKeys::GUEST_RESERVATION_FROM_SCHEDULE, $_SERVER['REQUEST_URI'], '/view-schedule.php', false); + } + } } diff --git a/Pages/ViewCalendarPage.php b/Pages/ViewCalendarPage.php index 5e1133c24..368fc528b 100644 --- a/Pages/ViewCalendarPage.php +++ b/Pages/ViewCalendarPage.php @@ -42,6 +42,9 @@ public function __construct() public function DisplayPage() { + URIScriptValidator::validate($_SERVER['REQUEST_URI'], '/view-calendar.php'); + ParamsValidator::validate(RouteParamsKeys::VIEW_CALENDAR, $_SERVER['REQUEST_URI'], '/view-calendar.php', true); + $this->Set('pageUrl', Pages::VIEW_CALENDAR); $this->Set('CreateReservationPage', Pages::GUEST_RESERVATION); $this->Set('HideCreate', !Configuration::Instance()->GetSectionKey(ConfigSection::PRIVACY, ConfigKeys::PRIVACY_ALLOW_GUEST_BOOKING, new BooleanConverter())); diff --git a/Pages/ViewSchedulePage.php b/Pages/ViewSchedulePage.php index 7f5ddcac2..07e55f223 100644 --- a/Pages/ViewSchedulePage.php +++ b/Pages/ViewSchedulePage.php @@ -41,6 +41,9 @@ public function __construct() public function ProcessPageLoad() { + URIScriptValidator::validate($_SERVER['REQUEST_URI'], '/view-schedule.php'); + ParamsValidator::validate(RouteParamsKeys::VIEW_SCHEDULE, $_SERVER['REQUEST_URI'], '/view-schedule.php', true); + $user = new NullUserSession(); $this->_presenter->PageLoad($user); diff --git a/lib/Common/Validators/URI/IParamsValidatorMethods.php b/lib/Common/Validators/URI/IParamsValidatorMethods.php new file mode 100644 index 000000000..5fc3888f7 --- /dev/null +++ b/lib/Common/Validators/URI/IParamsValidatorMethods.php @@ -0,0 +1,98 @@ +; ''; "") + * + * @param string $requestURI - The request URI to be validated for malicious content. + * @param string $redirectURL - The URL to which the user will be redirected if the URI is invalid. + * + * @return void - No return value. Redirection occurs if the URI is invalid. + */ + public static function validate(string $requestURI, string $redirectURL): void; +} diff --git a/lib/Common/Validators/URI/ParamsValidator.php b/lib/Common/Validators/URI/ParamsValidator.php new file mode 100644 index 000000000..1db940c58 --- /dev/null +++ b/lib/Common/Validators/URI/ParamsValidator.php @@ -0,0 +1,116 @@ + $validationType) { + + // If is an array of validations + if (is_array($validationType)) { + $allFailed = true; + $allMatchValid = true; + foreach ($validationType as $validation) { + + // If the validation is an array so its a mecth validation + if (is_array($validation)) { + foreach ($validation as $index => $expectedValue) { + if (self::runValidation($key, ParamsValidatorKeys::MATCH, $expectedValue, $requestURI)) { + $allMatchValid = false; + break; + } + } + } else { + if (self::runValidation($key, $validation, null, $requestURI)) { + $allFailed = false; + break; + } + } + } + + if ($allFailed && $allMatchValid) { + $valid = false; + } + } else { + if (!self::runValidation($key, $validationType, null, $requestURI)) { + $valid = false; + } + } + } + + if (!$valid) { + header("Location: " . $redirectURL); + exit; + } + } + + + /** + * Executes a specific validation based on the validation type. + * + * @param string $value The parameter value to validate. + * @param string $validationType The type of validation to run. + * @param mixed $expectedValue The expected value for match validation (optional). + * @param string $requestURI The full URI of the request. + * + * @return bool Returns `true` if validation passes, otherwise `false`. + */ + private static function runValidation(string $value, string $validationType, $expectedValue, string $requestURI): bool + { + switch ($validationType) { + case ParamsValidatorKeys::NUMERICAL: + return ParamsValidatorMethods::numericalValidator($value, $requestURI); + + case ParamsValidatorKeys::DATE: + return ParamsValidatorMethods::dateValidator($value, $requestURI); + + case ParamsValidatorKeys::SIMPLE_DATE: + return ParamsValidatorMethods::simpleDateValidatorList($value, $requestURI); + + case ParamsValidatorKeys::SIMPLE_DATETIME: + return ParamsValidatorMethods::simpleDateTimeValidator($value, $requestURI); + + case ParamsValidatorKeys::COMPLEX_DATETIME: + return ParamsValidatorMethods::complexDateTimedateValidator($value, $requestURI); + + case ParamsValidatorKeys::EXISTS: + return ParamsValidatorMethods::existsInURLValidator($value, $requestURI); + + case ParamsValidatorKeys::REDIRECT_GUEST_RESERVATION: + return ParamsValidatorMethods::redirectGuestReservationValidator($requestURI); + + case ParamsValidatorKeys::BOOLEAN: + return ParamsValidatorMethods::booleanValidator($value, $requestURI); + + case ParamsValidatorKeys::MATCH: + return ParamsValidatorMethods::matchValidator($value, $expectedValue, $requestURI); + + default: + return false; + } + } +} diff --git a/lib/Common/Validators/URI/ParamsValidatorMethods.php b/lib/Common/Validators/URI/ParamsValidatorMethods.php new file mode 100644 index 000000000..9c972daba --- /dev/null +++ b/lib/Common/Validators/URI/ParamsValidatorMethods.php @@ -0,0 +1,153 @@ +/', urldecode($requestURI)); + } +} diff --git a/lib/Common/Validators/URI/URIScriptValidator.php b/lib/Common/Validators/URI/URIScriptValidator.php new file mode 100644 index 000000000..ca3f37395 --- /dev/null +++ b/lib/Common/Validators/URI/URIScriptValidator.php @@ -0,0 +1,27 @@ +/', $decodedURI) || + preg_match('/%3C.*%3E|<.*>/', $decodedURI) || + preg_match('/on[a-z]+=[^&]*/i', $decodedURI); + } +} diff --git a/lib/Common/Validators/namespace.php b/lib/Common/Validators/namespace.php index 53925c126..e521de194 100644 --- a/lib/Common/Validators/namespace.php +++ b/lib/Common/Validators/namespace.php @@ -19,3 +19,8 @@ require_once(ROOT_DIR . 'lib/Common/Validators/RequiredEmailDomainValidator.php'); require_once(ROOT_DIR . 'lib/Common/Validators/TermsOfServiceValidator.php'); require_once(ROOT_DIR . 'lib/Common/Validators/RestrictedGuestValidator.php'); +require_once(ROOT_DIR . 'lib/Common/Validators/URI/IURIScriptValidator.php'); +require_once(ROOT_DIR . 'lib/Common/Validators/URI/URIScriptValidator.php'); +require_once(ROOT_DIR . 'lib/Common/Validators/URI/IParamsValidatorMethods.php'); +require_once(ROOT_DIR . 'lib/Common/Validators/URI/ParamsValidatorMethods.php'); +require_once(ROOT_DIR . 'lib/Common/Validators/URI/ParamsValidator.php'); diff --git a/lib/Server/ParamsValidatorKeys.php b/lib/Server/ParamsValidatorKeys.php new file mode 100644 index 000000000..89e7cf434 --- /dev/null +++ b/lib/Server/ParamsValidatorKeys.php @@ -0,0 +1,19 @@ + ParamsValidatorKeys::NUMERICAL, + QueryStringKeys::SCHEDULE_ID => ParamsValidatorKeys::NUMERICAL, + QueryStringKeys::RESERVATION_DATE => ParamsValidatorKeys::DATE, + QueryStringKeys::START_DATE => ParamsValidatorKeys::COMPLEX_DATETIME, + QueryStringKeys::END_DATE => ParamsValidatorKeys::COMPLEX_DATETIME + ]; + + public const GUEST_RESERVATION_FROM_CALENDAR = [ + QueryStringKeys::SCHEDULE_ID => ParamsValidatorKeys::EXISTS, + QueryStringKeys::REPORT_ID => ParamsValidatorKeys::EXISTS, + QueryStringKeys::START_DATE => ParamsValidatorKeys::SIMPLE_DATETIME, + QueryStringKeys::END_DATE => ParamsValidatorKeys::SIMPLE_DATETIME, + QueryStringKeys::REDIRECT => ParamsValidatorKeys::REDIRECT_GUEST_RESERVATION + ]; + + public const VIEW_SCHEDULE = [ + FormKeys::PARTICIPANT_ID => [ParamsValidatorKeys::EXISTS, ParamsValidatorKeys::NUMERICAL], + FormKeys::USER_ID => [ParamsValidatorKeys::EXISTS, ParamsValidatorKeys::NUMERICAL], + QueryStringKeys::SCHEDULE_ID => ParamsValidatorKeys::NUMERICAL, + QueryStringKeys::START_DATE => ParamsValidatorKeys::DATE, + "clearFilter" => ParamsValidatorKeys::NUMERICAL, + QueryStringKeys::START_DATES => [ParamsValidatorKeys::EXISTS, ParamsValidatorKeys::SIMPLE_DATE], + FormKeys::RESOURCE_TYPE_ID => [ParamsValidatorKeys::EXISTS, ParamsValidatorKeys::NUMERICAL], + FormKeys::MAX_PARTICIPANTS => [ParamsValidatorKeys::EXISTS, ParamsValidatorKeys::NUMERICAL], + FormKeys::SUBMIT => ParamsValidatorKeys::BOOLEAN, + QueryStringKeys::DATA_REQUEST => [ParamsValidatorKeys::MATCH => ['reservations']] + ]; + + public const VIEW_CALENDAR = [ + QueryStringKeys::REPORT_ID => [ParamsValidatorKeys::EXISTS, ParamsValidatorKeys::NUMERICAL], + QueryStringKeys::SCHEDULE_ID => [ParamsValidatorKeys::EXISTS, ParamsValidatorKeys::NUMERICAL], + QueryStringKeys::START => ParamsValidatorKeys::SIMPLE_DATE, + //QueryStringKeys::END => ParamsValidatorKeys::SIMPLE_DATE, + QueryStringKeys::GROUP_ID => [ParamsValidatorKeys::EXISTS, ParamsValidatorKeys::NUMERICAL] + ]; +} diff --git a/lib/Server/namespace.php b/lib/Server/namespace.php index 31edd13c9..a28deba68 100644 --- a/lib/Server/namespace.php +++ b/lib/Server/namespace.php @@ -10,3 +10,5 @@ require_once(ROOT_DIR . 'lib/Server/UploadedFile.php'); require_once(ROOT_DIR . 'lib/Server/Server.php'); require_once(ROOT_DIR . 'lib/Server/Url.php'); +require_once(ROOT_DIR . 'lib/Server/ParamsValidatorKeys.php'); +require_once(ROOT_DIR . 'lib/Server/RouteParamsKeys.php');