背景
最近负责公司的接口日志的记录,日志中需要获取客户端的真实ip,单纯的我直接调用HttpServletRequest.getRemoteAddr()方法获取。后来去es 看日志发现全部都是测试服务器的ip地址、才想起配置了nginx,获取到的是nginx的地址
X-Forwarded-For
后续再网上找到对应的代码
public static String getIpAddr(HttpServletRequest request) throws Exception {
String ip = request.getHeader("X-Real-IP");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个IP值,第一个为真实IP。
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}
继而认识到 X-Forwarded-For 以及X-Real-IP
根据维基百科对 XFF的定义:
X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。 Squid 缓存代理服务器的开发人员最早引入了这一HTTP头字段,并由IETF在HTTP头字段标准化草案[1]中正式提出。
XFF的格式
X-Forwarded-For: client1, proxy1, proxy2
其中的值通过一个 逗号+空格 把多个IP地址区分开, 最左边(client1)是最原始客户端的IP地址, 代理服务器每成功收到一个请求,就把请求来源IP地址添加到右边。 在上面这个例子中,这个请求成功通过了三台代理服务器:proxy1, proxy2 及 proxy3。请求由client1发出,到达了proxy3(proxy3可能是请求的终点)。请求刚从client1中发出时,XFF是空的,请求被发往proxy1;通过proxy1的时候,client1被添加到XFF中,之后请求被发往proxy2;通过proxy2的时候,proxy1被添加到XFF中,之后请求被发往proxy3;通过proxy3时,proxy2被添加到XFF中,之后请求的的去向不明,如果proxy3不是请求终点,请求会被继续转发。
鉴于伪造这一字段非常容易,应该谨慎使用X-Forwarded-For字段。正常情况下XFF中最后一个IP地址是最后一个代理服务器的IP地址, 这通常是一个比较可靠的信息来源。
说白了就是用于记录请求链ip的,最左边的可能就是客户端真实的ip地址(可能非法伪造)
而X-Real-IP 可以算是一个广泛的自定义非标准的头部,用于记录上一个请求源的ip
场景模仿
01
多个代理服务器
Client--->nginx01---->nginx02--->应用服务器
Client的ip为:1.1.1.1
nginx01的ip为:2.2.2.2
nginx02的ip为:3.3.3.3
那么这个时候X-Real-IP 对应的值就会是2.2.2.2,显然不是真正客户端的ip
所以在使用这个值的时候应该看下当前项目使用的代理服务器有多少个,这个值是上一个请求源的ip的地址
02
伪造 X-Forwarded-For
X-Forwarded-For 非rfc标准,在nginx需要显示的进行配置才能记录请求链ip
一般配置如下
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
remote_addr,它们用逗号分开.如果没有"X-Forwarded-For" 请求头,则
remote_addr。$remote_addr变量的值是客户端的IP.
所以当客户端非法构造X-Forwarded-For,那么X-Forwarded-For中最左边的值就是客户端非法构造的IP值,而非真实的Ip值
所以解决的办法就是在第一个对外的nginx将客户端传来的设置为空然后设置为真实ip
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
最后
根据具体情况来写获取真实ip,需要第一个nginx对客户端传来的头X-Forwarded-For设置为空并正确赋值
public static String getIpAddr(HttpServletRequest request) throws Exception {
String ip = request.getHeader("X-Forwarded-For");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个IP值,第一个为真实IP。
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}
引用
https://www.jianshu.com/p/5b24f0dbac9f
https://zh.wikipedia.org/wiki/X-Forwarded-For#cite_note-1
https://imququ.com/post/x-forwarded-for-header-in-http.html
http://blog.51cto.com/cwind/989220
网友评论