MediaWiki REL1_34
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}
Internet Explorer derives a cache filename from a URL, and then in certain circumstances,...
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 haveUndecodedRequestUri( $serverSoftware)
When passed the value of $_SERVER['SERVER_SOFTWARE'], this function returns true if that server is kn...
static findIE6Extension( $url)
Determine what extension IE6 will infer from a certain query string.
static isUrlExtensionBad( $urlPart, $extWhitelist=[])
Given a right-hand portion of a URL, determine whether IE would detect a potentially harmful file ext...
static fixUrlForIE6( $url, $extWhitelist=[])
Returns a variant of $url which will pass isUrlExtensionBad() but has the same GET parameters,...