Linux防止SSH暴力破解的多种方法
引言
SSH(Secure Shell)是 Linux 系统管理员远程管理服务器最常用的协议。它安全、稳定、功能强大,但也正因为如此,SSH 服务往往成为黑客攻击的首要目标 —— 尤其是暴力破解(Brute Force Attack)。攻击者通过自动化脚本不断尝试用户名和密码组合,试图获得系统访问权限。一旦成功,后果不堪设想:数据泄露、系统被控、沦为肉鸡、甚至被用于发动进一步攻击。
在本文中,我们将深入探讨如何从多个维度有效防御 SSH 暴力破解攻击。不仅会介绍传统配置方法,还会提供 Java 实现的实时监控与自动封禁工具,并结合可视化图表帮助你理解攻击模式和防御机制。
什么是 SSH 暴力破解?
SSH 暴力破解是指攻击者利用自动化程序,反复尝试不同的用户名和密码组合,以期“撞开”系统的登录大门。这类攻击通常具有以下特征:
- 高频次连接请求(每秒数次到数百次)
- 使用常见用户名(如
root,admin,test) - 使用弱密码字典(如
123456,password,qwerty) - 来源 IP 分布广泛或集中在某些恶意 IP 段
- 攻击持续时间长,可能持续数天甚至数周
小知识:根据 Shodan.io 的统计,全球每天有超过 200 万次 SSH 登录尝试来自恶意扫描器。
SSH 攻击趋势分析(mermaid 图表)
我们先来看一组模拟的 SSH 攻击频率随时间变化图,这有助于理解为何我们需要主动防御:

