网络攻击

前不久公司遭受了一次网络攻击。

早晨刚到公司,就发现登录接口的调用次数飙升,很快就确认是被恶意攻击,让安全部门做网关入口针对对方 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?的更多相关文章

  1. 多级反向代理下,Java获取请求客户端的真实IP地址多中方法整合

    在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...

  2. Java获取请求客户端的真实IP地址

    整理网友的材料,最后有源码,亲测能解决所有java获取IP真实地址的问题 整理的这里: 1.链接1 2.链接2 JSP里,获取客户端的IP地址的方法是: request.getRemoteAddr() ...

  3. java 获取请求客户端的真实IP地址

    转载自:http://leiyongping88.iteye.com/blog/1545930 用request.getRemoteAddr();方法获取的IP地址是:127.0.0.1或192.16 ...

  4. 通过HttpservletRequest对象获取客户端的真实IP地址

    这篇文章主要介绍了Java中使用HttpRequest获取用户真实IP地址,使用本文方法可以避免Apache.Squid.nginx等反向代理软件导致的非真实IP地址,需要的朋友可以参考下 在JSP里 ...

  5. 通过Request获取客户端的真实IP

    我们在做项目的时候经常需要获取客户端的真实ip去进行判断,为此搜索了相关文章,以下这个讲解的比较明白,直接拿来 https://blog.csdn.net/yin_jw/article/details ...

  6. 获取客户端用户真实ip方法整理(jekyll迁移)

    layout: post title: 获取客户端用户真实ip方法整理 date: 2019-08-22 author: xiepl1997 tags: springboot 由请求获取客户端ip地址 ...

  7. 使用CDN后配置nginx自定义日志获取访问用户的真实IP

    问题描述:         新上线了一个项目,架构如下(简单画的理解就好): 问题是:负载前面加上CDN后负载这里无法获取客户的真实访问IP,只能过去到CDN的IP地址: 问题解决: 修改nginx日 ...

  8. web服务器获取请求客户端真实地址的方法

    服务器获取客户端或者网页的请求,获取IP时需要注意,因为一个请求到达服务器之前,一般都会经过一层或者多层代理服务器,比如反向代理服务器将http://192.168.1.10:port/ 的URL反向 ...

  9. TFS应用层服务器获取F5用户的真实IP地址(高可用性)

    当用户数量达到一定级别(例如2千)以上,为保证TFS系统的持续服务,最大程度减少因系统宕机对研发团队的影响,系统管理员一般会考虑应用层和数据库层的高可用性方案. 在应用层的高可用性方案中,目前比较常见 ...

随机推荐

  1. U149791 正多边形变换

    原博客网页--洛谷博客 题目地址 如果您对群论有所了解,那么本题就是对二面体群 \(D_{2n}\) 的简单实现,您可以直接跳到代码部分.下面的解题思路只是对二面体群 \(D_{2n}\) 的构造思路 ...

  2. C++11运算符重载详解与向量类重载实例(<<,>>,+,-,*等)

    1. C++运算符重载介绍 C ++ 中预定义的运算符的操作对象只能是基本数据类型.但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作.这时就必须在C ++ 中重新定义这些运算符,赋予已 ...

  3. 6.Java方法

    一.什么是方法 方法:要去做某件事情,而采取的一些解决办法( Java 中的方法是完成某些事情的) System.out.println(); //类(系统类) 对象(标准输出对象) 方法(print ...

  4. Linux 之 usermod

    usermod [选项] 登录名 usermod用于修改用户基本信息 -d 修改用户的主目录,与-m选项一起使用 -d和-m要联合使用,否则修改的用户有问题 -g,--gid 修改用户组,该用户组是必 ...

  5. python 读写sql2008 类

    import pymssql class MSSQL: def __init__(self,host,user,pwd,db): self.host = host self.user = user s ...

  6. 【LeetCode】389.找不同

    389.找不同 知识点:哈希表.抵消思想: 题目描述 给定两个字符串 s 和 t,它们只包含小写字母. 字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母. 请找出在 t 中被添加的字母. ...

  7. Java 获取Word中指定图片的坐标位置

    本文介绍通过Java程序获取Word文档中指定图片的坐标位置. 程序运行环境: Word测试文档:.docx 2013 Free Spire.doc.jar 3.9.0 IntelliJ IDEA J ...

  8. CentOS下 Django部署 nginx+uWSGI+Django(二)

    该篇内容承接CentOS下 Django部署 uWSGI+Django(一),细节流程可参考此篇内容. 1. 当前系统 CentOS Linux release 7.6.1810 Python 2.7 ...

  9. 足不出户,一探古今,打造线上3D数字博物馆!

    随着3D技术的不断革新,为了让更多的用户领略历史之美,越来越多的博物馆开始举办线上展览.通过模拟不同的环境.灯光投影.360°无死角放大缩小展品,观众可以享受到身临其境的沉浸体验.不仅如此,给展品加上 ...

  10. Python输出格式化

    参考链接:https://m.jb51.net/article/33631.htm 要求:以固定长度在中间输出某字符串,剩余部分用其他符号补齐.如:"Hello World"  - ...