安全开发之IP地址伪造

1.1  IP地址伪造漏洞

1.1.1  漏洞挖掘

1.1.1.1  漏洞描述

漏洞描述:

IP地址伪造通常是伪造来源IP,为了绕过服务器端IP地址过滤,使非授权IP地址可以获取更多的防问权限。但是在正常的TCP/IP通信中,伪造数据包来源 IP会让发送出去的数据包返回到伪造的IP上,无法实现正常的通信。既然无法实现TCP/IP层级别的IP伪造,那么可以在应用层HTTP协议通过X-Forwarded-For这个扩展头部来实现。X-Forwarded-For位于HTTP协议的请求头,是一个扩展头部简称 XFF 头,只有在传输过程中经过 HTTP 代理或者负载均衡服务器时才会添加该项。

XFF 格式如下:

X-Forwarded-For: client1, proxy1, proxy2

可以看出,XFF 头信息可以有多个,中间用逗号分隔,第一项为真实的客户端ip,剩下的就是经过的代理或负载均衡服务器的ip地址。

       那么在代码中获取XFF 头信息中的第一个元素即为真实客户端IP,但是X-Forwarded-For作为HTTP协议头中的一部分通常是可以伪造的,很多系统简单的获取XFF头中定义的IP地址设置为来源地址,进行访问控制,攻击者可以篡改HTTP请求包中XFF项进行IP地址伪造。

注意:此类问题漏洞不能在代码中见到X-Forwarded-For就判断为IP地址伪造,应该结合上下文是否存在IP过滤或者IP访问控制的业务逻辑,否则尽量不要提此类漏洞。本身利用X-Forwarded-For是业务需要,为了应对传输过程中经过多层代理或者负载均衡服务器也能获取到真实IP的情况。

1.1.1.2  漏洞场景复现

获取IP地址的代码如下:

    public static String getIpAddr(HttpServletRequest request) {

        String ip = request.getHeader("X-Real-IP");

 

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {

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

        }

   // Proxy-Client-IP 字段和 WL-Proxy-Client-IP 字段只在 ApacheWeblogic Plug-In Enable+WebLogic 搭配下出现,其中“WL就是 WebLogic 的缩写。

即访问路径是:Client -> Apache WebServer + Weblogic http plugin -> Weblogic Instances,只为兼容,可以直接忽略

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {

            ip = request.getRemoteAddr();

        }

       //EMOTE_ADDR 是客户端跟服务器“握手”时的IP,但如果使用了“匿名代理”,REMOTE_ADDR将显示代理服务器的ip,或者最后一个代理服务器的ip

        if (ip != null) {

            if (ip.indexOf(',') > 0) {

                // 获取split中的第一个元素,如果使用代理(或者是多级)从代理中取出真实IP地址

                ip = ip.split(",")[0];

            }

        } else {

            ip = "0.0.0.0";

        }

        return ip;

    }

 

综上,在代码中获取到的IP,很有可能是伪造的IP地址。服务端系统会根据获取到的IP地址,进行IP过滤或者权限控制,代码如下:

/**

     * 校验指定IP是否被禁用

     *

     * @param ip

     * @return

     */

    public boolean verifyIP(String ip) {

        if (StringUtils.isBlank(ip)) {

            return true;

        }

        return securityDAO.countIPBan(ip) > 0 ? false : true;

    }

如果攻击者通过篡改X-Forwarded-For的IP地址,即可绕过IP禁用的限制

 

1.1.1.3  漏洞修复建议

针对IP伪造漏洞的修复方案需要考虑如下两种情况:

1.   客户端直接访问的web应用情况,中间不存在代理或者负载均衡,这种情况可以直接在代码层面利用ip = request.getRemoteAddr();获取客户端真实IP,不需要通过其它项进行获取。

2.   客户端需要经过多层代理或者负载均衡才能访问到web应用,在这种情况下如果还是在代码层面利用ip =request.getRemoteAddr();进行获取,获取到的是代理的IP。那么就需要获取XFF的split第一个元素IP作为真实IP,但是这里存在被伪造的风险。下面针对Nginx 进行安全配置:

在默认情况下,Nginx并不会对X-Forwarded-For头做任何的处理,除非用户使用proxy_set_header参数设置:

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

$proxy_add_x_forwarded_for变量包含客户端请求头中的"X-Forwarded-For",与$remote_addr用逗号分开,如果没有"X-Forwarded-For"请求头,则$proxy_add_x_forwarded_for等于$remote_addr。

nginx 配置
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_set_header X-Forwarded-For $remote_addr;

$remote_addr变量的值是客户端的IP

清空了客户端伪造传入的X-Forwarded-For,保证了使用request.getHeader("x-forwarded-for") 获取的ip为真实ip。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章