从上图可见,攻击并非均匀分布,而是集中在工作时间段,且周三出现“持续攻击”,说明攻击者也在“上班”。我们需要的是动态、智能、多层次的防御策略。
方法一:修改默认 SSH 端口
这是最基础但非常有效的一步。默认的 SSH 端口是 22,绝大多数自动化脚本都只会扫这个端口。改个端口,能过滤掉 90% 的低级扫描。
操作步骤:
- 编辑 SSH 配置文件:
sudo nano /etc/ssh/sshd_config
- 找到
Port 22,修改为其他端口(比如 2222):
Port 2222
- 重启 SSH 服务:
sudo systemctl restart sshd
- 别忘了在防火墙放行新端口:
sudo ufw allow 2222/tcp
注意:修改端口前请确保你有其他方式访问服务器(如控制台),避免把自己锁在外面!
方法二:禁用 root 登录
root 是 Linux 的超级用户,也是攻击者的首要目标。禁用其直接登录可大幅提升安全性。
操作步骤:
编辑 /etc/ssh/sshd_config:
PermitRootLogin no
然后重启 SSH:
sudo systemctl restart sshd
之后,你需要先用普通用户登录,再通过 su - 或 sudo 提权。
方法三:启用密钥认证,禁用密码登录
密码容易被暴力破解,而 SSH 密钥几乎不可能被暴力攻破(前提是私钥保管好)。
操作步骤:
- 在本地生成密钥对(如果还没有):
ssh-keygen -t rsa -b 4096
- 将公钥上传到服务器:
ssh-copy-id -p 2222 user@your-server-ip
- 修改
/etc/ssh/sshd_config:
PasswordAuthentication no PubkeyAuthentication yes
- 重启 SSH:
sudo systemctl restart sshd
现在只能通过私钥登录,暴力破解彻底失效!
方法四:使用 Fail2ban 自动封禁恶意 IP
Fail2ban 是一个入侵防御软件,它能监控日志文件,发现多次失败登录后自动将 IP 加入防火墙黑名单。
安装与配置:
sudo apt update sudo apt install fail2ban
复制默认配置:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
编辑 jail.local,设置 SSH 监控:
[sshd] enabled = true port = 2222 filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 86400 findtime = 600
解释:
maxretry = 3:10分钟内失败3次就封bantime = 86400:封禁24小时findtime = 600:监控最近10分钟内的日志
启动服务:
sudo systemctl enable fail2ban sudo systemctl start fail2ban
查看封禁状态:
sudo fail2ban-client status sshd
方法五:配置防火墙限制访问来源
如果你的团队只有固定几个 IP 需要访问 SSH,那就只允许这些 IP 连接。
使用 UFW(Ubuntu):
sudo ufw allow from 192.168.1.100 to any port 2222 sudo ufw deny 2222 sudo ufw enable
使用 firewalld(CentOS/RHEL):
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.100" port protocol="tcp" port="2222" accept' sudo firewall-cmd --permanent --remove-service=ssh sudo firewall-cmd --reload
这样,非白名单 IP 根本连不上 SSH 端口。
方法六:部署自研 Java 监控工具(实战代码!)
虽然 Fail2ban 很强大,但有时我们需要更灵活的控制 —— 比如对接企业内部告警系统、发送邮件、记录数据库、或对接云平台 API。
下面是一个用 Java 编写的简易 SSH 暴力破解监控器,它读取 /var/log/auth.log,检测失败登录,自动调用 iptables 封禁 IP。
项目结构简洁,适合二次开发集成到现有运维平台。
项目依赖(Maven)
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.13.0</version>
</dependency>
</dependencies>核心代码:SSHMonitor.java
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SSHMonitor {
private static final Logger logger = LoggerFactory.getLogger(SSHMonitor.class);
// 日志文件路径(Ubuntu/Debian)
private static final String AUTH_LOG_PATH = "/var/log/auth.log";
// 匹配失败登录的正则表达式
private static final String FAILED_LOGIN_PATTERN =
".*Failed password for (?:invalid user )?(\\S+) from (\\d+\\.\\d+\\.\\d+\\.\\d+).*";
// 白名单IP(不封禁)
private static final Set<String> WHITE_LIST = Set.of(
"127.0.0.1",
"192.168.1.1", // 示例,替换成你的管理IP
"10.0.0.1"
);
// 失败次数阈值
private static final int MAX_RETRY = 5;
// 封禁时长(秒)
private static final int BAN_DURATION = 3600; // 1小时
// 存储IP失败次数
private final Map<String, Integer> failedAttempts = new HashMap<>();
// 存储已封禁IP及解封时间
private final Map<String, LocalDateTime> bannedIPs = new HashMap<>();
public void startMonitoring() {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 每10秒扫描一次日志
scheduler.scheduleAtFixedRate(this::scanAuthLog, 0, 10, TimeUnit.SECONDS);
// 每分钟清理过期封禁
scheduler.scheduleAtFixedRate(this::unbanExpiredIPs, 0, 1, TimeUnit.MINUTES);
logger.info("SSH 暴力破解监控器已启动...");
}
private void scanAuthLog() {
File logFile = new File(AUTH_LOG_PATH);
if (!logFile.exists()) {
logger.warn("日志文件不存在: {}", AUTH_LOG_PATH);
return;
}
try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) {
String line;
Pattern pattern = Pattern.compile(FAILED_LOGIN_PATTERN);
while ((line = reader.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.matches()) {
String user = matcher.group(1);
String ip = matcher.group(2);
if (WHITE_LIST.contains(ip)) {
continue; // 白名单跳过
}
failedAttempts.merge(ip, 1, Integer::sum);
int attempts = failedAttempts.get(ip);
logger.warn("检测到失败登录: 用户={} IP={} 尝试次数={}", user, ip, attempts);
if (attempts >= MAX_RETRY && !bannedIPs.containsKey(ip)) {
banIP(ip);
}
}
}
} catch (IOException e) {
logger.error("读取日志文件失败", e);
}
}
private void banIP(String ip) {
try {
// 调用 iptables 封禁
String cmd = String.format("iptables -A INPUT -s %s -j DROP", ip);
Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();
LocalDateTime unbanTime = LocalDateTime.now().plusSeconds(BAN_DURATION);
bannedIPs.put(ip, unbanTime);
logger.info("⛔ 已封禁 IP: {},解封时间: {}", ip, unbanTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// 可选:发送邮件/企业微信/钉钉通知
// sendAlert("SSH暴力破解警报", "IP " + ip + " 因多次失败登录已被封禁");
} catch (Exception e) {
logger.error("封禁IP失败: {}", ip, e);
}
}
private void unbanExpiredIPs() {
LocalDateTime now = LocalDateTime.now();
List<String> toUnban = new ArrayList<>();
for (Map.Entry<String, LocalDateTime> entry : bannedIPs.entrySet()) {
if (now.isAfter(entry.getValue())) {
toUnban.add(entry.getKey());
}
}
for (String ip : toUnban) {
try {
String cmd = String.format("iptables -D INPUT -s %s -j DROP", ip);
Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();
bannedIPs.remove(ip);
failedAttempts.remove(ip); // 清除计数
logger.info("✅ 已解封 IP: {}", ip);
} catch (Exception e) {
logger.error("解封IP失败: {}", ip, e);
}
}
}
// 可扩展:发送告警通知
private void sendAlert(String title, String content) {
// 此处可集成邮件、Slack、企业微信、钉钉等
// 示例略...
}
public static void main(String[] args) {
new SSHMonitor().startMonitoring();
}
}如何运行?
- 编译打包成 JAR:
mvn clean package
- 后台运行(需 root 权限,因为要执行 iptables):
sudo java -jar ssh-monitor.jar
- 查看日志:
tail -f logs/app.log
可扩展功能建议:
- 对接 Redis 存储状态,支持多节点部署
- 记录攻击日志到 MySQL/Elasticsearch
- 调用云厂商 API(如阿里云、AWS)添加安全组规则
- 发送 Telegram/Slack/DingTalk 告警
- 支持 GeoIP 定位攻击者地理位置
攻击者地理分布模拟(mermaid 图表)
了解攻击来源有助于制定更有针对性的防御策略。以下是模拟的攻击 IP 地理分布:
渲染错误: Mermaid 渲染失败: Parsing failed: Lexer error on line 3, column 5: unexpected character: ->“<- at offset: 34, skipped 4 characters. Lexer error on line 3, column 10: unexpected character: ->:<- at offset: 39, skipped 1 characters. Lexer error on line 4, column 5: unexpected character: ->“<- at offset: 48, skipped 4 characters. Lexer error on line 4, column 10: unexpected character: ->:<- at offset: 53, skipped 1 characters. Lexer error on line 5, column 5: unexpected character: ->“<- at offset: 62, skipped 5 characters. Lexer error on line 5, column 11: unexpected character: ->:<- at offset: 68, skipped 1 characters. Lexer error on line 6, column 5: unexpected character: ->“<- at offset: 77, skipped 4 characters. Lexer error on line 6, column 10: unexpected character: ->:<- at offset: 82, skipped 1 characters. Lexer error on line 7, column 5: unexpected character: ->“<- at offset: 91, skipped 4 characters. Lexer error on line 7, column 10: unexpected character: ->:<- at offset: 96, skipped 1 characters. Lexer error on line 8, column 5: unexpected character: ->“<- at offset: 104, skipped 4 characters. Lexer error on line 8, column 10: unexpected character: ->:<- at offset: 109, skipped 1 characters. Parse error on line 3, column 12: Expecting token of type 'EOF' but found `35`. Parse error on line 4, column 12: Expecting token of type 'EOF' but found `25`. Parse error on line 5, column 13: Expecting token of type 'EOF' but found `15`. Parse error on line 6, column 12: Expecting token of type 'EOF' but found `10`. Parse error on line 7, column 12: Expecting token of type 'EOF' but found `8`. Parse error on line 8, column 12: Expecting token of type 'EOF' but found `7`.
可见,攻击来源分布广泛,无法单纯靠地域屏蔽。但可以结合威胁情报(如 AbuseIPDB)动态更新黑名单。
方法七:使用云端威胁情报服务
一些第三方服务提供全球恶意 IP 数据库,我们可以定期拉取并更新本地防火墙规则。
推荐两个免费 API:
示例:定时拉取 AbuseIPDB 黑名单
#!/bin/bash
# download-blacklist.sh
API_KEY="your_api_key_here"
BLACKLIST_URL="https://api.abuseipdb.com/api/v2/blacklist?confidenceMinimum=90"
curl -G $BLACKLIST_URL \
-H "Key: $API_KEY" \
-H "Accept: application/json" \
--data-urlencode "confidenceMinimum=90" \
| jq -r '.data[].ipAddress' > /tmp/abuseip_blacklist.txt
while read ip; do
iptables -A INPUT -s $ip -j DROP
done < /tmp/abuseip_blacklist.txt
logger "已从 AbuseIPDB 更新 $(wc -l < /tmp/abuseip_blacklist.txt) 个恶意IP"添加到 crontab,每天凌晨更新:
0 2 * * * /path/to/download-blacklist.sh
方法八:双因素认证(2FA)
即使密码或密钥泄露,2FA 也能提供最后一道防线。推荐使用 Google Authenticator。
Ubuntu 安装步骤:
sudo apt install libpam-google-authenticator
运行初始化:
google-authenticator
按提示操作,保存备用码,扫描二维码绑定手机 App。
编辑 PAM 配置:
sudo nano /etc/pam.d/sshd
添加:
auth required pam_google_authenticator.so
编辑 SSH 配置:
sudo nano /etc/ssh/sshd_config
确保:
ChallengeResponseAuthentication yes AuthenticationMethods publickey,keyboard-interactive
重启 SSH:
sudo systemctl restart sshd
下次登录时,除了密钥,还需输入手机上的 6 位验证码。
方法九:定期审计与日志分析
再好的防御也需要定期检查。建议每周审查 SSH 登录日志:
# 查看最近成功登录
last
# 查看失败登录
grep "Failed password" /var/log/auth.log | tail -20
# 统计攻击最频繁的IP
grep "Failed password" /var/log/auth.log | awk '{print $11}' | sort | uniq -c | sort -nr | head -10
也可以用 GoAccess、ELK、Graylog 等工具做可视化分析。
方法十:使用端口敲门(Port Knocking)
这是一种“隐写术”式的安全机制:SSH 端口默认关闭,只有按特定顺序“敲击”几个端口后,才临时开放 SSH。
安装 knockd:
sudo apt install knockd
配置 /etc/knockd.conf:
[options]
UseSyslog
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 10
command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 10
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
tcpflags = syn
启动服务:
sudo systemctl start knockd sudo systemctl enable knockd
客户端敲门:
knock your-server-ip 7000 8000 9000 ssh -p 2222 user@your-server-ip
完成操作后,再敲反向序列关闭端口:
knock your-server-ip 9000 8000 7000
隐蔽性极强,但操作略繁琐,适合高安全场景。
测试你的防御是否生效
部署完上述措施后,务必进行测试:
- 端口扫描测试:
nmap -p 22,2222 your-server-ip
应只看到 2222 开放(或完全无响应)。
- 暴力破解模拟(仅限自己服务器!):
hydra -l root -P /path/to/passwords.txt ssh://your-server-ip:2222 -t 4
观察是否被 Fail2ban 或 Java 工具封禁。
- 登录测试:
确保你仍能通过密钥 + 2FA 正常登录。
总结:构建纵深防御体系
没有单一方案能 100% 防御 SSH 暴力破解。最佳实践是分层防御:

