SYN Flood 是一种常见的 DDoS 攻击方式,通过发送大量伪造的 TCP 连接请求耗尽服务器资源。本文将深入探讨其原理,并提供 Java 应用层面的防御方案。
SYN Flood 攻击原理
TCP 三次握手过程中,攻击者只发送 SYN 包但不完成后续握手,导致服务器维护大量半开连接,最终资源耗尽无法响应正常请求。

SYN Cookie 原理
SYN Cookie 是一种无状态的防御机制,服务器不保存半开连接信息,而是将信息编码在 SYN-ACK 的序列号中:
- 收到 SYN 时,计算一个特殊的序列号(cookie)
- 将连接信息编码到这个序列号中
- 收到 ACK 时,从序列号中解码出原始信息
- 验证通过后才建立连接
这样可以完全避免半开连接队列被耗尽的问题。
Java 应用防御策略
1. 系统优化器
java
import java.io.*;
import java.util.logging.*;
public class SystemOptimizer {
private static final Logger logger = Logger.getLogger(SystemOptimizer.class.getName());
public static void printOptimizationGuide() {
logger.info("=== Linux系统优化建议 ===");
logger.info("1. 增加半连接队列大小:");
logger.info(" echo 2048 > /proc/sys/net/ipv4/tcp_max_syn_backlog");
logger.info("2. 减少SYN-ACK重试次数:");
logger.info(" echo 2 > /proc/sys/net/ipv4/tcp_synack_retries");
logger.info("3. 启用SYN Cookie:");
logger.info(" echo 1 > /proc/sys/net/ipv4/tcp_syncookies");
logger.info("4. 减少TIME_WAIT状态时间:");
logger.info(" echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout");
logger.info("5. 增加最大文件描述符:");
logger.info(" ulimit -n 65535");
}
public static void checkSystemConfig() {
if (!System.getProperty("os.name").toLowerCase().contains("linux")) {
logger.info("当前系统非Linux,跳过系统配置检查");
return;
}
logger.info("=== 系统配置检查 ===");
checkSysctl("net.ipv4.tcp_syncookies", "1");
checkSysctl("net.ipv4.tcp_max_syn_backlog", "2048");
checkSysctl("net.core.somaxconn", "65535");
checkSysctl("net.ipv4.tcp_fin_timeout", "30");
checkSysctl("net.ipv4.tcp_synack_retries", "2");
checkUlimit();
}
private static void checkSysctl(String key, String expectedValue) {
try {
Process p = Runtime.getRuntime().exec(new String[]{"sysctl", key});
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()))) {
String line = reader.readLine();
if (line != null) {
String[] parts = line.split("=");
if (parts.length == 2) {
String actualValue = parts[1].trim();
String status = actualValue.equals(expectedValue) ? "✓" : "✗";
logger.info(String.format("%s %s = %s (建议值: %s)",
status, key, actualValue, expectedValue));
if (!actualValue.equals(expectedValue)) {
logger.warning(String.format(
"%s 当前值 %s 不符合建议值 %s",
key, actualValue, expectedValue
));
}
}
}
}
p.waitFor();
} catch (IOException | InterruptedException e) {
logger.fine("无法读取系统配置 " + key + ": " + e.getMessage());
}
}
private static void checkUlimit() {
try {
Process p = Runtime.getRuntime().exec(new String[]{"bash", "-c", "ulimit -n"});
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()))) {
String line = reader.readLine();
if (line != null) {
int limit = Integer.parseInt(line.trim());
String status = limit >= 65535 ? "✓" : "✗";
logger.info(String.format("%s 文件描述符限制: %d (建议值: ≥65535)",
status, limit));
if (limit < 65535) {
logger.warning("文件描述符限制过低: " + limit);
}
}
}
p.waitFor();
} catch (Exception e) {
logger.fine("无法检查文件描述符限制: " + e.getMessage());
}
}
}
2. 配置管理中心
java
import java.util.*;
import java.io.*;
import java.util.logging.*;
public class DefenseConfig {
private static final Logger logger = Logger.getLogger(DefenseConfig.class.getName());
private static final Properties config = new Properties();
private static final String CONFIG_FILE = "/defense.properties";
static {
loadConfig();
}
public static void loadConfig() {
config.clear();
loadConfigFromFile();
loadConfigFromEnv();
}
private static void loadConfigFromFile() {
try (InputStream is = DefenseConfig.class.getResourceAsStream(CONFIG_FILE)) {
if (is != null) {
config.load(is);
logger.info("配置文件加载成功");
}
} catch (IOException e) {
logger.warning("配置文件加载失败,使用默认值: " + e.getMessage());
}
}
private static void loadConfigFromEnv() {
Map<String, String> env = System.getenv();
env.forEach((key, value) -> {
if (key.startsWith("DEFENSE_")) {
String propKey = key.substring(8).toLowerCase().replace('_', '.');
config.setProperty(propKey, value);
}
});
}
public static int getMaxConnections() {
return Integer.parseInt(config.getProperty("max.connections", "1000"));
}
public static int getMaxConnectionsPerIp() {
return Integer.parseInt(config.getProperty("max.connections.per.ip", "10"));
}
public static int getRateLimitCapacity() {
return Integer.parseInt(config.getProperty("rate.limit.capacity", "100"));
}
public static double getRateLimitRefillRate() {
return Double.parseDouble(config.getProperty("rate.limit.refill.rate", "50"));
}
public static int getSuspiciousThreshold() {
return Integer.parseInt(config.getProperty("suspicious.threshold", "5"));
}
public static int getServerPort() {
return Integer.parseInt(config.getProperty("server.port", "8080"));
}
public static int getAdminPort() {
return Integer.parseInt(config.getProperty("admin.port", "8081"));
}
public static int getSocketTimeout() {
return Integer.parseInt(config.getProperty("socket.timeout", "30000"));
}
// 配置验证
public static void validate() {
int serverPort = getServerPort();
if (serverPort < 1024 || serverPort > 65535) {
throw new IllegalArgumentException("服务端口必须在1024-65535之间");
}
int maxConnections = getMaxConnections();
int maxPerIp = getMaxConnectionsPerIp();
if (maxPerIp > maxConnections) {
throw new IllegalArgumentException("单IP最大连接数不能超过总连接数");
}
double refillRate = getRateLimitRefillRate();
if (refillRate <= 0) {
throw new IllegalArgumentException("令牌补充速率必须大于0");
}
}
}
3. 连接数限制器
java
import java.util.concurrent.*;
import java.net.*;
import java.io.*;
import java.util.logging.*;
public class ConnectionLimiter {
private static final Logger logger = Logger.getLogger(ConnectionLimiter.class.getName());
private final Semaphore connectionSemaphore;
private final int maxConnections;
private final ConcurrentHashMap<String, AtomicInteger> ipConnectionCount;
private final int maxConnectionsPerIp;
private final ScheduledExecutorService cleanupExecutor;
public ConnectionLimiter(int maxConnections, int maxConnectionsPerIp) {
this.maxConnections = maxConnections;
this.maxConnectionsPerIp = maxConnectionsPerIp;
this.connectionSemaphore = new Semaphore(maxConnections);
this.ipConnectionCount = new ConcurrentHashMap<>();
this.cleanupExecutor = Executors.newScheduledThreadPool(1);
// 定期清理无连接的IP记录
cleanupExecutor.scheduleAtFixedRate(this::cleanup, 1, 1, TimeUnit.MINUTES);
}
public boolean acceptConnection(Socket socket) throws IOException {
String clientIp = socket.getInetAddress().getHostAddress();
// 检查单个IP的连接数
AtomicInteger count = ipConnectionCount.computeIfAbsent(
clientIp, k -> new AtomicInteger(0)
);
if (count.get() >= maxConnectionsPerIp) {
logger.warning("IP " + clientIp + " 超过最大连接数限制");
socket.close();
return false;
}
// 检查总连接数
if (!connectionSemaphore.tryAcquire()) {
logger.warning("全局连接数已达上限");
socket.close();
return false;
}
count.incrementAndGet();
return true;
}
public void releaseConnection(Socket socket) {
String clientIp = socket.getInetAddress().getHostAddress();
connectionSemaphore.release();
ipConnectionCount.computeIfPresent(clientIp, (k, v) -> {
int newCount = v.decrementAndGet();
return newCount > 0 ? v : null;
});
}
public int getCurrentConnections() {
return maxConnections - connectionSemaphore.availablePermits();
}
private void cleanup() {
ipConnectionCount.entrySet().removeIf(entry ->
entry.getValue().get() == 0
);
}
public void shutdown() {
cleanupExecutor.shutdown();
}
}
4. 分布式限流器
java
import redis.clients.jedis.*;
import java.util.concurrent.*;
import java.util.logging.*;
public class DistributedRateLimiter {
private static final Logger logger = Logger.getLogger(DistributedRateLimiter.class.getName());
private final JedisPool jedisPool;
private final int maxRequestsPerSecond;
private final int maxRequestsPerIpPerSecond;
public DistributedRateLimiter(String redisHost, int redisPort,
int maxRequestsPerSecond,
int maxRequestsPerIpPerSecond) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(50);
config.setMinIdle(10);
config.setTestOnBorrow(true);
config.setTestWhileIdle(true);
config.setTimeBetweenEvictionRunsMillis(30000);
config.setMinEvictableIdleTimeMillis(60000);
this.jedisPool = new JedisPool(config, redisHost, redisPort, 2000, 2000);
this.maxRequestsPerSecond = maxRequestsPerSecond;
this.maxRequestsPerIpPerSecond = maxRequestsPerIpPerSecond;
}
public boolean tryAcquire(String clientIp) {
try (Jedis jedis = jedisPool.getResource()) {
// 检查全局限流
String globalKey = "rate_limit:global";
if (!checkRateLimit(jedis, globalKey, maxRequestsPerSecond)) {
return false;
}
// 检查IP级别限流
String ipKey = "rate_limit:ip:" + clientIp;
return checkRateLimit(jedis, ipKey, maxRequestsPerIpPerSecond);
}
}
private boolean checkRateLimit(Jedis jedis, String key, int maxRequests) {
long currentTime = System.currentTimeMillis();
long windowStart = currentTime - 1000;
Transaction tx = jedis.multi();
// 清理过期记录
tx.zremrangeByScore(key, 0, windowStart);
// 添加当前请求
tx.zadd(key, currentTime, String.valueOf(currentTime));
// 设置过期时间
tx.expire(key, 2);
// 获取窗口内的请求数
Response<Long> count = tx.zcard(key);
tx.exec();
return count.get() <= maxRequests;
}
public void shutdown() {
jedisPool.close();
}
}
// 混合限流器:Redis挂了自动降级到本地
public class HybridRateLimiter {
private static final Logger logger = Logger.getLogger(HybridRateLimiter.class.getName());
private final DistributedRateLimiter distributedLimiter;
private final RateLimiter localLimiter;
private volatile boolean useDistributed = true;
public HybridRateLimiter(DistributedRateLimiter distributedLimiter,
RateLimiter localLimiter) {
this.distributedLimiter = distributedLimiter;
this.localLimiter = localLimiter;
}
public boolean tryAcquire(String clientIp) {
if (useDistributed) {
try {
return distributedLimiter.tryAcquire(clientIp);
} catch (Exception e) {
logger.warning("Redis不可用,降级到本地限流: " + e.getMessage());
useDistributed = false;
scheduleRedisCheck();
}
}
return localLimiter.tryAcquire(clientIp);
}
private void scheduleRedisCheck() {
CompletableFuture.delayedExecutor(30, TimeUnit.SECONDS)
.execute(() -> {
useDistributed = true;
logger.info("尝试恢复Redis限流");
});
}
}
5. 本地限流器
java
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
public class RateLimiter {
private static final Logger logger = Logger.getLogger(RateLimiter.class.getName());
private final long capacity;
private final double refillRate;
private final AtomicLong tokens;
private final AtomicLong lastRefillTime;
private final ConcurrentHashMap<String, ClientRateLimit> clientLimits;
public RateLimiter(long capacity, double refillRatePerSecond) {
this.capacity = capacity;
this.refillRate = refillRatePerSecond;
this.tokens = new AtomicLong(capacity);
this.lastRefillTime = new AtomicLong(System.nanoTime());
this.clientLimits = new ConcurrentHashMap<>();
}
public boolean tryAcquire(String clientIp) {
// 每个IP单独限流
ClientRateLimit clientLimit = clientLimits.computeIfAbsent(
clientIp, k -> new ClientRateLimit(10, 2)
);
if (!clientLimit.tryAcquire()) {
return false;
}
// 全局限流
refillTokens();
return tokens.getAndUpdate(current ->
current > 0 ? current - 1 : current
) > 0;
}
private void refillTokens() {
long now = System.nanoTime();
long lastRefill = lastRefillTime.get();
long elapsedNanos = now - lastRefill;
if (elapsedNanos > TimeUnit.MILLISECONDS.toNanos(100) &&
lastRefillTime.compareAndSet(lastRefill, now)) {
double elapsedSeconds = elapsedNanos / 1_000_000_000.0;
long tokensToAdd = (long)(elapsedSeconds * refillRate);
if (tokensToAdd > 0) {
tokens.getAndUpdate(current ->
Math.min(capacity, current + tokensToAdd)
);
}
}
}
static class ClientRateLimit {
private final long capacity;
private final double refillRate;
private final AtomicLong tokens;
private final AtomicLong lastRefillTime;
ClientRateLimit(long capacity, double refillRatePerSecond) {
this.capacity = capacity;
this.refillRate = refillRatePerSecond;
this.tokens = new AtomicLong(capacity);
this.lastRefillTime = new AtomicLong(System.nanoTime());
}
boolean tryAcquire() {
refillTokens();
return tokens.getAndUpdate(current ->
current > 0 ? current - 1 : current
) > 0;
}
private void refillTokens() {
long now = System.nanoTime();
long lastRefill = lastRefillTime.get();
long elapsedNanos = now - lastRefill;
if (elapsedNanos > TimeUnit.MILLISECONDS.toNanos(100) &&
lastRefillTime.compareAndSet(lastRefill, now)) {
double elapsedSeconds = elapsedNanos / 1_000_000_000.0;
long tokensToAdd = (long)(elapsedSeconds * refillRate);
if (tokensToAdd > 0) {
tokens.getAndUpdate(current ->
Math.min(capacity, current + tokensToAdd)
);
}
}
}
}
}
6. IP 黑名单管理
java
import java.util.*;
import java.util.concurrent.*;
import java.time.*;
import java.util.logging.*;
public class IPBlacklist {
private static final Logger logger = Logger.getLogger(IPBlacklist.class.getName());
private final Set<String> permanentBlacklist = ConcurrentHashMap.newKeySet();
private final ConcurrentHashMap<String, Long> temporaryBlacklist = new ConcurrentHashMap<>();
private final ScheduledExecutorService cleanupExecutor;
public IPBlacklist() {
this.cleanupExecutor = Executors.newScheduledThreadPool(1);
cleanupExecutor.scheduleAtFixedRate(this::cleanupExpired, 1, 1, TimeUnit.MINUTES);
}
public void addToBlacklist(String ip, Duration duration) {
if (duration == null) {
permanentBlacklist.add(ip);
logger.warning("IP永久加入黑名单: " + ip);
} else {
temporaryBlacklist.put(ip, System.currentTimeMillis() + duration.toMillis());
logger.warning("IP临时加入黑名单: " + ip + ", 时长: " + duration);
}
}
public boolean isBlacklisted(String ip) {
if (permanentBlacklist.contains(ip)) {
return true;
}
Long expiry = temporaryBlacklist.get(ip);
if (expiry != null) {
if (System.currentTimeMillis() < expiry) {
return true;
} else {
temporaryBlacklist.remove(ip);
}
}
return false;
}
private void cleanupExpired() {
long now = System.currentTimeMillis();
temporaryBlacklist.entrySet().removeIf(entry -> entry.getValue() < now);
}
public void removeFromBlacklist(String ip) {
permanentBlacklist.remove(ip);
temporaryBlacklist.remove(ip);
logger.info("IP从黑名单移除: " + ip);
}
public Set<String> getBlacklistedIPs() {
Set<String> result = new HashSet<>(permanentBlacklist);
result.addAll(temporaryBlacklist.keySet());
return result;
}
public void shutdown() {
cleanupExecutor.shutdown();
}
}
7. 异常行为检测器
java
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.time.*;
import java.util.logging.*;
public class AnomalyDetector {
private static final Logger logger = Logger.getLogger(AnomalyDetector.class.getName());
private final ConcurrentHashMap<String, ConnectionMetrics> metricsMap;
private final ScheduledExecutorService scheduler;
private final int suspiciousThreshold;
private final IPBlacklist blacklist;
public AnomalyDetector(int suspiciousThreshold, IPBlacklist blacklist) {
this.metricsMap = new ConcurrentHashMap<>();
this.suspiciousThreshold = suspiciousThreshold;
this.blacklist = blacklist;
this.scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(this::cleanup, 1, 1, TimeUnit.MINUTES);
}
public boolean isSuspicious(String clientIp) {
if (blacklist.isBlacklisted(clientIp)) {
return true;
}
ConnectionMetrics metrics = metricsMap.computeIfAbsent(
clientIp, k -> new ConnectionMetrics()
);
metrics.recordConnection();
// 判断是否可疑
boolean suspicious = metrics.getIncompleteConnections() > suspiciousThreshold ||
metrics.getConnectionRate() > 10 ||
metrics.getIOExceptionRate() > 0.5;
if (suspicious) {
logger.warning(String.format("检测到可疑IP: %s, 未完成连接: %d, 连接速率: %.2f/s",
clientIp, metrics.getIncompleteConnections(), metrics.getConnectionRate()));
// 自动拉黑30分钟
blacklist.addToBlacklist(clientIp, Duration.ofMinutes(30));
}
return suspicious;
}
public void recordHandshakeComplete(String clientIp) {
ConnectionMetrics metrics = metricsMap.get(clientIp);
if (metrics != null) {
metrics.recordHandshakeComplete();
}
}
public void recordIOException(String clientIp) {
ConnectionMetrics metrics = metricsMap.get(clientIp);
if (metrics != null) {
metrics.recordIOException();
}
}
private void cleanup() {
Instant cutoff = Instant.now().minus(Duration.ofMinutes(5));
metricsMap.entrySet().removeIf(entry ->
entry.getValue().getLastActivity().isBefore(cutoff)
);
}
static class ConnectionMetrics {
private final AtomicInteger totalConnections = new AtomicInteger(0);
private final AtomicInteger completedHandshakes = new AtomicInteger(0);
private final AtomicInteger ioExceptions = new AtomicInteger(0);
private final AtomicLong lastActivityTime = new AtomicLong(System.currentTimeMillis());
private final AtomicLong firstConnectionTime = new AtomicLong(0);
void recordConnection() {
totalConnections.incrementAndGet();
lastActivityTime.set(System.currentTimeMillis());
firstConnectionTime.compareAndSet(0, System.currentTimeMillis());
}
void recordHandshakeComplete() {
completedHandshakes.incrementAndGet();
}
void recordIOException() {
ioExceptions.incrementAndGet();
}
int getIncompleteConnections() {
return totalConnections.get() - completedHandshakes.get();
}
double getConnectionRate() {
long duration = System.currentTimeMillis() - firstConnectionTime.get();
if (duration == 0) return 0;
return (double) totalConnections.get() / (duration / 1000.0);
}
double getIOExceptionRate() {
int total = totalConnections.get();
if (total == 0) return 0;
return (double) ioExceptions.get() / total;
}
Instant getLastActivity() {
return Instant.ofEpochMilli(lastActivityTime.get());
}
}
public void shutdown() {
scheduler.shutdown();
}
}
8. 高性能 NIO 服务器
java
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.time.Duration;
public class NIOSecureServer {
private static final Logger logger = Logger.getLogger(NIOSecureServer.class.getName());
private final int port;
private final Selector selector;
private final ServerSocketChannel serverChannel;
private final ConnectionLimiter connectionLimiter;
private final HybridRateLimiter rateLimiter;
private final IPBlacklist blacklist;
private final AnomalyDetector anomalyDetector;
private final ExecutorService workerPool;
private final MetricsCollector metricsCollector;
private volatile boolean running = true;
public NIOSecureServer(int port, MetricsCollector metricsCollector) throws IOException {
this.port = port;
this.metricsCollector = metricsCollector;
this.selector = Selector.open();
this.serverChannel = ServerSocketChannel.open();
this.serverChannel.bind(new InetSocketAddress(port));
this.serverChannel.configureBlocking(false);
this.serverChannel.register(selector, SelectionKey.OP_ACCEPT);
this.connectionLimiter = new ConnectionLimiter(
DefenseConfig.getMaxConnections(),
DefenseConfig.getMaxConnectionsPerIp()
);
// 初始化混合限流器
DistributedRateLimiter distributedLimiter = new DistributedRateLimiter(
"localhost", 6379,
DefenseConfig.getRateLimitCapacity(),
DefenseConfig.getMaxConnectionsPerIp()
);
RateLimiter localLimiter = new RateLimiter(
DefenseConfig.getRateLimitCapacity(),
DefenseConfig.getRateLimitRefillRate()
);
this.rateLimiter = new HybridRateLimiter(distributedLimiter, localLimiter);
this.blacklist = new IPBlacklist();
this.anomalyDetector = new AnomalyDetector(
DefenseConfig.getSuspiciousThreshold(), blacklist);
this.workerPool = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors() * 2
);
}
public void start() throws IOException {
logger.info("NIO服务器启动,端口:" + port);
while (running) {
selector.select(1000);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
try {
if (key.isAcceptable()) {
handleAccept(key);
} else if (key.isReadable()) {
handleRead(key);
}
} catch (Exception e) {
logger.severe("处理连接异常: " + e.getMessage());
key.cancel();
closeChannel(key.channel());
}
}
}
}
private void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
if (clientChannel == null) return;
String clientIp = ((InetSocketAddress) clientChannel.getRemoteAddress())
.getAddress().getHostAddress();
metricsCollector.recordConnectionAttempt(clientIp);
if (!performDefenseChecks(clientIp, clientChannel)) {
return;
}
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ,
new ClientContext(clientIp));
}
private boolean performDefenseChecks(String clientIp, SocketChannel channel)
throws IOException {
// 黑名单检查
if (blacklist.isBlacklisted(clientIp)) {
metricsCollector.recordConnectionRejection("blacklist", clientIp);
closeChannel(channel);
return false;
}
// 异常检测
if (anomalyDetector.isSuspicious(clientIp)) {
metricsCollector.recordConnectionRejection("suspicious", clientIp);
metricsCollector.recordAttackDetection("syn_flood", clientIp);
closeChannel(channel);
return false;
}
// 速率限制
if (!rateLimiter.tryAcquire(clientIp)) {
metricsCollector.recordConnectionRejection("rate_limit", clientIp);
closeChannel(channel);
return false;
}
// 连接数限制
Socket socket = channel.socket();
if (!connectionLimiter.acceptConnection(socket)) {
metricsCollector.recordConnectionRejection("connection_limit", clientIp);
return false;
}
return true;
}
private void handleRead(SelectionKey key) {
SocketChannel channel = (SocketChannel) key.channel();
ClientContext context = (ClientContext) key.attachment();
workerPool.submit(() -> {
long startTime = System.nanoTime();
try {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
processRequest(channel, buffer, context);
metricsCollector.recordRequestDuration(startTime, "process");
} else if (bytesRead < 0) {
closeConnection(key, channel);
}
} catch (IOException e) {
anomalyDetector.recordIOException(context.clientIp);
closeConnection(key, channel);
}
});
}
private void processRequest(SocketChannel channel, ByteBuffer buffer,
ClientContext context) throws IOException {
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String request = new String(data);
// 验证请求合法性
SecurityEnhancements.InputValidator.ValidationResult validation =
SecurityEnhancements.InputValidator.validate(request);
if (!validation.valid) {
logger.warning("检测到恶意请求,IP: " + context.clientIp + ", 原因: " + validation.reason);
blacklist.addToBlacklist(context.clientIp, Duration.ofHours(1));
closeChannel(channel);
return;
}
// 记录握手完成
anomalyDetector.recordHandshakeComplete(context.clientIp);
// 简单回显
String response = "Echo: " + request;
ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
channel.write(responseBuffer);
}
private void closeConnection(SelectionKey key, SocketChannel channel) {
key.cancel();
ClientContext context = (ClientContext) key.attachment();
if (context != null && channel.socket() != null) {
connectionLimiter.releaseConnection(channel.socket());
}
closeChannel(channel);
}
private void closeChannel(Channel channel) {
try {
if (channel != null && channel.isOpen()) {
channel.close();
}
} catch (IOException e) {
// 忽略关闭异常
}
}
public ConnectionLimiter getConnectionLimiter() {
return connectionLimiter;
}
public void stop() {
running = false;
workerPool.shutdown();
connectionLimiter.shutdown();
blacklist.shutdown();
anomalyDetector.shutdown();
try {
selector.close();
serverChannel.close();
} catch (IOException e) {
logger.severe("关闭服务器异常: " + e.getMessage());
}
}
static class ClientContext {
final String clientIp;
final long connectTime;
ClientContext(String clientIp) {
this.clientIp = clientIp;
this.connectTime = System.currentTimeMillis();
}
}
}
9. 安全增强模块
java
import javax.net.ssl.*;
import java.security.*;
import java.security.cert.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.Base64;
import java.util.regex.Pattern;
import java.io.FileInputStream;
import java.util.logging.*;
public class SecurityEnhancements {
private static final Logger logger = Logger.getLogger(SecurityEnhancements.class.getName());
private static final String HMAC_ALGORITHM = "HmacSHA256";
private final SecretKey hmacKey;
public SecurityEnhancements(String secretKey) {
this.hmacKey = new SecretKeySpec(secretKey.getBytes(), HMAC_ALGORITHM);
}
// 验证请求签名,防止伪造
public boolean verifyRequestSignature(String request, String signature,
String timestamp) {
try {
// 检查时间戳,5分钟内有效
long requestTime = Long.parseLong(timestamp);
long currentTime = System.currentTimeMillis();
if (Math.abs(currentTime - requestTime) > 300000) {
return false;
}
// 计算签名
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
mac.init(hmacKey);
String data = request + timestamp;
byte[] hmacBytes = mac.doFinal(data.getBytes());
String calculatedSignature = Base64.getEncoder().encodeToString(hmacBytes);
return calculatedSignature.equals(signature);
} catch (Exception e) {
logger.warning("签名验证失败: " + e.getMessage());
return false;
}
}
// 创建SSL服务器
public static SSLServerSocket createSSLServerSocket(int port,
String keystorePath,
String keystorePassword)
throws Exception {
KeyStore keyStore = KeyStore.getInstance("JKS");
try (FileInputStream fis = new FileInputStream(keystorePath)) {
keyStore.load(fis, keystorePassword.toCharArray());
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keyStore, keystorePassword.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(),
new SecureRandom());
SSLServerSocketFactory factory = sslContext.getServerSocketFactory();
SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(port);
// 只允许安全的协议和加密套件
serverSocket.setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.3"});
serverSocket.setEnabledCipherSuites(new String[]{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
});
return serverSocket;
}
// 输入验证器
public static class InputValidator {
private static final int MAX_REQUEST_SIZE = 4096;
private static final Pattern SQL_INJECTION_PATTERN = Pattern.compile(
"(?i).*(union|select|insert|update|delete|drop|create|alter|exec|script).*"
);
private static final Pattern XSS_PATTERN = Pattern.compile(
"(?i).*(<script|javascript:|onerror|onload|onclick).*"
);
public static ValidationResult validate(String input) {
if (input == null || input.isEmpty()) {
return ValidationResult.invalid("空请求");
}
if (input.length() > MAX_REQUEST_SIZE) {
return ValidationResult.invalid("请求过大");
}
if (SQL_INJECTION_PATTERN.matcher(input).matches()) {
return ValidationResult.invalid("检测到SQL注入");
}
if (XSS_PATTERN.matcher(input).matches()) {
return ValidationResult.invalid("检测到XSS攻击");
}
return ValidationResult.valid();
}
static class ValidationResult {
final boolean valid;
final String reason;
private ValidationResult(boolean valid, String reason) {
this.valid = valid;
this.reason = reason;
}
static ValidationResult valid() {
return new ValidationResult(true, null);
}
static ValidationResult invalid(String reason) {
return new ValidationResult(false, reason);
}
}
}
}
10. 监控指标收集器
实时监控系统状态,及时发现异常:
java
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.binder.jvm.*;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.prometheus.*;
import io.prometheus.client.exporter.HTTPServer;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.*;
public class MetricsCollector {
private static final Logger logger = Logger.getLogger(MetricsCollector.class.getName());
private final MeterRegistry registry;
private final Counter connectionAttempts;
private final Counter connectionRejections;
private final Timer requestDuration;
private final Counter attackDetections;
private ConnectionLimiter connectionLimiter;
public MetricsCollector() {
this.registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
// 注册JVM指标
new JvmGcMetrics().bindTo(registry);
new JvmMemoryMetrics().bindTo(registry);
new JvmThreadMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
// 自定义指标
this.connectionAttempts = Counter.builder("connection.attempts")
.description("总连接尝试数")
.register(registry);
this.connectionRejections = Counter.builder("connection.rejections")
.description("拒绝的连接数")
.tag("reason", "unknown")
.register(registry);
this.requestDuration = Timer.builder("request.duration")
.description("请求处理时间")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
this.attackDetections = Counter.builder("attack.detections")
.description("检测到的攻击次数")
.tag("type", "syn_flood")
.register(registry);
}
public void setConnectionLimiter(ConnectionLimiter connectionLimiter) {
this.connectionLimiter = connectionLimiter;
// 注册活跃连接数指标
Gauge.builder("connections.active", connectionLimiter,
ConnectionLimiter::getCurrentConnections)
.description("当前活跃连接数")
.register(registry);
}
public void recordConnectionAttempt(String clientIp) {
connectionAttempts.increment();
Counter.builder("connection.attempts.by.ip")
.tag("ip", anonymizeIp(clientIp))
.register(registry)
.increment();
}
public void recordConnectionRejection(String reason, String clientIp) {
Counter.builder("connection.rejections")
.tag("reason", reason)
.tag("ip", anonymizeIp(clientIp))
.register(registry)
.increment();
}
public void recordRequestDuration(long startTime, String endpoint) {
requestDuration.record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
}
public void recordAttackDetection(String attackType, String clientIp) {
Counter.builder("attack.detections")
.tag("type", attackType)
.tag("ip", anonymizeIp(clientIp))
.register(registry)
.increment();
}
private String anonymizeIp(String ip) {
// 保护隐私,只显示前两段
String[] parts = ip.split("\\.");
if (parts.length == 4) {
return parts[0] + "." + parts[1] + ".x.x";
}
return "unknown";
}
public void startHttpServer(int port) throws IOException {
HTTPServer server = new HTTPServer(port);
logger.info("Prometheus指标服务启动在端口: " + port);
}
public PrometheusMeterRegistry getRegistry() {
return (PrometheusMeterRegistry) registry;
}
}
11. 主程序入口
把所有组件组装起来,形成完整的防御系统:
java
import java.util.concurrent.*;
import java.util.logging.*;
import com.sun.net.httpserver.*;
import java.net.InetSocketAddress;
import java.io.IOException;
public class SynFloodDefenseApplication {
private static final Logger logger = Logger.getLogger(
SynFloodDefenseApplication.class.getName());
private final NIOSecureServer nioServer;
private final MetricsCollector metricsCollector;
private final ScheduledExecutorService scheduler;
private final HttpServer adminServer;
public SynFloodDefenseApplication() throws Exception {
// 加载并验证配置
DefenseConfig.loadConfig();
DefenseConfig.validate();
// 初始化监控
this.metricsCollector = new MetricsCollector();
// 初始化NIO服务器
this.nioServer = new NIOSecureServer(DefenseConfig.getServerPort(), metricsCollector);
// 设置连接限制器到监控系统
metricsCollector.setConnectionLimiter(nioServer.getConnectionLimiter());
// 初始化调度器
this.scheduler = Executors.newScheduledThreadPool(2);
// 初始化管理接口
this.adminServer = HttpServer.create(
new InetSocketAddress(DefenseConfig.getAdminPort()), 0);
setupAdminEndpoints();
}
private void setupAdminEndpoints() {
// 健康检查
adminServer.createContext("/health", exchange -> {
String response = "{\"status\":\"UP\",\"timestamp\":" +
System.currentTimeMillis() + "}";
exchange.getResponseHeaders().set("Content-Type", "application/json");
exchange.sendResponseHeaders(200, response.length());
try (var os = exchange.getResponseBody()) {
os.write(response.getBytes());
}
});
// 配置重载
adminServer.createContext("/admin/reload-config", exchange -> {
if (!"POST".equals(exchange.getRequestMethod())) {
exchange.sendResponseHeaders(405, -1);
return;
}
try {
DefenseConfig.loadConfig();
DefenseConfig.validate();
String response = "{\"status\":\"Config reloaded successfully\"}";
exchange.sendResponseHeaders(200, response.length());
try (var os = exchange.getResponseBody()) {
os.write(response.getBytes());
}
logger.info("配置重新加载成功");
} catch (Exception e) {
String error = "{\"error\":\"" + e.getMessage() + "\"}";
exchange.sendResponseHeaders(500, error.length());
try (var os = exchange.getResponseBody()) {
os.write(error.getBytes());
}
}
});
adminServer.setExecutor(Executors.newCachedThreadPool());
}
public void start() {
try {
printStartupInfo();
// 系统优化检查
SystemOptimizer.checkSystemConfig();
// 启动Prometheus
metricsCollector.startHttpServer(9090);
// 启动管理服务器
adminServer.start();
logger.info("管理接口启动在端口: " + DefenseConfig.getAdminPort());
// 启动定期任务
startScheduledTasks();
// 启动NIO服务器
Thread serverThread = new Thread(() -> {
try {
nioServer.start();
} catch (Exception e) {
logger.severe("NIO服务器启动失败: " + e.getMessage());
e.printStackTrace();
}
});
serverThread.setName("NIOServer-Main");
serverThread.start();
logger.info("SYN Flood防御系统启动完成");
// 注册关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
// 保持主线程运行
serverThread.join();
} catch (Exception e) {
logger.severe("启动失败: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
private void printStartupInfo() {
String banner = "\n" +
"███████╗██╗ ██╗███╗ ██╗ ███████╗██╗ ██████╗ ██████╗ ██████╗ \n" +
"██╔════╝╚██╗ ██╔╝████╗ ██║ ██╔════╝██║ ██╔═══██╗██╔═══██╗██╔══██╗\n" +
"███████╗ ╚████╔╝ ██╔██╗ ██║ █████╗ ██║ ██║ ██║██║ ██║██║ ██║\n" +
"╚════██║ ╚██╔╝ ██║╚██╗██║ ██╔══╝ ██║ ██║ ██║██║ ██║██║ ██║\n" +
"███████║ ██║ ██║ ╚████║ ██║ ███████╗╚██████╔╝╚██████╔╝██████╔╝\n" +
"╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ \n" +
" DEFENSE SYSTEM v1.0 \n";
logger.info(banner);
logger.info("=== 系统信息 ===");
logger.info("操作系统: " + System.getProperty("os.name"));
logger.info("Java版本: " + System.getProperty("java.version"));
logger.info("可用处理器: " + Runtime.getRuntime().availableProcessors());
logger.info("最大内存: " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "MB");
logger.info("服务端口: " + DefenseConfig.getServerPort());
logger.info("管理端口: " + DefenseConfig.getAdminPort());
logger.info("监控端口: 9090");
}
private void startScheduledTasks() {
// 定期打印统计信息
scheduler.scheduleAtFixedRate(() -> {
try {
printStatistics();
} catch (Exception e) {
logger.warning("打印统计信息失败: " + e.getMessage());
}
}, 1, 1, TimeUnit.MINUTES);
// 定期检查系统健康
scheduler.scheduleAtFixedRate(() -> {
try {
checkSystemHealth();
} catch (Exception e) {
logger.warning("系统健康检查失败: " + e.getMessage());
}
}, 30, 30, TimeUnit.SECONDS);
}
private void printStatistics() {
logger.info("=== 运行统计 ===");
logger.info("活跃连接数: " + nioServer.getConnectionLimiter().getCurrentConnections());
logger.info("系统运行正常");
}
private void checkSystemHealth() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
double memoryUsage = (double) usedMemory / runtime.maxMemory();
if (memoryUsage > 0.9) {
logger.warning("内存使用率过高: " +
String.format("%.2f%%", memoryUsage * 100));
}
int threadCount = Thread.activeCount();
if (threadCount > 1000) {
logger.warning("线程数过多: " + threadCount);
}
}
private void shutdown() {
logger.info("正在关闭SYN Flood防御系统...");
try {
nioServer.stop();
scheduler.shutdown();
scheduler.awaitTermination(5, TimeUnit.SECONDS);
adminServer.stop(0);
logger.info("系统已安全关闭");
} catch (Exception e) {
logger.severe("关闭过程中发生错误: " + e.getMessage());
}
}
public static void main(String[] args) {
try {
// 配置日志
LogManager.getLogManager().readConfiguration(
SynFloodDefenseApplication.class
.getResourceAsStream("/logging.properties"));
// 启动应用
SynFloodDefenseApplication app = new SynFloodDefenseApplication();
app.start();
} catch (Exception e) {
System.err.println("启动失败: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
}
配置文件示例
defense.properties
properties
# 连接限制配置
max.connections=1000
max.connections.per.ip=10
# 速率限制配置
rate.limit.capacity=100
rate.limit.refill.rate=50
# 异常检测配置
suspicious.threshold=5
# 服务器配置
server.port=8080
admin.port=8081
socket.timeout=30000
logging.properties
properties
# 日志配置
handlers=java.util.logging.ConsoleHandler,java.util.logging.FileHandler
# 默认日志级别
.level=INFO
# 控制台处理器
java.util.logging.ConsoleHandler.level=INFO
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s%n
# 文件处理器
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.pattern=logs/application.log
java.util.logging.FileHandler.limit=10485760
java.util.logging.FileHandler.count=10
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append=true
# 特定包的日志级别
com.defense.level=FINE
pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.defense</groupId>
<artifactId>syn-flood-defense</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Redis客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
<!-- Prometheus监控 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.10.5</version>
</dependency>
<!-- 测试框架 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
测试用例
java
import org.junit.*;
import org.mockito.*;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import java.net.*;
import java.util.concurrent.*;
import java.time.*;
public class DefenseSystemTest {
@Mock
private Socket mockSocket;
@Mock
private InetAddress mockAddress;
private ConnectionLimiter connectionLimiter;
private RateLimiter rateLimiter;
private IPBlacklist blacklist;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
connectionLimiter = new ConnectionLimiter(100, 5);
rateLimiter = new RateLimiter(10, 5);
blacklist = new IPBlacklist();
// 配置mock
when(mockAddress.getHostAddress()).thenReturn("192.168.1.100");
when(mockSocket.getInetAddress()).thenReturn(mockAddress);
when(mockSocket.isClosed()).thenReturn(false);
}
@Test
public void testRateLimiterAccuracy() throws InterruptedException {
String testIp = "192.168.1.100";
int successCount = 0;
// 消耗初始令牌
for (int i = 0; i < 10; i++) {
assertTrue(rateLimiter.tryAcquire(testIp));
}
assertFalse(rateLimiter.tryAcquire(testIp));
// 等待补充
Thread.sleep(1100);
// 应该补充了约5个令牌
for (int i = 0; i < 10; i++) {
if (rateLimiter.tryAcquire(testIp)) {
successCount++;
}
}
assertTrue(successCount >= 4 && successCount <= 6);
}
@Test
public void testBlacklistExpiration() throws InterruptedException {
String testIp = "192.168.1.100";
// 添加临时黑名单
blacklist.addToBlacklist(testIp, Duration.ofMillis(500));
assertTrue(blacklist.isBlacklisted(testIp));
// 等待过期
Thread.sleep(600);
assertFalse(blacklist.isBlacklisted(testIp));
}
@Test
public void testConnectionLimiterPerIP() throws Exception {
String testIp = "192.168.1.100";
// 测试单IP连接限制
for (int i = 0; i < 5; i++) {
Socket socket = mock(Socket.class);
InetAddress address = mock(InetAddress.class);
when(address.getHostAddress()).thenReturn(testIp);
when(socket.getInetAddress()).thenReturn(address);
when(socket.isClosed()).thenReturn(false);
assertTrue("第" + (i+1) + "个连接应该成功",
connectionLimiter.acceptConnection(socket));
}
// 第6个连接应该失败
Socket socket = mock(Socket.class);
InetAddress address = mock(InetAddress.class);
when(address.getHostAddress()).thenReturn(testIp);
when(socket.getInetAddress()).thenReturn(address);
when(socket.isClosed()).thenReturn(false);
assertFalse("第6个连接应该失败",
connectionLimiter.acceptConnection(socket));
// 验证socket被关闭
verify(socket).close();
}
@After
public void tearDown() {
connectionLimiter.shutdown();
blacklist.shutdown();
}
}
性能测试工具
java
import java.net.*;
import java.io.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.logging.*;
public class PerformanceTest {
private static final Logger logger = Logger.getLogger(PerformanceTest.class.getName());
public static void main(String[] args) throws Exception {
int threads = 100;
int requestsPerThread = 1000;
String targetHost = "localhost";
int targetPort = 8080;
ExecutorService executor = Executors.newFixedThreadPool(threads);
CountDownLatch startLatch = new CountDownLatch(1);
CountDownLatch endLatch = new CountDownLatch(threads);
AtomicInteger successCount = new AtomicInteger();
AtomicInteger failCount = new AtomicInteger();
AtomicLong totalLatency = new AtomicLong();
for (int i = 0; i < threads; i++) {
executor.submit(() -> {
try {
startLatch.await();
for (int j = 0; j < requestsPerThread; j++) {
long start = System.nanoTime();
try (Socket socket = new Socket(targetHost, targetPort)) {
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
out.println("TEST");
String response = in.readLine();
if (response != null) {
successCount.incrementAndGet();
totalLatency.addAndGet(System.nanoTime() - start);
} else {
failCount.incrementAndGet();
}
} catch (Exception e) {
failCount.incrementAndGet();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
endLatch.countDown();
}
});
}
logger.info("开始性能测试...");
long testStart = System.currentTimeMillis();
startLatch.countDown();
endLatch.await();
long testDuration = System.currentTimeMillis() - testStart;
logger.info("=== 测试结果 ===");
logger.info(String.format("总请求数: %d", threads * requestsPerThread));
logger.info(String.format("成功: %d", successCount.get()));
logger.info(String.format("失败: %d", failCount.get()));
logger.info(String.format("总耗时: %.2f秒", testDuration / 1000.0));
logger.info(String.format("吞吐量: %.2f req/s",
successCount.get() / (testDuration / 1000.0)));
logger.info(String.format("平均延迟: %.2f ms",
totalLatency.get() / successCount.get() / 1_000_000.0));
executor.shutdown();
}
}
容器化部署
Dockerfile
dockerfile
# 构建阶段
FROM maven:3.8-openjdk-11 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# 运行阶段
FROM openjdk:11-jre-slim
WORKDIR /app
# 安装必要工具
RUN apt-get update && apt-get install -y \
curl \
netcat \
&& rm -rf /var/lib/apt/lists/*
# 复制打包好的文件
COPY --from=builder /app/target/syn-flood-defense.jar .
COPY --from=builder /app/target/lib ./lib
# 复制配置文件
COPY config/defense.properties ./config/
# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
RUN chown -R appuser:appuser /app
USER appuser
# JVM优化参数
ENV JAVA_OPTS="-XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:+UseStringDeduplication \
-XX:+ParallelRefProcEnabled \
-XX:MaxRAMPercentage=75.0 \
-XX:+ExitOnOutOfMemoryError \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/tmp/heapdump.hprof"
# 暴露端口
EXPOSE 8080 8081 9090
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:8081/health || exit 1
# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar syn-flood-defense.jar"]
Kubernetes 部署配置
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: syn-flood-defense
spec:
replicas: 3
selector:
matchLabels:
app: syn-flood-defense
template:
metadata:
labels:
app: syn-flood-defense
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
spec:
containers:
- name: app
image: syn-flood-defense:latest
ports:
- containerPort: 8080
name: service
- containerPort: 8081
name: admin
- containerPort: 9090
name: metrics
env:
- name: DEFENSE_MAX_CONNECTIONS
value: "2000"
- name: REDIS_HOST
value: redis-service
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health
port: 8081
initialDelaySeconds: 30
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: syn-flood-defense-service
spec:
selector:
app: syn-flood-defense
ports:
- port: 8080
targetPort: 8080
name: service
type: LoadBalancer
---
apiVersion: v1
kind: ConfigMap
metadata:
name: defense-config
data:
defense.properties: |
max.connections=2000
max.connections.per.ip=20
rate.limit.capacity=200
rate.limit.refill.rate=100
suspicious.threshold=10
server.port=8080
socket.timeout=30000
部署运维
快速部署脚本
bash
#!/bin/bash
# deploy.sh - 一键部署脚本
echo "开始部署SYN Flood防御系统..."
# 检查环境
check_environment() {
echo "检查部署环境..."
# 检查Java
if ! command -v java &> /dev/null; then
echo "错误: 未安装Java"
exit 1
fi
# 检查Docker
if ! command -v docker &> /dev/null; then
echo "警告: 未安装Docker,跳过容器化部署"
fi
echo "环境检查通过"
}
# 创建应用用户
create_app_user() {
if ! id "appuser" &>/dev/null; then
echo "创建应用用户..."
sudo useradd -r -s /bin/false appuser
fi
}
# 优化系统参数
optimize_system() {
echo "优化系统参数..."
# 备份原配置
sudo cp /etc/sysctl.conf /etc/sysctl.conf.backup
# 应用优化
cat << EOF | sudo tee -a /etc/sysctl.conf
# SYN Flood Defense
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 2
net.core.somaxconn = 65535
EOF
sudo sysctl -p
echo "系统优化完成"
}
# 部署应用
deploy_application() {
echo "部署应用..."
# 创建目录
sudo mkdir -p /opt/syn-flood-defense/{config,logs}
# 复制文件
sudo cp target/syn-flood-defense.jar /opt/syn-flood-defense/
sudo cp config/* /opt/syn-flood-defense/config/
# 设置权限
sudo chown -R appuser:appuser /opt/syn-flood-defense
# 创建systemd服务
cat << EOF | sudo tee /etc/systemd/system/syn-flood-defense.service
[Unit]
Description=SYN Flood Defense System
After=network.target
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/syn-flood-defense
ExecStart=/usr/bin/java -jar syn-flood-defense.jar
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# 启动服务
sudo systemctl daemon-reload
sudo systemctl enable syn-flood-defense
sudo systemctl start syn-flood-defense
echo "应用部署完成"
}
# 执行部署
check_environment
create_app_user
optimize_system
deploy_application
echo "部署完成!"
echo "服务状态: sudo systemctl status syn-flood-defense"
echo "查看日志: sudo journalctl -u syn-flood-defense -f"
Prometheus 告警规则
yaml
# prometheus-alerts.yml
groups:
- name: syn_flood_alerts
rules:
- alert: HighConnectionRejectionRate
expr: rate(connection_rejections[1m]) > 100
for: 2m
labels:
severity: warning
annotations:
summary: "连接拒绝率过高"
description: "过去1分钟拒绝了 {{ $value }} 个连接"
- alert: PossibleSynFloodAttack
expr: rate(attack_detections{type="syn_flood"}[5m]) > 10
for: 1m
labels:
severity: critical
annotations:
summary: "检测到可能的SYN Flood攻击"
description: "5分钟内检测到 {{ $value }} 次攻击"
- alert: HighMemoryUsage
expr: jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "JVM内存使用率过高"
description: "堆内存使用率超过90%"
总结
防御措施 | 实现方式 | 效果 | 适用场景 |
---|---|---|---|
连接数限制 | Semaphore + ConcurrentHashMap | 防止资源耗尽 | 所有 TCP 服务 |
分布式限流 | Redis + 滑动窗口 | 支持集群部署 | 分布式系统 |
本地限流 | 令牌桶算法 | 平滑限流 | 单机应用 |
IP 黑名单 | 永久/临时黑名单 | 快速阻断恶意 IP | 遭受持续攻击时 |
异常检测 | 行为模式分析 | 自动识别攻击 | 智能防御场景 |
NIO 服务器 | Java NIO + 线程池 | 高并发处理 | 高性能要求 |
SSL/TLS | TLSv1.3 加密 | 传输安全 | 敏感数据传输 |
输入验证 | 正则匹配 | 防止注入攻击 | Web 应用 |
监控告警 | Prometheus + Grafana | 实时监控 | 生产环境 |
系统优化 | 内核参数调整 | 提升抗压能力 | Linux 服务器 |
附录:API 文档
java
/**
* 管理接口API文档
*
* GET /health
* 健康检查接口
* 响应示例:
* {
* "status": "UP",
* "timestamp": 1234567890,
* "checks": {
* "redis": "UP",
* "memory": "OK",
* "connections": 150
* }
* }
*
* POST /admin/reload-config
* 重新加载配置文件
* 响应示例:
* {
* "status": "Config reloaded successfully"
* }
*
* GET /admin/blacklist
* 获取当前黑名单列表
* 响应示例:
* {
* "blacklist": [
* {"ip": "192.168.1.100", "type": "permanent"},
* {"ip": "192.168.1.101", "type": "temporary", "expiry": 1234567890}
* ]
* }
*
* POST /admin/blacklist
* 添加IP到黑名单
* 请求体:
* {
* "ip": "192.168.1.100",
* "duration": "30m" // 可选,不填则永久黑名单
* }
*
* DELETE /admin/blacklist/{ip}
* 从黑名单移除指定IP
*
* GET /metrics
* Prometheus监控指标
* 端口:9090
*/
故障排查
遇到问题时,按以下步骤排查:
1. 服务无法启动
bash
# 检查端口占用
netstat -tlnp | grep -E '8080|8081|9090'
# 查看详细日志
tail -f /opt/syn-flood-defense/logs/application.log
# 检查配置文件
cat /opt/syn-flood-defense/config/defense.properties
2. 连接被大量拒绝
bash
# 检查当前配置
curl http://localhost:8081/health
# 查看黑名单
curl http://localhost:8081/admin/blacklist
# 查看监控指标
curl http://localhost:9090/metrics | grep connection
3. 内存占用过高
bash
# 生成堆转储
jmap -dump:format=b,file=heapdump.hprof $(pgrep -f syn-flood-defense)
# 查看线程状态
jstack $(pgrep -f syn-flood-defense) > threaddump.txt
# 查看GC日志
grep "Full GC" /opt/syn-flood-defense/logs/gc.log
4. Redis 连接失败
bash
# 检查Redis状态
redis-cli ping
# 检查连接数
redis-cli info clients
# 测试连接
redis-cli -h localhost -p 6379
5. 性能问题排查
bash
# CPU使用率
top -H -p $(pgrep -f syn-flood-defense)
# 网络连接数
ss -ant | grep :8080 | wc -l
# IO等待
iostat -x 1
性能调优建议
JVM 参数优化
bash
# 生产环境推荐配置
JAVA_OPTS="-server \
-Xms4g \
-Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:+ParallelRefProcEnabled \
-XX:+UseStringDeduplication \
-XX:+DisableExplicitGC \
-XX:+AlwaysPreTouch \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseNUMA \
-Djava.net.preferIPv4Stack=true \
-Dfile.encoding=UTF-8"
系统参数优化
bash
# 网络优化
echo 'net.core.netdev_max_backlog = 5000' >> /etc/sysctl.conf
echo 'net.ipv4.tcp_mem = 786432 1048576 26777216' >> /etc/sysctl.conf
echo 'net.ipv4.tcp_rmem = 4096 87380 134217728' >> /etc/sysctl.conf
echo 'net.ipv4.tcp_wmem = 4096 65536 134217728' >> /etc/sysctl.conf
# 文件系统优化
echo 'fs.file-max = 2097152' >> /etc/sysctl.conf
echo 'fs.nr_open = 2097152' >> /etc/sysctl.conf
监控优化
yaml
# Grafana查询优化
- 使用recording rules减少查询压力
- 合理设置数据保留期
- 使用downsampling降低存储需求
常见问题解答
Q: 为什么选择 NIO 而不是 Netty?
A: NIO 已经能满足大部分场景需求,而且是 JDK 自带的,减少了外部依赖。如果需要更高性能,可以很容易地改造成 Netty 实现。
Q: Redis 挂了怎么办?
A: 系统会自动降级到本地限流,虽然在分布式环境下可能不够精确,但能保证服务可用。建议使用 Redis 集群提高可用性。
Q: 如何确定合适的限流阈值?
A: 先通过压测确定系统极限,然后设置为极限值的 70-80%。生产环境中根据实际情况动态调整。
Q: 黑名单会不会误伤正常用户?
A: 系统采用临时黑名单机制,默认 30 分钟后自动解除。同时提供了 API 可以手动移除误判的 IP。
Q: 能防御多大规模的攻击?
A: 单机能处理每秒数万个连接请求,具体取决于硬件配置。建议配合 CDN 和硬件防火墙使用。