diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 37ed0e97a68d9fd88e848c292fbbf6f63503f3cb..fec1be9ad3a6163744a6cfde69965417b3121608 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -20,6 +20,7 @@ use Drupal\Core\Http\TrustedHostsRequestFactory; use Drupal\Core\Installer\InstallerRedirectTrait; use Drupal\Core\Language\Language; +use Drupal\Core\Security\RequestSanitizer; use Drupal\Core\Site\Settings; use Drupal\Core\Test\TestDatabase; use Symfony\Cmf\Component\Routing\RouteObjectInterface; @@ -542,6 +543,12 @@ public function loadLegacyIncludes() { * {@inheritdoc} */ public function preHandle(Request $request) { + // Sanitize the request. + $request = RequestSanitizer::sanitize( + $request, + (array) Settings::get(RequestSanitizer::SANITIZE_WHITELIST, []), + (bool) Settings::get(RequestSanitizer::SANITIZE_LOG, FALSE) + ); $this->loadLegacyIncludes(); diff --git a/core/lib/Drupal/Core/Security/RequestSanitizer.php b/core/lib/Drupal/Core/Security/RequestSanitizer.php new file mode 100644 index 0000000000000000000000000000000000000000..8ba17b95cf51c7b2007af4210ae044324554c551 --- /dev/null +++ b/core/lib/Drupal/Core/Security/RequestSanitizer.php @@ -0,0 +1,99 @@ +attributes->get(self::SANITIZED, FALSE)) { + // Process query string parameters. + $get_sanitized_keys = []; + $request->query->replace(static::stripDangerousValues($request->query->all(), $whitelist, $get_sanitized_keys)); + if ($log_sanitized_keys && !empty($get_sanitized_keys)) { + trigger_error(sprintf('Potentially unsafe keys removed from query string parameters (GET): %s', implode(', ', $get_sanitized_keys))); + } + + // Request body parameters. + $post_sanitized_keys = []; + $request->request->replace(static::stripDangerousValues($request->request->all(), $whitelist, $post_sanitized_keys)); + if ($log_sanitized_keys && !empty($post_sanitized_keys)) { + trigger_error(sprintf('Potentially unsafe keys removed from request body parameters (POST): %s', implode(', ', $post_sanitized_keys))); + } + + // Cookie parameters. + $cookie_sanitized_keys = []; + $request->cookies->replace(static::stripDangerousValues($request->cookies->all(), $whitelist, $cookie_sanitized_keys)); + if ($log_sanitized_keys && !empty($cookie_sanitized_keys)) { + trigger_error(sprintf('Potentially unsafe keys removed from cookie parameters: %s', implode(', ', $cookie_sanitized_keys))); + } + + if (!empty($get_sanitized_keys) || !empty($post_sanitized_keys) || !empty($cookie_sanitized_keys)) { + $request->overrideGlobals(); + } + $request->attributes->set(self::SANITIZED, TRUE); + } + return $request; + } + + /** + * Strips dangerous keys from $input. + * + * @param mixed $input + * The input to sanitize. + * @param string[] $whitelist + * An array of keys to whitelist as safe. + * @param string[] $sanitized_keys + * An array of keys that have been removed. + * + * @return mixed + * The sanitized input. + */ + protected static function stripDangerousValues($input, array $whitelist, array &$sanitized_keys) { + if (is_array($input)) { + foreach ($input as $key => $value) { + if ($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE)) { + unset($input[$key]); + $sanitized_keys[] = $key; + } + else { + $input[$key] = static::stripDangerousValues($input[$key], $whitelist, $sanitized_keys); + } + } + } + return $input; + } + +}