Completed
Push — development ( eda7c5...09d583 )
by Nils
07:28
created

Bootup   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 185
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 185
rs 10
c 0
b 0
f 0
wmc 20
lcom 0
cbo 1

6 Methods

Rating   Name   Duplication   Size   Complexity  
B filterRequestInputs() 0 38 5
B filterRequestUri() 0 57 8
A filterString() 0 4 1
A get_random_bytes() 0 14 3
A initAll() 0 6 1
A is_php() 0 12 2
1
<?php
2
3
namespace protect\AntiXSS;
4
5
/**
6
 * Class Bootup
7
 *
8
 * this is a bootstrap for the polyfills (iconv / intl / mbstring / normalizer / xml)
9
 *
10
 * @package voku\helper
11
 */
12
class Bootup
13
{
14
  /**
15
   * filter request inputs
16
   *
17
   * Ensures inputs are well formed UTF-8
18
   * When not, assumes Windows-1252 and converts to UTF-8
19
   * Tests only values, not keys
20
   *
21
   * @param int    $normalization_form
22
   * @param string $leading_combining
23
   */
24
  public static function filterRequestInputs($normalization_form = 4 /* n::NFC */, $leading_combining = '◌')
25
  {
26
    $a = array(
27
        &$_FILES,
28
        &$_ENV,
29
        &$_GET,
30
        &$_POST,
31
        &$_COOKIE,
32
        &$_SERVER,
33
        &$_REQUEST,
34
    );
35
36
    /** @noinspection ReferenceMismatchInspection */
37
    /** @noinspection ForeachSourceInspection */
38
    foreach ($a[0] as &$r) {
39
      $a[] = array(
40
          &$r['name'],
41
          &$r['type'],
42
      );
43
    }
44
    unset($r, $a[0]);
45
46
    $len = count($a) + 1;
47
    for ($i = 1; $i < $len; ++$i) {
48
      /** @noinspection ReferenceMismatchInspection */
49
      /** @noinspection ForeachSourceInspection */
50
      foreach ($a[$i] as &$r) {
51
        /** @noinspection ReferenceMismatchInspection */
52
        $s = $r; // $r is a reference, $s a copy
53
        if (is_array($s)) {
54
          $a[$len++] = &$r;
55
        } else {
56
          $r = self::filterString($s, $normalization_form, $leading_combining);
57
        }
58
      }
59
      unset($r, $a[$i]);
60
    }
61
  }
62
63
  /**
64
   * Filter current REQUEST_URI .
65
   *
66
   * @param string|null $uri <p>If null is set, then the server REQUEST_URI will be used.</p>
67
   * @param bool        $exit
68
   *
69
   * @return mixed
70
   */
71
  public static function filterRequestUri($uri = null, $exit = true)
72
  {
73
    if (!isset($uri)) {
74
75
      if (!isset($_SERVER['REQUEST_URI'])) {
76
        return false;
77
      }
78
79
      $uri = $_SERVER['REQUEST_URI'];
80
    }
81
82
    $uriOrig = $uri;
83
84
    //
85
    // Ensures the URL is well formed UTF-8
86
    //
87
88
    if (preg_match('//u', urldecode($uri))) {
89
      return $uri;
90
    }
91
92
    //
93
    // When not, assumes Windows-1252 and redirects to the corresponding UTF-8 encoded URL
94
    //
95
96
    $uri = preg_replace_callback(
97
        '/[\x80-\xFF]+/',
98
        function ($m) {
99
          return urlencode($m[0]);
100
        },
101
        $uri
102
    );
103
104
    $uri = preg_replace_callback(
105
        '/(?:%[89A-F][0-9A-F])+/i',
106
        function ($m) {
107
          return urlencode(UTF8::encode('UTF-8', urldecode($m[0])));
108
        },
109
        $uri
110
    );
111
112
    if (
113
        $uri !== $uriOrig
114
        &&
115
        $exit === true
116
        &&
117
        headers_sent() === false
118
    ) {
119
      // Use ob_start() to buffer content and avoid problem of headers already sent...
120
      $severProtocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1');
121
      header($severProtocol . ' 301 Moved Permanently');
122
      header('Location: ' . $uri);
0 ignored issues
show
Security Response Splitting introduced by
'Location: ' . $uri can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Fetching key REQUEST_URI from $_SERVER, and $uri is assigned
    in includes/libraries/protect/AntiXSS/bootup.php on line 79
  2. $uri is passed through preg_replace_callback()
    in includes/libraries/protect/AntiXSS/bootup.php on line 101
  3. $uri is assigned
    in includes/libraries/protect/AntiXSS/bootup.php on line 96
  4. $uri is passed through preg_replace_callback()
    in includes/libraries/protect/AntiXSS/bootup.php on line 109
  5. $uri is assigned
    in includes/libraries/protect/AntiXSS/bootup.php on line 104

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
123
      exit();
124
    }
125
126
    return $uri;
127
  }
128
129
  /**
130
   * Normalizes to UTF-8 NFC, converting from WINDOWS-1252 when needed.
131
   *
132
   * @param string $s
133
   * @param int    $normalization_form
134
   * @param string $leading_combining
135
   *
136
   * @return string
137
   */
138
  public static function filterString($s, $normalization_form = 4 /* n::NFC */, $leading_combining = '◌')
139
  {
140
    return UTF8::filter($s, $normalization_form, $leading_combining);
141
  }
142
143
  /**
144
   * Get random bytes via "random_bytes()" (+ polyfill).
145
   *
146
   * @ref https://github.com/paragonie/random_compat/
147
   *
148
   * @param  int $length Output length
149
   *
150
   * @return  string|false false on error
151
   */
152
  public static function get_random_bytes($length)
153
  {
154
    if (!$length) {
155
      return false;
156
    }
157
158
    $length = (int)$length;
159
160
    if ($length <= 0) {
161
      return false;
162
    }
163
164
    return random_bytes($length);
165
  }
166
167
  /**
168
   * bootstrap
169
   */
170
  public static function initAll()
171
  {
172
    ini_set('default_charset', 'UTF-8');
173
174
    // everything is init via composer, so we are done here ...
175
  }
176
177
  /**
178
   * Determines if the current version of PHP is equal to or greater than the supplied value.
179
   *
180
   * @param string $version
181
   *
182
   * @return bool <p>Return <strong>true</strong> if the current version is $version or higher</p>
183
   */
184
  public static function is_php($version)
185
  {
186
    static $_IS_PHP;
187
188
    $version = (string)$version;
189
190
    if (!isset($_IS_PHP[$version])) {
191
      $_IS_PHP[$version] = version_compare(PHP_VERSION, $version, '>=');
192
    }
193
194
    return $_IS_PHP[$version];
195
  }
196
}