MediaWiki  master
IEUrlExtension.php
Go to the documentation of this file.
1 <?php
62  public static function areServerVarsBad( $vars, $extWhitelist = [] ) {
63  // Check QUERY_STRING or REQUEST_URI
64  if ( isset( $vars['SERVER_SOFTWARE'] )
65  && isset( $vars['REQUEST_URI'] )
66  && self::haveUndecodedRequestUri( $vars['SERVER_SOFTWARE'] )
67  ) {
68  $urlPart = $vars['REQUEST_URI'];
69  } elseif ( isset( $vars['QUERY_STRING'] ) ) {
70  $urlPart = $vars['QUERY_STRING'];
71  } else {
72  $urlPart = '';
73  }
74 
75  if ( self::isUrlExtensionBad( $urlPart, $extWhitelist ) ) {
76  return true;
77  }
78 
79  // Some servers have PATH_INFO but not REQUEST_URI, so we check both
80  // to be on the safe side.
81  if ( isset( $vars['PATH_INFO'] )
82  && self::isUrlExtensionBad( $vars['PATH_INFO'], $extWhitelist )
83  ) {
84  return true;
85  }
86 
87  // All checks passed
88  return false;
89  }
90 
100  public static function isUrlExtensionBad( $urlPart, $extWhitelist = [] ) {
101  if ( strval( $urlPart ) === '' ) {
102  return false;
103  }
104 
105  $extension = self::findIE6Extension( $urlPart );
106  if ( strval( $extension ) === '' ) {
107  // No extension or empty extension
108  return false;
109  }
110 
111  if ( in_array( $extension, [ 'php', 'php5' ] ) ) {
112  // Script extension, OK
113  return false;
114  }
115  if ( in_array( $extension, $extWhitelist ) ) {
116  // Whitelisted extension
117  return false;
118  }
119 
120  if ( !preg_match( '/^[a-zA-Z0-9_-]+$/', $extension ) ) {
121  // Non-alphanumeric extension, unlikely to be registered.
122  // The regex above is known to match all registered file extensions
123  // in a default Windows XP installation. It's important to allow
124  // extensions with ampersands and percent signs, since that reduces
125  // the number of false positives substantially.
126  return false;
127  }
128 
129  // Possibly bad extension
130  return true;
131  }
132 
140  public static function fixUrlForIE6( $url, $extWhitelist = [] ) {
141  $questionPos = strpos( $url, '?' );
142  if ( $questionPos === false ) {
143  $beforeQuery = $url . '?';
144  $query = '';
145  } elseif ( $questionPos === strlen( $url ) - 1 ) {
146  $beforeQuery = $url;
147  $query = '';
148  } else {
149  $beforeQuery = substr( $url, 0, $questionPos + 1 );
150  $query = substr( $url, $questionPos + 1 );
151  }
152 
153  // Multiple question marks cause problems. Encode the second and
154  // subsequent question mark.
155  $query = str_replace( '?', '%3E', $query );
156  // Append an invalid path character so that IE6 won't see the end of the
157  // query string as an extension
158  $query .= '&*';
159  // Put the URL back together
160  $url = $beforeQuery . $query;
161  if ( self::isUrlExtensionBad( $url, $extWhitelist ) ) {
162  // Avoid a redirect loop
163  return false;
164  }
165  return $url;
166  }
167 
192  public static function findIE6Extension( $url ) {
193  $pos = 0;
194  $hashPos = strpos( $url, '#' );
195  if ( $hashPos !== false ) {
196  $urlLength = $hashPos;
197  } else {
198  $urlLength = strlen( $url );
199  }
200  $remainingLength = $urlLength;
201  while ( $remainingLength > 0 ) {
202  // Skip ahead to the next dot
203  $pos += strcspn( $url, '.', $pos, $remainingLength );
204  if ( $pos >= $urlLength ) {
205  // End of string, we're done
206  return false;
207  }
208 
209  // We found a dot. Skip past it
210  $pos++;
211  $remainingLength = $urlLength - $pos;
212 
213  // Check for illegal characters in our prospective extension,
214  // or for another dot
215  $nextPos = $pos + strcspn( $url, "<>\\\"/:|?*.", $pos, $remainingLength );
216  if ( $nextPos >= $urlLength ) {
217  // No illegal character or next dot
218  // We have our extension
219  return substr( $url, $pos, $urlLength - $pos );
220  }
221  if ( $url[$nextPos] === '?' ) {
222  // We've found a legal extension followed by a question mark
223  // If the extension is NOT exe, dll or cgi, return it
224  $extension = substr( $url, $pos, $nextPos - $pos );
225  if ( strcasecmp( $extension, 'exe' ) && strcasecmp( $extension, 'dll' ) &&
226  strcasecmp( $extension, 'cgi' )
227  ) {
228  return $extension;
229  }
230  // Else continue looking
231  }
232  // We found an illegal character or another dot
233  // Skip to that character and continue the loop
234  $pos = $nextPos;
235  $remainingLength = $urlLength - $pos;
236  }
237  return false;
238  }
239 
257  public static function haveUndecodedRequestUri( $serverSoftware ) {
258  static $whitelist = [
259  'Apache',
260  'Zeus',
261  'LiteSpeed' ];
262  if ( preg_match( '/^(.*?)($|\/| )/', $serverSoftware, $m ) ) {
263  return in_array( $m[1], $whitelist );
264  } else {
265  return false;
266  }
267  }
268 
269 }
static isUrlExtensionBad( $urlPart, $extWhitelist=[])
Given a right-hand portion of a URL, determine whether IE would detect a potentially harmful file ext...
static haveUndecodedRequestUri( $serverSoftware)
When passed the value of $_SERVER[&#39;SERVER_SOFTWARE&#39;], this function returns true if that server is kn...
Internet Explorer derives a cache filename from a URL, and then in certain circumstances, uses the extension of the resulting file to determine the content type of the data, ignoring the Content-Type header.
static areServerVarsBad( $vars, $extWhitelist=[])
Check a subset of $_SERVER (or the whole of $_SERVER if you like) to see if it indicates that the req...
static fixUrlForIE6( $url, $extWhitelist=[])
Returns a variant of $url which will pass isUrlExtensionBad() but has the same GET parameters...
static findIE6Extension( $url)
Determine what extension IE6 will infer from a certain query string.