HTTPOXY漏洞說明

好久沒寫文章了, 博客都長草了, 早上起來本來想去上班, 一看這麼大雨, 這要上路了不得堵死啊.

再加上有同學對我昨天轉發的微博HTTPOXY漏洞表示不理解, 問會不會影響普通應用, 於是就寫篇文章介紹下, 等早高峰過了吧;)…..

不過要注意的是, 這裡我只是介紹PHP這個角度, 關於Go和Python等其他角度的,因為我也不是”很”懂,你們還是看原文吧 🙂

漏洞原文在這裡, https://httpoxy.org/, 沒看懂的一定都是英語沒過6級的 🙂

這裡有一個核心的背景是, 長久一來我們習慣了使用一個名為”http_proxy”的環境變量來設置我們的請求代理, 比如在命令行我們經常這麼用:

 http_proxy=127.0.0.1:9999 wget http://www.laruence.com/

通過設置一個http_proxy的環境變量, 讓wget使用代理請求http://www.laruence.com/

有據可考的是, 這樣的設定最初來自1994年的CERN libwww 2.15, 我猜測大概是當時很多工具是基於這個類庫做的, 於是就慢慢成了一個既定標準吧. 只不過這些應用都要求http_proxy是全部小寫的, 還不足以造成今天這個漏洞.

但估計是因為環境變量習慣都是大寫的原因吧, 後來有的類庫開始支持大寫的HTTP_PROXY, 比如yum: https://www.centos.org/docs/5/html/yum/sn-yum-proxy-server.html

再後來很多的類庫, 各種語言的, 都開始支持這種配置, 有的支持大寫的, 有的支持小寫的, 還有的都支持.

包括我們自己, 也很有可能在日常的工作中寫出如下的代碼(我就曾經在寫爬蟲的時候寫過):

 <?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);

那麼問題來了, 在CGI(RFC 3875)的模式的時候, 會把請求中的Header, 加上HTTP_ 前綴, 註冊為環境變量, 所以如果你在Header中發送一個Proxy:xxxxxx, 那麼PHP就會把他註冊為HTTP_PROXY環境變量, 於是getenv(“HTTP_PROXY”)就變成可被控制的了. 那麼如果你的所有類似的請求, 都會被代理到攻擊者想要的地址,之後攻擊者就可以偽造,監聽,篡改你的請求了…

比如:

  curl -H "Proxy:127.0.0.1:8000" http://host.com/httpoxy.php

所以, 這個漏洞要影響你, 有幾個核心前提是:

  • 你的服務會對外請求資源
  • 你的服務使用了HTTP_PROXY(大寫的)環境變量來代理你的請求(可能是你自己寫,或是使用一些有缺陷的類庫)
  • 你的服務跑在PHP的CGI模式下(cgi, php-fpm)

如果你沒有滿足上面的條件, 那麼恭喜你,你不受此次漏洞影響 :).

後記: 在微博上有同學提醒, 我可能把這個問題的影響潛意識的讓大家覺得危害不大, 但實際上, 延伸一下: 所有HTTP_開頭的環境變量在CGI下都是不可信的, 千萬不要用于敏感操作, 另外一點就是, 我深刻的體會過, 做安全的同學想象力非常豐富, 雖然看似很小的一個點, 但到了安全的同學手裡, 配合他們豐富的想象力, 強大的社工能力, 也是能做出巨大攻擊效果的….

那知道了原理修復起來也很簡單了, 以Nginx為例, 在配置中加入:

    fastcgi_param HTTP_PROXY "";

所以建議, 即使你不受此次漏洞影響, 也應該加入這個配置.

而如果你是一個類庫的作者,或者你因為什麼原因沒有辦法修改服務配置, 那麼你就需要在代碼中加入對sapi的判斷, 除非是cli模式, 否則永遠不要相信http_proxy環境變量,

 <?php if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {    //只有CLI模式下, HTTP_PROXY環境變量才是可控的 }

就好比Guzzle的這個修復:Addressing HTTP_PROXY security vulnerability

補充: 從PHP5.5.38開始, getenv增加了第二個參數, local_only = false, 如果這個參數為true, 則只會從系統本地的環境變量表中獲取, 從而修復這個問題, 並且默認的PHP將攔截HTTP_PROXY: fix

thanks

Leave a Reply

Your email address will not be published. Required fields are marked *