PHP 、Python 等网站应用惊爆远程代理漏洞:httpoxy 2016-07-20 17:15
[indent]这是一个针对 PHP、Go、Python 等语言的 CGI 应用的漏洞。
[/indent]httpoxy 是一系列影响到以 CGI 或类 CGI 方式运行的应用的漏洞名称。简单的来说,它就是一个名字空间的冲突问题。
[*]RFC 3875 (CGI)中定义了从 HTTP 请求的 Proxy头部直接填充到环境变量HTTP_PROXY的方式
[*]HTTP_PROXY是一个常用于配置外发代理的环境变量
如果我的 Web 应用存在这种漏洞会怎么样?
当一个利用了此漏洞的 HTTP 客户端发起请求时,它可以做到:
[*]通过你的 Web 应用去代理请求别的 URL
[*]直接让你的服务器打开指定的远程地址及端口
[*]浪费服务器的资源,替攻击者访问指定的资源
哪些受到影响?
以下情况会存在安全漏洞:
[*]代码运行在 CGI 上下文中,这样 HTTP_PROXY就会变成一个真实的或模拟的环境变量
[*]一个信任 HTTP_PROXY的 HTTP 客户端,并且支持代理功能
[*]该客户端会在请求内部发起一个 HTTP(或 HTTPS)请求
[td]
| 语言 | 环境 | HTTP 客户端 |
| PHP | php-fpm
mod_php | Guzzle 4+
Artax |
| Python | wsgiref.handlers.CGIHandler
twisted.web.twcgi.CGIScript | requests |
| Go | net/http/cgi | net/http |
PHP
[*]是否存在缺陷依赖于你的应用代码和 PHP 库,但是影响面看起来似乎非常广泛
[*]只要在处理用户请求的过程中使用了一个带有该缺陷的库,就可能被利用
[*]如果你使用了有该缺陷的库,该缺陷会影响任意 PHP 版本
[*]甚至会影响到替代的 PHP 运行环境,比如部署在 FastCGI 模式下的 HHVM
[*]Guzzle 4.0.0rc2 及其以后版本受影响,Guzzle 3 及更低版本不受影响
[*]其它的例子还有 Composer 的 StreamContextBuilder 工具类
Python
[*]Python 代码只有部署在 CGI 模式下才存在缺陷,一般来说,存在缺陷的代码会使用类似wsgiref.handlers.CGIHandler的 CGI 控制器
[*]正常方式部署的 Python web 应用不受影响(大多数人使用 WSGI 或 FastCGI,这两个不受影响),所以受到影响的 Python 应用要比 PHP 少得多
[*]wsgi 不受影响,因为 os.environ 不会受到 CGI 数据污染
[*]Go 代码必须部署在 CGI 下才受影响。一般来说受到影响的代码会使用 net/http/cgi包
[*]像 Python 一样,这并不是部署 Go 为一个 Web 应用的通常方式。所以受到影响的情形很少
[*]相较而言,Go 的 net/http/fcgi包并不设置实际的环境变量,所以不受影响
最好的修复方式是在他们攻击你的应用之前尽早封挡 Proxy请求头部。这很简单,也很安全。
[*]说它安全是因为 IETF 没有定义 Proxy请求头部,也没有列在IANA 的消息头部注册[1]中。这表明对该头部的使用是非标准的,甚至也不会临时用到
[*]符合标准的 HTTP 客户端和服务器绝不应该读取和发送这个头部
[*]你可以从请求中去掉这个头部或者干脆整个封挡使用它的请求
[*]你可以在上游没有发布补丁时自己来解决这个问题
[*]当 HTTP 请求进来时就检查它,这样可以一次性修复好许多存在缺陷的应用
[*]在反向代理和应用防火墙之后的应用剔除 Proxy请求头部是安全的
Nginx/FastCGI
使用如下语句封挡传递给 PHP-FPM、PHP-PM 的请求头,这个语句可以放在 fastcgi.conf 或 fastcgi_param 中(视你使用了哪个配置文件):
[*]fastcgi_param HTTP_PROXY "";
Apache
对于 Apache 受影响的具体程度,以及其它的 Apache 软件项目,比如 Tomcat ,推荐参考 Apache 软件基金会的官方公告[2]。 以下是一些主要信息:
如果你在 Apache HTTP 服务器中使用 mod_cgi来运行 Go 或 Python 写的脚本,那么它们会受到影响(这里HTTP_PROXY环境变量是“真实的”)。而mod_php由于用于 PHP 脚本,也存在该缺陷。
如果你使用 mod_headers 模块,你可以通过下述配置在进一步处理请求前就 unset 掉Proxy请求头部:
[*]RequestHeader unset Proxy early
[*]SecRule &REQUEST_HEADERS
roxy "@gt 0" "id:1000005,log,deny,msg:'httpoxy denied'"
HAProxy
通过下述配置剔除该请求头部:
[*]http-request del-header Proxy
通过下述语句取消该头部,请将它放到已有的 vcl_recv 小节里面:
[*]sub vcl_recv {
[*][...]
[*]unset req.http.proxy;
[*][...]
[*]}
使用如下语句移除该头部。把它放到已有的过滤器里面:
[*]http protocol httpfilter {
[*]match request header remove "Proxy"
[*]}
弹回包含 Proxy头部的请求。
[*]创建一个 /path/to/deny-proxy.lua文件,让它对于 lighttpd 只读,内容如下:
if (lighty.request["Proxy"] == nil) then return 0 else return 403 end
[*]修改 lighttpd.conf以加载mod_magnet模块,并运行如上 lua 代码:
server.modules += ( "mod_magnet" )
magnet.attract-raw-url-to = ( "/path/to/deny-proxy.lua" )
从请求中剔除 Proxy头部。加入如下语句到lighttpd.conf中:
[*]req_header.remove "Proxy";
用户端的修复不能解决该缺陷,所以不必费劲:
[*]使用 unset($_SERVER['HTTP_PROXY'])并不会影响到getenv 返回的值,所以无用
[*]使用 putenv('HTTP_PROXY=')也没效果(putenv 只能影响到来自实际环境变量的值,而不是来自请求头部的)
该漏洞首次发现与15年前。
[*]2001 年 3 月
Randal L. Schwartz 在 libwww-perl 发现该缺陷并修复。
[*]2001 年 4 月
Cris Bailiff 在 curl 中发现该缺陷并修复。
[*]2012 年 7 月
在Net::HTTP 的HTTP_PROXY实现中, Ruby 团队的 Akira Tanaka 发现了该缺陷
[*]2013 年 11 月
在 nginx 邮件列表中提到了该缺陷。发现者 Jonathan Matthews 对此不太有把握,不过事实证明他是对的。
[*]2015 年 2 月
Stefan Fritsch 在 Apache httpd-dev 邮件列表中提到了它。
[*]2016 年 7 月
Vend 安全团队的 Scott Geary 发现了对该缺陷,并且它影响到了 PHP 等许多现代的编程语言和库。
[1]: http://www.iana.org/assignments/ ... ssage-headers.xhtml
[2]: https://www.apache.org/security/asf-httpoxy-response.txt
[3]: https://www.apache.org/security/asf-httpoxy-response.txt