To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Revision:

root / core / lib / Drupal / Core / Security / RequestSanitizer.php @ 15:e200cb7efeb3

History | View | Annotate | Download (5.19 KB)

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
}