Better validation of the URL used in core HTTP requests.

git-svn-id: http://core.svn.wordpress.org/trunk@24480 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Andrew Nacin 2013-06-21 06:07:47 +00:00
parent c0c884b522
commit 96ee267343
10 changed files with 92 additions and 66 deletions

View File

@ -183,6 +183,7 @@ class WP_Importer {
$headers = array(); $headers = array();
$args = array(); $args = array();
$args['reject_unsafe_urls'] = true;
if ( true === $head ) if ( true === $head )
$args['method'] = 'HEAD'; $args['method'] = 'HEAD';
if ( !empty( $username ) && !empty( $password ) ) if ( !empty( $username ) && !empty( $password ) )

View File

@ -497,7 +497,7 @@ function download_url( $url, $timeout = 300 ) {
if ( ! $tmpfname ) if ( ! $tmpfname )
return new WP_Error('http_no_file', __('Could not create Temporary file.')); return new WP_Error('http_no_file', __('Could not create Temporary file.'));
$response = wp_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname ) ); $response = wp_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname, 'reject_unsafe_urls' => true ) );
if ( is_wp_error( $response ) ) { if ( is_wp_error( $response ) ) {
unlink( $tmpfname ); unlink( $tmpfname );

View File

@ -66,7 +66,11 @@ class WP_SimplePie_File extends SimplePie_File {
$this->method = SIMPLEPIE_FILE_SOURCE_REMOTE; $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE;
if ( preg_match('/^http(s)?:\/\//i', $url) ) { if ( preg_match('/^http(s)?:\/\//i', $url) ) {
$args = array( 'timeout' => $this->timeout, 'redirection' => $this->redirects); $args = array(
'timeout' => $this->timeout,
'redirection' => $this->redirects,
'reject_unsafe_urls' => true,
);
if ( !empty($this->headers) ) if ( !empty($this->headers) )
$args['headers'] = $this->headers; $args['headers'] = $this->headers;
@ -85,12 +89,10 @@ class WP_SimplePie_File extends SimplePie_File {
$this->status_code = wp_remote_retrieve_response_code( $res ); $this->status_code = wp_remote_retrieve_response_code( $res );
} }
} else { } else {
if ( ! file_exists($url) || ( ! $this->body = file_get_contents($url) ) ) { $this->error = '';
$this->error = 'file_get_contents could not read the file';
$this->success = false; $this->success = false;
} }
} }
}
} }
/** /**

View File

@ -87,6 +87,7 @@ class WP_Http {
'redirection' => apply_filters( 'http_request_redirection_count', 5), 'redirection' => apply_filters( 'http_request_redirection_count', 5),
'httpversion' => apply_filters( 'http_request_version', '1.0'), 'httpversion' => apply_filters( 'http_request_version', '1.0'),
'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ) ), 'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ) ),
'reject_unsafe_urls' => apply_filters( 'http_request_reject_unsafe_urls', false ),
'blocking' => true, 'blocking' => true,
'headers' => array(), 'headers' => array(),
'cookies' => array(), 'cookies' => array(),
@ -118,7 +119,11 @@ class WP_Http {
if ( false !== $pre ) if ( false !== $pre )
return $pre; return $pre;
$arrURL = parse_url( $url ); if ( $r['reject_unsafe_urls'] )
$url = wp_http_validate_url( $url );
$url = wp_kses_bad_protocol( $url, array( 'http', 'https', 'ssl' ) );
$arrURL = @parse_url( $url );
if ( empty( $url ) || empty( $arrURL['scheme'] ) ) if ( empty( $url ) || empty( $arrURL['scheme'] ) )
return new WP_Error('http_request_failed', __('A valid URL was not provided.')); return new WP_Error('http_request_failed', __('A valid URL was not provided.'));
@ -1146,6 +1151,8 @@ class WP_Http_Curl {
// The option doesn't work with safe mode or when open_basedir is set, and there's a // The option doesn't work with safe mode or when open_basedir is set, and there's a
// bug #17490 with redirected POST requests, so handle redirections outside Curl. // bug #17490 with redirected POST requests, so handle redirections outside Curl.
curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false ); curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false );
if ( defined( 'CURLOPT_PROTOCOLS' ) ) // PHP 5.2.10 / cURL 7.19.4
curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
switch ( $r['method'] ) { switch ( $r['method'] ) {
case 'HEAD': case 'HEAD':

View File

@ -113,7 +113,7 @@ class WP_oEmbed {
$providers = array(); $providers = array();
// Fetch URL content // Fetch URL content
if ( $html = wp_remote_retrieve_body( wp_remote_get( $url ) ) ) { if ( $html = wp_remote_retrieve_body( wp_remote_get( $url, array( 'reject_unsafe_urls' => true ) ) ) ) {
// <link> types that contain oEmbed provider URLs // <link> types that contain oEmbed provider URLs
$linktypes = apply_filters( 'oembed_linktypes', array( $linktypes = apply_filters( 'oembed_linktypes', array(
@ -195,7 +195,7 @@ class WP_oEmbed {
*/ */
function _fetch_with_format( $provider_url_with_args, $format ) { function _fetch_with_format( $provider_url_with_args, $format ) {
$provider_url_with_args = add_query_arg( 'format', $format, $provider_url_with_args ); $provider_url_with_args = add_query_arg( 'format', $format, $provider_url_with_args );
$response = wp_remote_get( $provider_url_with_args ); $response = wp_remote_get( $provider_url_with_args, array( 'reject_unsafe_urls' => true ) );
if ( 501 == wp_remote_retrieve_response_code( $response ) ) if ( 501 == wp_remote_retrieve_response_code( $response ) )
return new WP_Error( 'not-implemented' ); return new WP_Error( 'not-implemented' );
if ( ! $body = wp_remote_retrieve_body( $response ) ) if ( ! $body = wp_remote_retrieve_body( $response ) )

View File

@ -5396,7 +5396,8 @@ class wp_xmlrpc_server extends IXR_Server {
sleep(1); sleep(1);
// Let's check the remote site // Let's check the remote site
$linea = wp_remote_retrieve_body( wp_remote_get( $pagelinkedfrom, array( 'timeout' => 10, 'redirection' => 0 ) ) ); $linea = wp_remote_retrieve_body( wp_remote_get( $pagelinkedfrom, array( 'timeout' => 10, 'redirection' => 0, 'reject_unsafe_urls' => true ) ) );
if ( !$linea ) if ( !$linea )
return $this->pingback_error( 16, __( 'The source URL does not exist.' ) ); return $this->pingback_error( 16, __( 'The source URL does not exist.' ) );

View File

@ -1658,7 +1658,7 @@ function discover_pingback_server_uri( $url, $deprecated = '' ) {
if ( 0 === strpos($url, $uploads_dir['baseurl']) ) if ( 0 === strpos($url, $uploads_dir['baseurl']) )
return false; return false;
$response = wp_remote_head( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) ); $response = wp_remote_head( $url, array( 'timeout' => 2, 'httpversion' => '1.0', 'reject_unsafe_urls' => true ) );
if ( is_wp_error( $response ) ) if ( is_wp_error( $response ) )
return false; return false;
@ -1671,7 +1671,7 @@ function discover_pingback_server_uri( $url, $deprecated = '' ) {
return false; return false;
// Now do a GET since we're going to look in the html headers (and we're sure it's not a binary file) // Now do a GET since we're going to look in the html headers (and we're sure it's not a binary file)
$response = wp_remote_get( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) ); $response = wp_remote_get( $url, array( 'timeout' => 2, 'httpversion' => '1.0', 'reject_unsafe_urls' => true ) );
if ( is_wp_error( $response ) ) if ( is_wp_error( $response ) )
return false; return false;
@ -1906,6 +1906,7 @@ function trackback($trackback_url, $title, $excerpt, $ID) {
$options = array(); $options = array();
$options['timeout'] = 4; $options['timeout'] = 4;
$options['reject_unsafe_urls'] = true;
$options['body'] = array( $options['body'] = array(
'title' => $title, 'title' => $title,
'url' => get_permalink($ID), 'url' => get_permalink($ID),
@ -1953,62 +1954,13 @@ function weblog_ping($server = '', $path = '') {
* Default filter attached to pingback_ping_source_uri to validate the pingback's Source URI * Default filter attached to pingback_ping_source_uri to validate the pingback's Source URI
* *
* @since 3.5.1 * @since 3.5.1
* @see wp_http_validate_url()
* *
* @param string $source_uri * @param string $source_uri
* @return string * @return string
*/ */
function pingback_ping_source_uri( $source_uri ) { function pingback_ping_source_uri( $source_uri ) {
$uri = esc_url_raw( $source_uri, array( 'http', 'https' ) ); return (string) wp_http_validate_url( $source_uri );
if ( ! $uri )
return '';
$parsed_url = @parse_url( $uri );
if ( ! $parsed_url )
return '';
if ( isset( $parsed_url['user'] ) || isset( $parsed_url['pass'] ) )
return '';
if ( false !== strpos( $parsed_url['host'], ':' ) )
return '';
$parsed_home = @parse_url( get_option( 'home' ) );
$same_host = strtolower( $parsed_home['host'] ) === strtolower( $parsed_url['host'] );
if ( ! $same_host ) {
$host = trim( $parsed_url['host'], '.' );
if ( preg_match( '#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $host ) ) {
$ip = $host;
} else {
$ip = gethostbyname( $host );
if ( $ip === $host ) // Error condition for gethostbyname()
$ip = false;
}
if ( $ip ) {
if ( '127.0.0.1' === $ip )
return '';
$parts = array_map( 'intval', explode( '.', $ip ) );
if ( 10 === $parts[0] )
return '';
if ( 172 === $parts[0] && 16 <= $parts[1] && 31 >= $parts[1] )
return '';
if ( 192 === $parts[0] && 168 === $parts[1] )
return '';
}
}
if ( empty( $parsed_url['port'] ) )
return $uri;
$port = $parsed_url['port'];
if ( 80 === $port || 443 === $port || 8080 === $port )
return $uri;
if ( $parsed_home && $same_host && $parsed_home['port'] === $port )
return $uri;
return '';
} }
/** /**

View File

@ -496,6 +496,7 @@ function wp_get_http( $url, $file_path = false, $red = 1 ) {
$options = array(); $options = array();
$options['redirection'] = 5; $options['redirection'] = 5;
$options['reject_unsafe_urls'] = true;
if ( false == $file_path ) if ( false == $file_path )
$options['method'] = 'HEAD'; $options['method'] = 'HEAD';
@ -543,7 +544,7 @@ function wp_get_http_headers( $url, $deprecated = false ) {
if ( !empty( $deprecated ) ) if ( !empty( $deprecated ) )
_deprecated_argument( __FUNCTION__, '2.7' ); _deprecated_argument( __FUNCTION__, '2.7' );
$response = wp_remote_head( $url ); $response = wp_remote_head( $url, array( 'reject_unsafe_urls' => true ) );
if ( is_wp_error( $response ) ) if ( is_wp_error( $response ) )
return false; return false;
@ -758,6 +759,7 @@ function wp_remote_fopen( $uri ) {
$options = array(); $options = array();
$options['timeout'] = 10; $options['timeout'] = 10;
$options['reject_unsafe_urls'] = true;
$response = wp_remote_get( $uri, $options ); $response = wp_remote_get( $uri, $options );

View File

@ -330,3 +330,64 @@ function send_origin_headers() {
return false; return false;
} }
/**
* Validate a URL for safe use in the HTTP API.
*
* @since 3.5.2
*
* @return mixed URL or false on failure.
*/
function wp_http_validate_url( $url ) {
$url = esc_url_raw( $url, array( 'http', 'https' ) );
if ( ! $url )
return false;
$parsed_url = @parse_url( $url );
if ( ! $parsed_url )
return false;
if ( isset( $parsed_url['user'] ) || isset( $parsed_url['pass'] ) )
return false;
if ( false !== strpos( $parsed_url['host'], ':' ) )
return false;
$parsed_home = @parse_url( get_option( 'home' ) );
$same_host = strtolower( $parsed_home['host'] ) === strtolower( $parsed_url['host'] );
if ( ! $same_host ) {
$host = trim( $parsed_url['host'], '.' );
if ( preg_match( '#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $host ) ) {
$ip = $host;
} else {
$ip = gethostbyname( $host );
if ( $ip === $host ) // Error condition for gethostbyname()
$ip = false;
}
if ( $ip ) {
if ( '127.0.0.1' === $ip )
return false;
$parts = array_map( 'intval', explode( '.', $ip ) );
if ( 10 === $parts[0] )
return false;
if ( 172 === $parts[0] && 16 <= $parts[1] && 31 >= $parts[1] )
return false;
if ( 192 === $parts[0] && 168 === $parts[1] )
return false;
}
}
if ( empty( $parsed_url['port'] ) )
return $url;
$port = $parsed_url['port'];
if ( 80 === $port || 443 === $port || 8080 === $port )
return $url;
if ( $parsed_home && $same_host && $parsed_home['port'] === $port )
return $url;
return false;
}

View File

@ -536,7 +536,7 @@ endif;
* @return Snoopy style response * @return Snoopy style response
*/ */
function _fetch_remote_file($url, $headers = "" ) { function _fetch_remote_file($url, $headers = "" ) {
$resp = wp_remote_request($url, array('headers' => $headers, 'timeout' => MAGPIE_FETCH_TIME_OUT)); $resp = wp_remote_request($url, array('headers' => $headers, 'timeout' => MAGPIE_FETCH_TIME_OUT, 'reject_unsafe_urls' => true ));
if ( is_wp_error($resp) ) { if ( is_wp_error($resp) ) {
$error = array_shift($resp->errors); $error = array_shift($resp->errors);