- URL: https://www.laruence.com/en/2016/07/19/3101.html
- Please include attribution when republishing.
It's been a while since my last post — the blog had completely gone to seed. I woke up this morning ready to head to work, then saw the downpour and figured I'd be stuck in traffic for who knows how long. On top of that, a few people told me they didn't quite get the HTTPOXY vulnerability I re-tweeted yesterday and wanted to know whether it would affect ordinary applications, so I sat down and wrote this up. Let's just wait out the morning rush first 😉
One note before we start: I'm only going to cover the PHP side of things here. For Go, Python, and the rest, I don't know that stuff well enough — just go read the original 🙂
The writeup for the vulnerability lives here: https://httpoxy.org/ (if you didn't understand it, it's definitely because your English never got past CET-6 🙂 )
There's one key piece of background here. For a long time we've been accustomed to using an environment variable named "http_proxy" to configure a request proxy. On the command line, for example, we often do this:
http_proxy=127.0.0.1:9999 wget http://www.laruence.com/
Setting that http_proxy env var tells wget to fetch http://www.laruence.com/ through the proxy.
As far as anyone can tell, the practice traces back to CERN libwww 2.15 from 1994. My guess is that a lot of tools of the day were built on that library, and it slowly became a de facto standard. Except those applications all demanded that http_proxy be lowercase, which wasn't quite enough to create today's vulnerability.
But probably because environment variable names conventionally use uppercase, some libraries later started supporting the uppercase HTTP_PROXY too — for example, yum: https://www.centos.org/docs/5/html/yum/sn-yum-proxy-server.html
After that, many libraries in many languages began supporting this configuration — some accepted the uppercase form, some the lowercase, and some both.
- Guzzle (uppercase): https://github.com/guzzle/guzzle/blob/10a49d5e1b8729c5e05cbdbf475b38b7099eb35e/src/Client.php#L167
- Artax (both uppercase and lowercase): https://github.com/amphp/artax/blob/3e3eedafcecc82c3c86c3a00ca602b5efa9c2cfa/lib/HttpSocketPool.php#L26
Including ourselves — we've very likely written code like the following in our day-to-day work (I did, when I was writing a web crawler):
<?php
$http_proxy = getenv("HTTP_PROXY");
if ($http_proxy) {
$context = array(
'http' => array(
'proxy' => $http_proxy,
'request_fulluri' => true,
),
);
$s_context = stream_context_create($context);
} else {
$s_context = NULL;
}
$ret = file_get_contents("http://www.laruence.com/", false, $s_context);
So here's the problem. In CGI mode (RFC 3875), every request header gets a HTTP_ prefix and is registered as an environment variable. So if you send a "Proxy: xxxxxx" header, PHP will register it as the HTTP_PROXY env var — and suddenly getenv("HTTP_PROXY") is attacker-controllable. That means every outgoing request like the one above gets proxied wherever the attacker wants, and from there they can spoof, intercept, and tamper with your requests...
For example:
curl -H "Proxy:127.0.0.1:8000" http://host.com/httpoxy.php
So for this vulnerability to affect you, a few core conditions must all hold:
- Your service makes outgoing requests to external resources
- Your service uses the (uppercase) HTTP_PROXY env var to proxy its requests — whether in code you wrote yourself or via some buggy library
- Your service runs under PHP's CGI mode (cgi, php-fpm)
If you don't meet those conditions, congratulations — this vulnerability has no impact on you 🙂
Postscript: After the fact, someone on Weibo called out that I may have unconsciously made the impact of this problem sound mild. In reality, take it one step further: under CGI, any environment variable starting with HTTP_ is untrusted, and you should never use it for anything sensitive. One more point — I've experienced this firsthand: people in security have an almost boundless imagination. What looks like a tiny detail to the rest of us, in the hands of a security researcher armed with that imagination and strong social-engineering skills, can turn into a massive attack.
Once you understand the mechanism, the fix is simple. Using Nginx as an example, add this to your config:
fastcgi_param HTTP_PROXY "";
So my recommendation: even if you're not affected by this vulnerability, add this config line anyway.
And if you maintain a library, or for some reason can't modify your service's configuration, then add a SAPI check in your code — unless you're running in CLI mode, never trust the http_proxy env var:
<?php
if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
// only in CLI mode is the HTTP_PROXY env var trustworthy
}
Guzzle's fix works exactly this way: Addressing HTTP_PROXY security vulnerability
Addendum: starting with PHP 5.5.38, getenv() gained a second parameter, local_only = false. When set to true, the value is read only from the system's local environment table, which fixes the problem. Stock PHP also started blocking HTTP_PROXY: fix
thanks
Be First to Comment