每一层都是保险丝,即使某一层被绕过,下一层仍能阻止入侵。
结语
SSH 是我们管理 Linux 服务器的生命线,保护它就是保护我们的数字资产。通过本文介绍的十种方法 —— 从简单改端口,到编写 Java 自动防御程序 —— 你可以根据自身需求构建一套坚固、灵活、智能的防御体系。
记住:安全不是一次性任务,而是持续的过程。定期更新、审计、演练,才能在真正的攻击来临时从容应对。
保持警惕,安全无忧。
如果你觉得本文有帮助,欢迎分享给你的运维小伙伴!一起提升 Linux 服务器的安全水位!
本文所有代码示例均可在标准 Linux + Java 11+ 环境下运行。生产环境使用前请充分测试并做好备份。
以上就是Linux防止SSH暴力破解的多种方法的详细内容,更多关于Linux防止SSH暴力破解的资料请关注脚本之家其它相关文章!
相关文章
在Ubuntu 16.10安装mysql workbench报未安装软件包 libpng12-0错误的解决方法
这篇文章主要介绍了在Ubuntu 16.10安装mysql workbench报未安装软件包 libpng12-0错误的解决方法的相关资料,需要的朋友可以参考下2016-11-11
linux常用命令之zip/unzip命令(压缩文件/解压缩文件)详解
这篇文章主要介绍了linux常用命令之zip/unzip命令(压缩文件/解压缩文件),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2025-04-04


最新评论