服务器受到网络攻击时,如何获取请求客户端的真实 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系统的持续服务,最大程度减少因系统宕机对研发团队的影响,系统管理员一般会考虑应用层和数据库层的高可用性方案. 在应用层的高可用性方案中,目前比较常见 ...
随机推荐
- 保存TextBox中的文字为Path功能
保存TextBox中的文字为Path功能 今天再设计一个我自己程序的Icon时使用了Path+textbox做了图形,我不想导出为PNG,因为颜色比较单一,我又想通过代码控制颜色,所以我想完整的保存为 ...
- python使用笔记006-函数+json操作
一.函数 函数:提高代码的复用性 1.1 函数的定义 1 def hello(): 2 print('hello') 3 print('fdsfjslkfs') 4 5 #函数不调用就不会执行 6 h ...
- 没事就要多做多练,Shell脚本循环例题做一做
Shell脚本循环例题 一.示例1 二.示例2 三.示例3 四.示例4 ...
- PYTHON IDLE同时运行两个PY,互不影响
1.打开指定文件夹 2.右击a1.py,用IDLE打开(idle1) 3.右击a2.py,用IDLE打开(idle2) 4.则分别按F5运行,则两个互不影响. 5.再用IDLE1打开新程序,用IDLE ...
- 2019版pycharm永久激活
链接:https://pan.baidu.com/s/1vY1KBvi2NHIgoN8C2qaFbg 提取码:p4gx 1.下对应版本的jar包,放到pycharm目录的bin目录下2.去C:\Use ...
- Skywalking-04:扩展Metric监控信息
扩展 Metric 监控信息 官方文档 Source and Scope extension for new metrics 案例:JVM Thread 增加 Metrics 修改 Thread 的定 ...
- 前端构建第1篇之---引入elementUI
张艳涛 写于2021-1-19 HOW:如何引入elementui? 在项目根目录package.json引入,在重新执行npm install "dependencies": { ...
- WIN XP SP2系统经常性死机问题解决历程
如题: 1.初始时,XP还能进入系统,等系统3分钟左右,鼠标熄灭,键盘无反应,查看资源管理器CPU 100%,内存占用不高. 2.现象初步分析: a.怀疑是病毒占用CPU 100%,于是下载360安全 ...
- C++动态内存管理与源码剖析
引言 在本篇文章中,我们主要剖析c++中的动态内存管理,包括malloc.new expression.operator new.array new和allocator内存分配方法以及对应的内存释放方 ...
- Jquery遍历复选框选中项
var ret=''; $('name=chkIds').each(function(){ if($(this).is(':checked')){ ret+=$(this).val()+','; } ...