WordPress.org

Make WordPress Core

Opened 2 years ago

Last modified 19 months ago

#34053 new defect (bug)

HTTP API (Curl backend) inappropriately sends Content-Length header on POST requests made through a proxy server CONNECT

Reported by: petertvs Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 4.3.1
Component: HTTP API Keywords:
Focuses: administration Cc:

Description

When WordPress is configured to communicate with the outside world through a HTTP proxy server, using

define('WP_PROXY_HOST', 'some-proxy-server');
define('WP_PROXY_PORT', '3128');

in wp-config.php, HTTP POST requests that are over HTTPS, and pass through the proxy server, get a Content-Length header inserted in the CONNECT request, instead of in the headers of the POST request made inside the HTTPS tunnel.

On certain proxy servers configured to parse requests strictly, they will reject this outer CONNECT request with a HTTP 400 Bad Request, as the Content-Length header should not be there(?)

Here is an HTTP POST request and response, going via a HTTPS CONNECT request (in this case a request for Google's Recaptcha through the Contact Form 7 plugin) behind a BlueCoat proxy server configured with tolerant-request-parsing off:

CONNECT www.google.com:443 HTTP/1.1
Host: www.google.com:443
User-Agent: WordPress/4.3.1; https://staging.testvalley.hants.sch.uk
Proxy-Connection: Keep-Alive
Accept-Encoding: deflate;q=1.0, compress;q=0.5, gzip;q=0.5
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 1052

HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Proxy-Connection: close
Connection: close
Content-Length: 513

invalid_request: Your request could not be processed. Request could not be handled    

To demonstrate this is not a plugin issue, a similar request (a check for core updates by clicking 'Check again' on the Updates page in WP-Admin) that is also affected:

CONNECT api.wordpress.org:443 HTTP/1.1
Host: api.wordpress.org:443
User-Agent: WordPress/4.3.1; https://staging.testvalley.hants.sch.uk
Proxy-Connection: Keep-Alive
Accept-Encoding: deflate;q=1.0, compress;q=0.5, gzip;q=0.5
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 193

HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Proxy-Connection: close
Connection: close
Content-Length: 513

invalid_request: Your request could not be processed. Request could not be handled   

HTTP (not HTTPS) requests, including POST, proceed fine through the proxy. HTTPS GET requests also work correctly.

This seems to be an issue that derives from where includes/class-http.php injects the Content-Length header before dispatching the request to the chosen HTTP backend. This header injection does not cause issues with a HTTP POST request, but in the case of an HTTPS POST request going through the proxy, the Content-Length header ends up in the HTTPS CONNECT request to the proxy, rather than the actual request to the server that is wrapped inside the established tunnel.

Commenting out lines 273 and 274 of includes/class-http.php causes the above two requests to succeed.

Change History (4)

#1 @dd32
2 years ago

This is a kind of wacky cURL behaviour.. but kind of makes sense.

cURL has fixed this within the last year in 7.37.0, and now has an extra set of options to control the behaviour:

http://curl.haxx.se/libcurl/c/CURLOPT_HTTPHEADER.html
http://curl.haxx.se/libcurl/c/CURLOPT_HEADEROPT.html

Unfortunately, PHP's cURL handler doesn't appear to support these yet. https://bugs.php.net/bug.php?id=68176

I'm not sure we should/want to remove the field, but it effectively breaks some proxy servers.

#2 @awbauer
2 years ago

We're seeing the same issue here. In our current case, HTTPS POST connections to https://www.google.com/recaptcha/api/siteverify (with content in the body) time out 100% of the time. When the POSTs are submitted without body content, the requests complete no problem.

Commenting out those lines in class-http.php has been the only fix that's worked for us.

#3 @awbauer
2 years ago

Actually, just discovered that tapping into the http_api_curl action provides a functioning workaround.

<?php
function bu_http_api_curl( $handle, $r, $url ) {
        if ( ! empty( $r['body'] ) || 'POST' == $r['method'] || 'PUT' == $r['method'] ) {
                unset($r['headers']['Content-Length']);
                curl_setopt( $handle, CURLOPT_HTTPHEADER, $r['headers'] );
        }
}
add_action( 'http_api_curl', 'bu_http_api_curl', 10, 3 );

#4 @Veraxus
19 months ago

Just started running into this as well. The proxy servers outright reject the request because the Content-Length header triggers a malformed warning. Currently, we're using a tiny mu-plugin fix similar to @awbauer's comment and are exploring the logistics of contributing a patch.

This really should be fixed since Content-Length is definitively non-standard.

Note: See TracTickets for help on using tickets.