一、前言
外部ip对某一个web进行频繁访问,有可能是对web进行攻击,现在提供一种基于网关的ip频繁访问web限制策略,犹如带刀侍卫,审查异常身份人员。如发现异常或者暴力闯关者,即可进行识别管制。
二、基于网关的ip频繁访问web限制策略
1、ServerHttpReques的方式t获取
获取访问端ip地址
/**
* 获取客户端IP地址
*
* @param request
* @return
*/
public String getIpAddress(ServerHttpRequest request) {
String ip = getHeaderValue(request, "x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = getHeaderValue(request, "Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = getHeaderValue(request, "X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = getHeaderValue(request, "WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = getHeaderValue(request, "X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddress().getAddress().getHostAddress();
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getLocalAddress().getAddress().getHostAddress();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
public static String getHeaderValue(ServerHttpRequest request, String name) {
List<String> stringList = request.getHeaders().get(name);
if (!CollectionUtils.isEmpty(stringList)) {
return stringList.get(0);
}
return "";
}
/**
* 判断大规模异常请求是否受限(同一个IP)
*
* @param count 1分钟内限制次数
* @param request
* @return
*/
public boolean isLimit(ServerHttpRequest request, int count) {
String clientIp = getIpAddress(request);
String noPermissIP = redisUtils.get(RedisKeys.getNoPermissClientIP(clientIp));
if (!StringUtils.isEmpty(noPermissIP)) {
String noPermissIPLog = redisUtils.get(RedisKeys.getNoPermissIPLog(clientIp));
if (!StringUtils.isEmpty(noPermissIPLog)) {
log.info("ip:" + clientIp + "访问系统接口频繁,被限制访问1天!");
redisUtils.set(RedisKeys.getNoPermissIPLog(clientIp), "1", RedisUtils.DEFAULT_A_DAY);
}
return false;
}
String stratTime = redisUtils.get(RedisKeys.getRequestIPStartTime(clientIp));
if (StringUtils.isEmpty(stratTime)) {
stratTime = String.valueOf(System.currentTimeMillis());
redisUtils.set(RedisKeys.getRequestIPPermiss(clientIp), "1", RedisUtils.DEFAULT_A_DAY);
redisUtils.set(RedisKeys.getRequestIPStartTime(clientIp), stratTime, RedisUtils.DEFAULT_A_MIN);
}
Long interval = System.currentTimeMillis() - Long.parseLong(stratTime);
String reqCount = redisUtils.get(RedisKeys.getRequestIPPermiss(clientIp));
if (StringUtils.isEmpty(reqCount)) {
redisUtils.set(RedisKeys.getRequestIPPermiss(clientIp), "1", RedisUtils.DEFAULT_A_DAY);
} else {
redisUtils.set(RedisKeys.getRequestIPPermiss(clientIp), (Integer.parseInt(reqCount) + 1) + "",
RedisUtils.DEFAULT_A_DAY);
}
if (interval < 60000) { // 一分钟
if (!StringUtils.isEmpty(reqCount)) {
if (Integer.parseInt(reqCount) > count) {
redisUtils.set(RedisKeys.getNoPermissIP(clientIp), clientIp, RedisUtils.DEFAULT_A_DAY);
log.info(clientIp + "在最近" + (double) (Math.round(interval / 1000) / 100.0) + "秒内访问系统接口累计" + reqCount
+ "次");
log.info("ip:" + clientIp + "访问系统接口频繁,被限制访问1天!");
return false;
}
}
}
return true;
}
2、HttpServletReques的方式
/**
* 获取客户端IP地址
*
* @param request
* @return
*/
public String getIpAddress(HttpServletRequest request) {
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("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
String [] ipArr = ip.split(",");
ip = ipArr[0];
ip.replaceAll("0:0:0:0:0:0:0:1", "127.0.0.1");
return ip;
}
ip频繁访问限制
这里需要借助redis缓存
/**
* 判断大规模异常请求是否受限(同一个IP)
*
* @param request
* @return
*/
public boolean isLimit(HttpServletRequest request) {
String clientIp = getIpAddress(request);
String noPermissIP = redisUtils.get(RedisKeys.getNoPermissIP(clientIp));
if (!StringUtils.isEmpty(noPermissIP)) {
String noPermissIPLog = redisUtils.get(RedisKeys.getNoPermissIPLog(clientIp));
if (!StringUtils.isEmpty(noPermissIPLog)) {
log.info("ip:" + clientIp + "访问系统接口频繁,被限制访问1天!");
redisUtils.set(RedisKeys.getNoPermissIPLog(clientIp), "1", RedisUtils.DEFAULT_A_DAY);
}
return false;
}
String stratTime = redisUtils.get(RedisKeys.getRequestIPStartTime(clientIp));
if (StringUtils.isEmpty(stratTime)) {
stratTime = String.valueOf(System.currentTimeMillis());
redisUtils.set(RedisKeys.getRequestIPPermiss(clientIp), "1", RedisUtils.DEFAULT_A_DAY);
redisUtils.set(RedisKeys.getRequestIPStartTime(clientIp), stratTime, RedisUtils.DEFAULT_A_MIN);
}
Long interval = System.currentTimeMillis() - Long.parseLong(stratTime);
String reqCount = redisUtils.get(RedisKeys.getRequestIPPermiss(clientIp));
if (StringUtils.isEmpty(reqCount)) {
redisUtils.set(RedisKeys.getRequestIPPermiss(clientIp), "1", RedisUtils.DEFAULT_A_DAY);
} else {
redisUtils.set(RedisKeys.getRequestIPPermiss(clientIp), (Integer.parseInt(reqCount) + 1) + "",
RedisUtils.DEFAULT_A_DAY);
}
if (interval < 60000) { // 一分钟
if (!StringUtils.isEmpty(reqCount)) {
if (Integer.parseInt(reqCount) > 2000) {
redisUtils.set(RedisKeys.getNoPermissIP(clientIp), clientIp, RedisUtils.DEFAULT_A_DAY);
log.info(clientIp + "在最近" + (double) (Math.round(interval / 1000) / 100.0) + "秒内访问系统接口累计" + reqCount
+ "次");
log.info("ip:" + clientIp + "访问系统接口频繁,被限制访问1天!");
return false;
}
}
}
return true;
}
三、制作开关
对策略应该增加开关,作为启用策略和关闭策略使用
/**
* 1为开启,其他都是关闭
*/
@Value("${interceptor.isAccLimit:-1}")
private String isAccLimit;
四、对敏感性信息进行识(扩展)
- 定义敏感信息:
- 首先,明确哪些信息被认为是敏感的。这通常包括但不限于:密码、信用卡信息、社会保险号、个人身份信息(如全名、地址、电话号码、电子邮件地址)、医疗记录等。
- 输入验证:
- 使用输入验证来确保用户输入的数据符合预期的格式和类型。这可以防止恶意用户输入可能导致安全漏洞的数据。
- 对于密码,确保它们足够复杂,并符合安全标准(如长度、特殊字符、大小写等)。
- 加密存储:
- 不要以明文形式存储敏感信息。使用加密算法(如AES、RSA等)对敏感信息进行加密,并确保密钥得到妥善保管。
- 对于密码,使用哈希算法(如SHA-256、bcrypt等)进行存储,并加入“盐”(salt)来增加哈希的复杂性。
- 传输安全:
- 使用HTTPS来加密用户与Web服务器之间的通信,确保敏感信息在传输过程中不被窃取或篡改。
- 避免在URL或GET请求中传递敏感信息,因为它们可能会被记录在浏览器历史、服务器日志或代理缓存中。
- 访问控制:
- 实现严格的访问控制策略,确保只有授权的用户和应用程序可以访问敏感信息。
- 使用身份验证和授权机制(如OAuth、JWT等)来验证用户身份并授予相应的权限。
- 日志和监控:
- 记录与敏感信息相关的所有活动,包括访问、修改和删除等。这有助于在发生安全事件时进行调查和追溯。
- 使用日志分析工具来监控潜在的安全威胁,并设置警报以在发现异常活动时进行通知。
- 定期审查:
- 定期对代码和配置进行安全审查,以确保没有意外的敏感信息泄露或安全漏洞。
- 遵循最新的安全标准和最佳实践来更新和加固应用程序。
- 使用安全框架和库:
- 利用现有的安全框架和库(如Spring Security、OWASP等)来简化安全性的实现和维护。
- 这些框架和库通常提供了许多内置的安全功能和防护措施,可以大大降低安全风险。
- 培训和教育:
- 对开发人员和管理员进行安全培训和教育,使他们了解如何识别和处理敏感信息以及如何遵循最佳的安全实践。
- 备份和恢复:
- 定期备份敏感信息,并确保备份数据的安全性。
- 制定灾难恢复计划,以便在发生安全事件或数据丢失时能够迅速恢复敏感信息。