服务器受到网络攻击时,如何获取请求客户端的真实 IP?
网络攻击
前不久公司遭受了一次网络攻击。
早晨刚到公司,就发现登录接口的调用次数飙升,很快就确认是被恶意攻击,让安全部门做网关入口针对对方 IP 加了限制。
并统一对所有的 IP 加了调用的频率限制。

登录
基本每一家公司都会有登录接口,然而无论大小,多少都会存在一些问题。
最核心的准则这里稍微提一下,以后有机会展开:
(1)密码一定要加密存储(而且不能是简单的 MD5),日志一定要脱敏。
(2)登录接口一定要添加验证码,防止接口被恶意调用的第一步
(3)禁止用户使用弱口令,弱口令字典可自行 github
(4)异地登录等要求用户进行二次验证
当然,个人认为最好的方式还是限制调用的次数和频率。
比如银行的密码错误 3 次,直接冻结 24H 之类的。
限流
限流功能,建议统一做在网关这一层,没有必要每个业务应用都去实现。
网关和限流的框架以前写过,感兴趣的话以后可以重点讲一下。
获取 IP
本来被攻击也是家常便饭,时间一久,也就淡忘了。
不过同事最近接了一个需求,其中涉及到获取 HTTP 请求客户端的真实 IP。
机智的小伙伴们,能说出你平时获取 IP 的方法吗?百度也行。
复制黏贴
同事接到这个需求,感觉也不难。
巧的是以前应用里就有获取 IP 的代码,更巧的查了一下,发现获取的不对。
于是就去百度了一下,复制黏贴,三下五除二上线了。
比如:
public static String getIp(final HttpServletRequest req) {
String ip = req.getHeader("X-Forwarded-For");
if (StringUtil.isEmpty(ip)) {
ip = req.getHeader("Proxy-Client-IP");
}
if (StringUtil.isEmpty(ip)) {
ip = req.getHeader("WL-Proxy-Client-IP");
}
if (StringUtil.isEmpty(ip)) {
ip = req.getRemoteAddr();
}
return ip;
}
稍微靠谱点的:
public class IPUtils {
public static String getClientAddress(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
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("X-Forwarded-For");
}
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.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
}
}
第二天发现,获取的 IP 地址还是不对,白忙活一场,还要重新上线。
应该如何获取 IP 呢?
其实获取客户端的真实 IP,首先和我们的应用架构是紧密相关的。
我们先看一下网上最常见的解释:
网络架构主要分为两种情况:
(1)客户端未经过代理,直接访问服务器端(nginx,squid,haproxy);
(2)客户端通过多级代理,最终到达服务器端(nginx,squid,haproxy);

客户端请求信息都包含在HttpServletRequest中,可以通过方法 getRemoteAddr() 获得该客户端IP。
方式一形式,可以直接获得该客户端真实IP。
方式二中通过代理的形式,此时经过多级反向的代理,通过方法 getRemoteAddr() 得不到客户端真实IP,可以通过 x-forwarded-for 等获得转发后请求信息。
当客户端请求被转发,IP将会追加在其后并以逗号隔开,例如:10.47.103.13,4.2.2.2,10.96.112.230。
术业有专攻
吸取教训
这次同事吸取了上一次的教育,去和安全部门请教了一波。
得到的答案是:
真实客户端 IP 的建议获取方式如下:
X-Client-IP ,X-Real-IP, X-Real-Ip, WL-Proxy-Client-IP,PROXY_CLIENT_IP, X_Forwarded_For, request.getRemoteAddr()
属性
这些都是个啥?

