-
Notifications
You must be signed in to change notification settings - Fork 82
IP获取原理和典型使用场景
很多公司部署服务已经不会直接裸着暴露端口出去。多数会放在LBS(负载均衡)或WAF(Web应用防火墙)后面,此时正确公网IP获取就显得尤为重要。
场景:
服务器不经过任何反向代理,直接接收信息
这个一般在开发环境使用,因为开发环境一般不会部署LBS或WAF。也是最能直接获取到客户端请求的环境。
场景:
这一般就是公司正式部署环境了,此时一般会部署LBS或WAF。也有先接WAF,后接LBS,最后再到ghost_sa服务端的情况。
LBS或WAF获取公网IP的原理: LBS或WAF获取到IP(此时不管是什么IP)都会在HTTP请求头中添加X-Forwarded-For一类的参数,这个参数根据不同的供应商,会有些许不同,可能是一个,也可能混用多个不同的参数。以X-Forwarded-For举例,他的格式通常为:
clientIP, proxy1IP, proxy2IP, ...
通常第一个IP为客户端IP,后面的IP为代理IP
为了兼容各种公司不同的部署方式,ghost_sa采用了投票机制,即多个IP都为同一个IP时,则认为该IP为客户端IP。
以当前的版本为例,ghost_sa会通过component/url_tools.py下的get_req_info方法,循环寻找请求头中以下几个参数:
['X-Forwarded-For','X-Original-Forwarded-For','X-True-Ip','X-Client-Ip','Wl-Proxy-Client-Ip','Proxy-Client-IP','X-Real-IP','HTTP_CLIENT_IP']
如果找到,则会把这个参数里的第一个,即clientIP切出来进行判断,如果符合ip地址的特征,再判断该IP是内网还是公网,如果是公网,则加入公网池,如果是内网,则加入内网地址池。
循环结束后,还会把ghost_sa直接获取到的IP进行判断,加入公网或内网地址池。
最后,会通过投票机制,优先查询公网池,选取公网池里出现次数最多的IP,作为用户IP。
如果公网池里没有IP,则查询内网池,选取内网池里出现次数最多的IP,作为用户IP。
使用这种方法,可以保证无论什么部署方式,都能正确获取到公网IP。
1.用户是通过内网访问,或由于某些原因,WAF或LBS不能传递参数。确实是获取不到公网IP。
2.海外部署供应商IP比较富裕或国内异地部署不走VPN,WAF和LBS直接用公网IP传递。这种情况存在所有环节都是公网IP的情况。容易造成判断失误。
针对这两种情况,还有一些特殊使用场景的,还可以使用上报端获取公网IP的方式。
场景:
1.指定IP测试 当用户在内网进行测试时,可以通过指定IP的方式,让服务器识别用户传递的IP为。
2.CDN或OSS等中间件传递信息 在使用access_control(第三方鉴权)功能时,CDN需要向ghost_sa传递用户公网IP。否则识别到的都是CDN的IP,将不能根据用户IP或IP段鉴权。
3.服务端埋点 当用户使用服务端埋点时,向ghost_sa上报埋点的是服务端,而不是用户。所以ghost_sa无法获取到用户的IP,这时需要服务端向ghost_sa传递用户的IP。 不过这个不是推荐用法,在这个场景下,更推荐使用埋点传递公网IP,即下面的场景。
以当前的版本为例,还是在component/url_tools.py下的get_req_info方法: ghost_sa会以如下的顺序,依次在请求的json, form, query中寻找以下参数:
"http_x_forward_for"
"remote_addr"
"ip"
但是不会在请求头里寻找。以确保上报的值,确实是人为配置的,而不是来自开发环境的自动配置。
注意:这些值只会校验确实符合ip地址的特征,而不会进行IP的判断。如果传递的是内网ip,或者多个ip拼接。也会被认为符合ip地址的特征。
安心提示:基于CDN等服务无法执行太过个性化的脚本。虽然这些值不会进行合法性判断,但是由于不是用户直接透传的,而是通过CDN或第三方透传的,CDN可以挡住这三个敏感参数由用户请求透传。所以爬虫其实无法利用这三个参数,伪造自己的IP地址。
场景:
1.服务端埋点 当用户使用服务端埋点时,向ghost_sa上报埋点的是服务端,而不是用户。所以ghost_sa无法获取用户ip。当需要记录用户ip,以用于access_control,或基于IP地址的分析时,如请求的来源分布。
2.客户端有指定IP地址的需求 当需要追踪的用户IP地址,不是用户请求上报埋点的IP地址时。比如处于多WAN或SD-WAN等具有分流功能的网络环境时。请求业务服务端和上报埋点走的不是用一个路由。
以当前的版本为例,在configs/admin.py下,user_ip_first 参数为True时,会取 configs/admin.py 下的 user_ip_key 定义的字段作为key,从埋点上报中,寻找这个Key,一旦找到,会比对该key的value是否符合ip地址的特征。如果符合,会再次判断该IP是否为公网IP,如果是公网IP。则认定该IP为该条埋点的上报IP。
如果不开启user_ip_first,或开启user_ip_first,但是没有定义user_ip_key,或开启user_ip_first,并且定义了user_ip_key,但是user_ip_key的值不是ip地址的特征,或不是公网IP。都会忽略该条埋点的上报IP。 依照 URL传递IP >> 服务端获取公网IP 的顺序,确定IP地址。