MediaWiki  REL1_22
IEUrlExtension.php
Go to the documentation of this file.
00001 <?php
00044 class IEUrlExtension {
00062     public static function areServerVarsBad( $vars, $extWhitelist = array() ) {
00063         // Check QUERY_STRING or REQUEST_URI
00064         if ( isset( $vars['SERVER_SOFTWARE'] )
00065             && isset( $vars['REQUEST_URI'] )
00066             && self::haveUndecodedRequestUri( $vars['SERVER_SOFTWARE'] ) )
00067         {
00068             $urlPart = $vars['REQUEST_URI'];
00069         } elseif ( isset( $vars['QUERY_STRING'] ) ) {
00070             $urlPart = $vars['QUERY_STRING'];
00071         } else {
00072             $urlPart = '';
00073         }
00074 
00075         if ( self::isUrlExtensionBad( $urlPart, $extWhitelist ) ) {
00076             return true;
00077         }
00078 
00079         // Some servers have PATH_INFO but not REQUEST_URI, so we check both
00080         // to be on the safe side.
00081         if ( isset( $vars['PATH_INFO'] )
00082             && self::isUrlExtensionBad( $vars['PATH_INFO'], $extWhitelist ) )
00083         {
00084             return true;
00085         }
00086 
00087         // All checks passed
00088         return false;
00089     }
00090 
00100     public static function isUrlExtensionBad( $urlPart, $extWhitelist = array() ) {
00101         if ( strval( $urlPart ) === '' ) {
00102             return false;
00103         }
00104 
00105         $extension = self::findIE6Extension( $urlPart );
00106         if ( strval( $extension ) === '' ) {
00107             // No extension or empty extension
00108             return false;
00109         }
00110 
00111         if ( in_array( $extension, array( 'php', 'php5' ) ) ) {
00112             // Script extension, OK
00113             return false;
00114         }
00115         if ( in_array( $extension, $extWhitelist ) ) {
00116             // Whitelisted extension
00117             return false;
00118         }
00119 
00120         if ( !preg_match( '/^[a-zA-Z0-9_-]+$/', $extension ) ) {
00121             // Non-alphanumeric extension, unlikely to be registered.
00122             //
00123             // The regex above is known to match all registered file extensions
00124             // in a default Windows XP installation. It's important to allow
00125             // extensions with ampersands and percent signs, since that reduces
00126             // the number of false positives substantially.
00127             return false;
00128         }
00129 
00130         // Possibly bad extension
00131         return true;
00132     }
00133 
00141     public static function fixUrlForIE6( $url, $extWhitelist = array() ) {
00142         $questionPos = strpos( $url, '?' );
00143         if ( $questionPos === false ) {
00144             $beforeQuery = $url . '?';
00145             $query = '';
00146         } elseif ( $questionPos === strlen( $url ) - 1 ) {
00147             $beforeQuery = $url;
00148             $query = '';
00149         } else {
00150             $beforeQuery = substr( $url, 0, $questionPos + 1 );
00151             $query = substr( $url, $questionPos + 1 );
00152         }
00153 
00154         // Multiple question marks cause problems. Encode the second and
00155         // subsequent question mark.
00156         $query = str_replace( '?', '%3E', $query );
00157         // Append an invalid path character so that IE6 won't see the end of the
00158         // query string as an extension
00159         $query .= '&*';
00160         // Put the URL back together
00161         $url = $beforeQuery . $query;
00162         if ( self::isUrlExtensionBad( $url, $extWhitelist ) ) {
00163             // Avoid a redirect loop
00164             return false;
00165         }
00166         return $url;
00167     }
00168 
00193     public static function findIE6Extension( $url ) {
00194         $pos = 0;
00195         $hashPos = strpos( $url, '#' );
00196         if ( $hashPos !== false ) {
00197             $urlLength = $hashPos;
00198         } else {
00199             $urlLength = strlen( $url );
00200         }
00201         $remainingLength = $urlLength;
00202         while ( $remainingLength > 0 ) {
00203             // Skip ahead to the next dot
00204             $pos += strcspn( $url, '.', $pos, $remainingLength );
00205             if ( $pos >= $urlLength ) {
00206                 // End of string, we're done
00207                 return false;
00208             }
00209 
00210             // We found a dot. Skip past it
00211             $pos++;
00212             $remainingLength = $urlLength - $pos;
00213 
00214             // Check for illegal characters in our prospective extension,
00215             // or for another dot
00216             $nextPos = $pos + strcspn( $url, "<>\\\"/:|?*.", $pos, $remainingLength );
00217             if ( $nextPos >= $urlLength ) {
00218                 // No illegal character or next dot
00219                 // We have our extension
00220                 return substr( $url, $pos, $urlLength - $pos );
00221             }
00222             if ( $url[$nextPos] === '?' ) {
00223                 // We've found a legal extension followed by a question mark
00224                 // If the extension is NOT exe, dll or cgi, return it
00225                 $extension = substr( $url, $pos, $nextPos - $pos );
00226                 if ( strcasecmp( $extension, 'exe' ) && strcasecmp( $extension, 'dll' ) &&
00227                     strcasecmp( $extension, 'cgi' ) )
00228                 {
00229                     return $extension;
00230                 }
00231                 // Else continue looking
00232             }
00233             // We found an illegal character or another dot
00234             // Skip to that character and continue the loop
00235             $pos = $nextPos;
00236             $remainingLength = $urlLength - $pos;
00237         }
00238         return false;
00239     }
00240 
00259     public static function haveUndecodedRequestUri( $serverSoftware ) {
00260         static $whitelist = array(
00261             'Apache',
00262             'Zeus',
00263             'LiteSpeed' );
00264         if ( preg_match( '/^(.*?)($|\/| )/', $serverSoftware, $m ) ) {
00265             return in_array( $m[1], $whitelist );
00266         } else {
00267             return false;
00268         }
00269     }
00270 
00271 }