转载自:
1. 伪造原理
在阅读本文前,大家要有一个概念,在实现正常的TCP/IP 双方通信情况下,是无法伪造来源 IP 的,也就是说,在 TCP/IP 协议中,可以伪造数据包来源 IP ,但这会让发送出去的数据包有去无回,无法实现正常的通信。这就像我们给对方写信时,如果写出错误的发信人地址,而收信人按信封上的发信人地址回信时,原发信人是无法收到回信的。
一些DDoS 攻击,如 SYN flood, 就是利用了 TCP/ip 的此缺陷而实现攻击的。《计算机网络》教材一书上,对这种行为定义为“发射出去就不管”。
因此,本文标题中的伪造来源IP 是带引号的。并非是所有 HTTP 应用程序中存在此漏洞。
那么在HTTP 中, " 伪造来源 IP", 又是如何造成的?如何防御之?
在理解这个原理之前,读者有必要对HTTP 协议有所了解。 HTTP 是一个应用层协议,基于请求 / 响应模型。客户端(往往是浏览器)请求与服务器端响应一一对应。
请求信息由请求头和请求正文构成(在GET 请求时,可视请求正文为空)。请求头类似我们写信时信封上的基本信息,对于描述本次请求的一些双方约定。而请求正文就类似于信件的正文。服务器的响应格式,也是类似的,由响应头信息和响应正文构成。
为了解这个原理,可使用Firefox Firebug, 或 IE 浏览器插件 HTTPwatch 来跟踪 HTTP 请求 / 响应数据。
左边即是请求数据,右边即是服务器响应数据。
特点:
请求 header 中除第一行外,其它行均由 header 名称, header 值组成,如 Accept-Encoding: gzip, deflate , header 名称与值之间有冒号相隔,之间的空格是可有可无的。
那么,在HTTP 应用程序中,如何取得指定的请求 header 信息呢?
以下是js获取ip地址:
public String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
由上可知: 服务器通过request中的
x-forwarded-for 和 client-ip 来获取 客户端的ip.
那么,如果客户端伪造 Client-Ip, X-Forward-For ,可以欺骗此程序,达到“伪造 IP ”之目的.
2. 那么如何伪造这项值(X-Forward-For)?
方法一: 如果你会写程序,并了解HTTP 协议,直接伪造请求 header 即可
方法二: 使用 Firefox 的Moify Headers 插件即可(推荐)
3. 如何避免伪造ip
方法:服务器重新配置X-Forward-For 为正确的值
服务器集群之间的通信,是可以信任的。我们要做的就是在离用户最近的前端代理上,强制设定X-Forward-For 的值,后端所有机器不作任何设置,直接信任并使用前端机器传递过来的 X-Forward-For 值即可。
即在最前端的Nginx 上设置:
location ~ ^/static {
proxy_pass ....;
proxy_set_header X-Forward-For $remote_addr ;
}
如果最前端(与用户直接通信)代理服务器是与php fastcgi 直接通信,则需要在其上设定:
location ~ "\.+\.php$" {
fastcgi_pass localhost:9000;
fastcgi_param HTTP_X_FORWARD_FOR $remote_addr;
}
记住,$remote_addr 是 nginx 的内置变量,代表了客户端真实(网络传输层) IP 。通过此项措施,强行将 X-Forward-For 设置为客户端 ip, 使客户端无法通过本文所述方式“伪造 IP ”。