Spring Cloud Gateway防重放攻击实现
在上面的代码中,在验证请求的Nonce是否合法时,会先判断Nonce是否已经在usedNonceSet中出现过,如果出现过,则认为该请求是重放攻击,返回错误响应。需要注意的是,这个TIMESTAMP_VALID_TIME是需要根据具体场景和需求来确定的,如果设置过大,则会增加重放攻击的风险,如果设置过小,则会增加误拦截的风险。这个usedNonceSet变量应该是在类的变量里定义并初始化的,并且需
一、实现防重放攻击的方案
-
使用Nonce和Timestamp: 在请求中加入一个随机数(Nonce)和当前时间戳,服务端收到请求后验证该请求的Nonce是否已经使用过并且请求的时间戳是否在合理时间范围内。
-
使用Token: 在服务端生成一个唯一的Token,并在响应中返回给客户端,客户端在下一次请求中需要带上该Token,服务端收到请求后验证该Token是否已经使用过。
-
使用Redis 或者 JWT: 将每次请求的信息存储在Redis中或者JWT中,服务端收到请求后验证该请求是否重复。
二、使用Nonce和Timestamp进行防重放攻击
具体实现方式如下:
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author Clang
* @version 1.0
* @date 2023/1/17 14:01
*/
@Component
@Slf4j
public class ReplayAttackFilter implements GlobalFilter, Ordered {
private static final long TIMESTAMP_VALID_TIME = 5 * 60 * 1000;
private final Set<String> usedNonceSet = Collections.synchronizedSet(new HashSet<>());
/**
* 每分钟执行一次
*/
@Scheduled(cron = "0 * * * * *")
public void clearUsedNonceSet() {
usedNonceSet.clear();
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 从请求头中获取Nonce和Timestamp
String nonce = exchange.getRequest().getHeaders().getFirst("nonce");
String timestamp = exchange.getRequest().getHeaders().getFirst("Timestamp");
// 验证Nonce和Timestamp是否合法
if (validateNonceAndTimestamp(nonce, timestamp)) {
// 如果合法,则放行请求
return chain.filter(exchange);
} else {
// 如果不合法,则返回错误响应
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
}
@Override
public int getOrder() {
// 设置过滤器的优先级
return -1;
}
private boolean validateNonceAndTimestamp(String nonce, String timestamp) {
// 判断Nonce和Timestamp是否为空
if (nonce == null || timestamp == null) {
return false;
}
// 验证Nonce是否已经使用过
if (usedNonceSet.contains(nonce)) {
return false;
} else {
usedNonceSet.add(nonce);
}
// 验证Timestamp是否在合理时间范围内
long timeStampValue;
try {
timeStampValue = Long.parseLong(timestamp);
} catch (NumberFormatException e) {
return false;
}
long currentTime = System.currentTimeMillis();
if (timeStampValue < currentTime - TIMESTAMP_VALID_TIME || timeStampValue > currentTime + TIMESTAMP_VALID_TIME) {
return false;
}
return true;
}
}
usedNonceSet是一个Set类型的变量,用来存储已经使用过的Nonce值。在上面的代码中,在验证请求的Nonce是否合法时,会先判断Nonce是否已经在usedNonceSet中出现过,如果出现过,则认为该请求是重放攻击,返回错误响应。
这个usedNonceSet变量应该是在类的变量里定义并初始化的,并且需要注意的是,这个set要确保线程安全,在不同的线程中能够正确的读写。
private final Set<String> usedNonceSet = Collections.synchronizedSet(new HashSet<>());
这里把usedNonceSet 使用了Collections.synchronizedSet 包装,可以保证线程安全。
需要注意的是,这种方法的限制是有时效性的,在一定时间内重复使用的nonce是不会被拦截的。因此需要定期清空这个set,或者使用其他方式存储已使用过的nonce。
/**
* 每分钟执行一次
*/
@Scheduled(cron = "0 * * * * *")
public void clearUsedNonceSet() {
usedNonceSet.clear();
}
需要注意的是,这些方法都只是简单的清空set,更好的做法是能够记录每个nonce使用的时间,在超过时效性之后删除。
private static final long TIMESTAMP_VALID_TIME = 5 * 60 * 1000;
这里定义了TIMESTAMP_VALID_TIME为5分钟,即请求时间戳的有效时间范围为当前时间前5分钟和当前时间后5分钟。
需要注意的是,这个TIMESTAMP_VALID_TIME是需要根据具体场景和需求来确定的,如果设置过大,则会增加重放攻击的风险,如果设置过小,则会增加误拦截的风险。
以上方法都是定时清空usedNonceSet的方法,可以根据业务需求和资源限制进行选择。
提示:更多内容可以访问Clang’s Blog:https://www.clang.asia
更多推荐
所有评论(0)