第一次看感觉还是有点蒙,于是去简单整理,便于以后查阅。
X-Forwarded-For
这是一个 Squid 开发的字段,只有在通过了HTTP代理或者负载均衡服务器时才会添加该项。
格式为 X-Forwarded-For:client1,proxy1,proxy2,一般情况下,第一个ip为客户端真实ip,后面的为经过的代理服务器ip。
现在大部分的代理都会加上这个请求头。
Proxy-Client-IP/WL-Proxy-Client-IP
这个一般是经过 apache http 服务器的请求才会有,用apache http做代理时一般会加上Proxy-Client-IP请求头,而WL-Proxy-Client-IP是他的weblogic插件加上的头。
HTTP_CLIENT_IP
有些代理服务器会加上此请求头
X-Real-IP
nginx代理一般会加上此请求头。
remote_addr
remote_addr 指的是当前直接请求的客户端IP地址,它存在于tcp请求体中,是http协议传输时自动添加的,不受请求头header所控制。
所以,当客户端与服务器间不存在任何代理时,通过remote_addr获取客户端IP地址是最准确的,也是最安全的。
注意
这些请求头都不是http协议里的标准请求头,也就是说这个是各个代理服务器自己规定的表示客户端地址的请求头。
即使请求经过的代理都会按自己的规范附上代理请求头,上面的属性也不能确保获得的一定是客户端ip。
最重要的一点,请求头都是可以伪造的。
说了这么多,感觉这里面水太深了,建议最好还是选择放弃深究,让我们直接上代码。

