背景:
公司一个开放接口,被爬虫获取,黑客使用多个代理服务器,发起网络攻击,因ip伪造,导致网关黑名单拦截失败。
概念:
X-Forwarded-For X-Forwarded-For 是一个扩展头。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP,现在已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中.
网络请求示意图
分析
网络请求通过层层网络设备的转发,最终到达服务端。每一个环节收到请求中的remote_addr必定是上游环节的真实IP,转发设备将请求中的remote_addr追加到X-Forwarded-For之后形成请求全链路。
程序通常通过http协议头中的X-Forwarded-For来获取ip,传统获取真实ip方法是获取X-Forwarded-For请求头中左侧第一个ip当做真实ip,但该字段很容易被伪造,只要请求头中添加该字段,后续网络代理服务,只会在后面添加连接ip,这就造成真实ip获取失败,ip拦截失败。
伪造的ip:
X-Forwarded-For: 伪造ip1, 伪造ip2, client_ip, proxy1_ip, proxy2_ip
如果想要过滤掉或覆盖伪造的ip的话,X-Forwarded-For列表需从右向左遍历,排除内网ip与其他可信代理ip地址, 获取到的第一个有效ip地址既为用户的真实ip地址。
真实IP获取方法如下:
/**
* internalProxies即内网代理IP,这些默认都是受信任的IP,
* internalProxies 为信任的代理服务器 如阿里云
* @param requstForwardForIps
* @return 请求真实ip
*/
public String getRemoteIpByForward(List<String> requstForwardForIps) {
if (CollectionUtils.isEmpty(requstForwardForIps) || StringUtils.isBlank(requstForwardForIps.get(0))) {
return null;
}
String forwardRemoteIp = requstForwardForIps.get(0);
List<String> originalRemoteIpList = Arrays.asList(forwardRemoteIp.split(","));
String realOriginalRemoteIp = "";
//从右向左遍历
for (int i = originalRemoteIpList.size(); i > 0; i--) {
String remoteIp = originalRemoteIpList.get(i - 1).trim();
//匹配内网ip
boolean matches = remoteIp.trim().matches(internalProxies);
if (matches) {
continue;
}
//匹配可信代理
boolean trustedProxiesMatch = remoteIp.trim().matches(trustedProxies);
if (trustedProxiesMatch) {
continue;
}
realOriginalRemoteIp = remoteIp;
break;
}
return realOriginalRemoteIp;
}