44815f68cd49313dbe36d632ddcf18643768fdcd
[yaffs-website] / web / core / lib / Drupal / Core / Security / RequestSanitizer.php
1 <?php
2
3 namespace Drupal\Core\Security;
4
5 use Drupal\Component\Utility\UrlHelper;
6 use Symfony\Component\HttpFoundation\ParameterBag;
7 use Symfony\Component\HttpFoundation\Request;
8
9 /**
10  * Sanitizes user input.
11  */
12 class RequestSanitizer {
13
14   /**
15    * Request attribute to mark the request as sanitized.
16    */
17   const SANITIZED = '_drupal_request_sanitized';
18
19   /**
20    * The name of the setting that configures the whitelist.
21    */
22   const SANITIZE_WHITELIST = 'sanitize_input_whitelist';
23
24   /**
25    * The name of the setting that determines if sanitized keys are logged.
26    */
27   const SANITIZE_LOG = 'sanitize_input_logging';
28
29   /**
30    * Strips dangerous keys from user input.
31    *
32    * @param \Symfony\Component\HttpFoundation\Request $request
33    *   The incoming request to sanitize.
34    * @param string[] $whitelist
35    *   An array of keys to whitelist as safe. See default.settings.php.
36    * @param bool $log_sanitized_keys
37    *   (optional) Set to TRUE to log an keys that are sanitized.
38    *
39    * @return \Symfony\Component\HttpFoundation\Request
40    *   The sanitized request.
41    */
42   public static function sanitize(Request $request, $whitelist, $log_sanitized_keys = FALSE) {
43     if (!$request->attributes->get(self::SANITIZED, FALSE)) {
44       $update_globals = FALSE;
45       $bags = [
46         'query' => 'Potentially unsafe keys removed from query string parameters (GET): %s',
47         'request' => 'Potentially unsafe keys removed from request body parameters (POST): %s',
48         'cookies' => 'Potentially unsafe keys removed from cookie parameters: %s',
49       ];
50       foreach ($bags as $bag => $message) {
51         if (static::processParameterBag($request->$bag, $whitelist, $log_sanitized_keys, $bag, $message)) {
52           $update_globals = TRUE;
53         }
54       }
55       if ($update_globals) {
56         $request->overrideGlobals();
57       }
58       $request->attributes->set(self::SANITIZED, TRUE);
59     }
60     return $request;
61   }
62
63   /**
64    * Processes a request parameter bag.
65    *
66    * @param \Symfony\Component\HttpFoundation\ParameterBag $bag
67    *   The parameter bag to process.
68    * @param string[] $whitelist
69    *   An array of keys to whitelist as safe.
70    * @param bool $log_sanitized_keys
71    *   Set to TRUE to log keys that are sanitized.
72    * @param string $bag_name
73    *   The request parameter bag name. Either 'query', 'request' or 'cookies'.
74    * @param string $message
75    *   The message to log if the parameter bag contains keys that are removed.
76    *   If the message contains %s that is replaced by a list of removed keys.
77    *
78    * @return bool
79    *   TRUE if the parameter bag has been sanitized, FALSE if not.
80    */
81   protected static function processParameterBag(ParameterBag $bag, $whitelist, $log_sanitized_keys, $bag_name, $message) {
82     $sanitized = FALSE;
83     $sanitized_keys = [];
84     $bag->replace(static::stripDangerousValues($bag->all(), $whitelist, $sanitized_keys));
85     if (!empty($sanitized_keys)) {
86       $sanitized = TRUE;
87       if ($log_sanitized_keys) {
88         trigger_error(sprintf($message, implode(', ', $sanitized_keys)));
89       }
90     }
91
92     if ($bag->has('destination')) {
93       $destination_dangerous_keys = static::checkDestination($bag->get('destination'), $whitelist);
94       if (!empty($destination_dangerous_keys)) {
95         // The destination is removed rather than sanitized because the URL
96         // generator service is not available and this method is called very
97         // early in the bootstrap.
98         $bag->remove('destination');
99         $sanitized = TRUE;
100         if ($log_sanitized_keys) {
101           trigger_error(sprintf('Potentially unsafe destination removed from %s parameter bag because it contained the following keys: %s', $bag_name, implode(', ', $destination_dangerous_keys)));
102         }
103       }
104     }
105     return $sanitized;
106   }
107
108   /**
109    * Checks a destination string to see if it is dangerous.
110    *
111    * @param string $destination
112    *   The destination string to check.
113    * @param array $whitelist
114    *   An array of keys to whitelist as safe.
115    *
116    * @return array
117    *   The dangerous keys found in the destination parameter.
118    */
119   protected static function checkDestination($destination, array $whitelist) {
120     $dangerous_keys = [];
121     $parts = UrlHelper::parse($destination);
122     // If there is a query string, check its query parameters.
123     if (!empty($parts['query'])) {
124       static::stripDangerousValues($parts['query'], $whitelist, $dangerous_keys);
125     }
126     return $dangerous_keys;
127   }
128
129   /**
130    * Strips dangerous keys from $input.
131    *
132    * @param mixed $input
133    *   The input to sanitize.
134    * @param string[] $whitelist
135    *   An array of keys to whitelist as safe.
136    * @param string[] $sanitized_keys
137    *   An array of keys that have been removed.
138    *
139    * @return mixed
140    *   The sanitized input.
141    */
142   protected static function stripDangerousValues($input, array $whitelist, array &$sanitized_keys) {
143     if (is_array($input)) {
144       foreach ($input as $key => $value) {
145         if ($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE)) {
146           unset($input[$key]);
147           $sanitized_keys[] = $key;
148         }
149         else {
150           $input[$key] = static::stripDangerousValues($input[$key], $whitelist, $sanitized_keys);
151         }
152       }
153     }
154     return $input;
155   }
156
157 }