java 实现
老马把上面的实现做了简单的整理,java 初步实现如下:
import com.github.houbb.heaven.util.lang.StringUtil;
import com.github.houbb.web.core.dto.IpInfo;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
/**
* IP 工具类
*
* @author 老马啸西风
* @since 1.0.0
*/
public final class IpUtil {
private IpUtil(){}
/**
* 获取所有的 IP 信息
* @param request 入参
* @return 结果
* @since 0.0.3
*/
public static IpInfo getAllIpInfo(HttpServletRequest request) {
IpInfo ipInfo = new IpInfo();
ipInfo.setClientIp(request.getHeader("X-Client-IP"));
ipInfo.setRealIP(request.getHeader("X-Real-IP"));
ipInfo.setRealIp(request.getHeader("X-Real-Ip"));
ipInfo.setWlProxyClientIP(request.getHeader("WL-Proxy-Client-IP"));
ipInfo.setProxyClientIp(request.getHeader("PROXY_CLIENT_IP"));
ipInfo.setForwardedFor(request.getHeader("X_Forwarded_For"));
ipInfo.setRemoteAddress(request.getRemoteAddr());
return ipInfo;
}
/**
* 获取 ip 信息
* @param request 请求
* @return 结果
* @since 1.0.0
*/
private String getIp(HttpServletRequest request) {
List<String> keyList = Arrays.asList(
"X-Client-IP",
"X-Real-IP",
"X-Real-Ip",
"WL-Proxy-Client-IP",
"PROXY_CLIENT_IP",
"X_Forwarded_For"
);
for(String key : keyList) {
String ip = request.getHeader(key);
// 是合法的 IP,直接返回
if(StringUtil.isNotEmptyTrim(ip)
&& !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
}
// 结果可能为包含 , 好的多个
return request.getRemoteAddr();
}
/**
* 获取 ip 信息
* @param request 请求
* @return 结果
* @since 1.0.0
*/
private String getSingleIp(HttpServletRequest request) {
String ip = getIp(request);
if(ip.contains(",")) {
return ip.split(",")[0];
}
return ip;
}
}
小结
获取客户端真实 IP 是一个很基础的方法,但是也是很重要的一个方法。
看起来不起眼的一个方法,写的不好,可能整个公司的安全就是一张纸。
网上的资料参差不齐,使用时注意甄别。
包括本篇,毕竟老马对网络安全也是一点不懂。
我是老马,期待与你的下次重逢。

服务器受到网络攻击时,如何获取请求客户端的真实 IP?的更多相关文章
- 多级反向代理下,Java获取请求客户端的真实IP地址多中方法整合
在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...
- Java获取请求客户端的真实IP地址
整理网友的材料,最后有源码,亲测能解决所有java获取IP真实地址的问题 整理的这里: 1.链接1 2.链接2 JSP里,获取客户端的IP地址的方法是: request.getRemoteAddr() ...
- java 获取请求客户端的真实IP地址
转载自:http://leiyongping88.iteye.com/blog/1545930 用request.getRemoteAddr();方法获取的IP地址是:127.0.0.1或192.16 ...
- 通过HttpservletRequest对象获取客户端的真实IP地址
这篇文章主要介绍了Java中使用HttpRequest获取用户真实IP地址,使用本文方法可以避免Apache.Squid.nginx等反向代理软件导致的非真实IP地址,需要的朋友可以参考下 在JSP里 ...
- 通过Request获取客户端的真实IP
我们在做项目的时候经常需要获取客户端的真实ip去进行判断,为此搜索了相关文章,以下这个讲解的比较明白,直接拿来 https://blog.csdn.net/yin_jw/article/details ...
- 获取客户端用户真实ip方法整理(jekyll迁移)
layout: post title: 获取客户端用户真实ip方法整理 date: 2019-08-22 author: xiepl1997 tags: springboot 由请求获取客户端ip地址 ...
- 使用CDN后配置nginx自定义日志获取访问用户的真实IP
问题描述: 新上线了一个项目,架构如下(简单画的理解就好): 问题是:负载前面加上CDN后负载这里无法获取客户的真实访问IP,只能过去到CDN的IP地址: 问题解决: 修改nginx日 ...
- web服务器获取请求客户端真实地址的方法
服务器获取客户端或者网页的请求,获取IP时需要注意,因为一个请求到达服务器之前,一般都会经过一层或者多层代理服务器,比如反向代理服务器将http://192.168.1.10:port/ 的URL反向 ...
- TFS应用层服务器获取F5用户的真实IP地址(高可用性)
当用户数量达到一定级别(例如2千)以上,为保证TFS系统的持续服务,最大程度减少因系统宕机对研发团队的影响,系统管理员一般会考虑应用层和数据库层的高可用性方案. 在应用层的高可用性方案中,目前比较常见 ...
随机推荐
- Ubuntu中Docker的安装与使用
Ubuntu中安装Docker 更新ubuntu的apt源索引 sudo apt-get update 2.安装包允许apt通过HTTPS使用仓库 sudo apt-get install \ apt ...
- 个人博客开发之blog-api 项目全局日志拦截记录
前言 大型完善项目中肯定是需要一个全局日志拦截,记录每次接口访问相关信息,包括: 访问ip,访问设备,请求参数,响应结果,响应时间,开始请求时间,访问接口描述,访问的用户,接口地址,请求类型,便于项目 ...
- final修饰符(7)-缓存实例的不可变类
不可变类的实例状态不可改变,可以很方便地被多个对象所共享,可以考虑缓存这种不可变类的实例
- [001] - JavaSE面试题(一):面向对象
第一期:Java面试 - 100题,梳理各大网站优秀面试题.大家可以跟着我一起来刷刷Java理论知识 [001] - JavaSE面试题(一):面向对象 第1问:面向对象和面向过程的区别? 面向过程 ...
- Java基础之反射总结
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...
- 10、Java——内部类
1.类中定义类 (1)当一类中的成员,作为另外一种事物的时候,这个成员就可以定义为内部类. (2)分类:①成员内部类 ②静态内部类 ③私有内部类 ④局部内部类 ⑤匿名内部类 ⑥Lambda表达式 ...
- Python urllib翻译笔记一
22.5.urllib- URL处理模块urllib 是一个收集几个模块以处理URL的包: urllib.request 用于打开和阅读URL urllib.error 包含由urllib.reque ...
- 光学动作捕捉系统中的反光标识点(Marker点)
动作捕捉系统本质上是一种定位系统,通常需要在目标物布置定位设备进行追踪.以红外光学为原理的动作捕捉系统,主要由由光学镜头.动作捕捉软件.反光标识点.POE交换机.和若干配件组成,其中反光标识点(Mar ...
- UnitTest 用法
功能 1.能组织多个用例去执行 2.提供丰富的断言方法 3.提供丰富的日志与测试结果 核心要素 1.TestCase 2.TestSuite 3.TextTestRunner 4.Fixture 用法 ...
- sql select 1 和 exists
SELECT * FROM LACOMMISION WHERE MANAGECOM LIKE'8694%' AND STATE='1' AND EXISTS(SELECT 1 FROM laagent ...