Linux防止SSH暴力破解的多种方法

 更新时间:2026年04月27日 08:40:09   作者:知远漫谈  
SSH(Secure Shell)是 Linux 系统管理员远程管理服务器最常用的协议,它安全、稳定、功能强大,但也正因为如此,SSH 服务往往成为黑客攻击的首要目标,尤其是暴力破解,在本文中,我们将深入探讨如何从多个维度有效防御 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% 的低级扫描。

操作步骤:

  1. 编辑 SSH 配置文件:
sudo nano /etc/ssh/sshd_config
  1. 找到 Port 22,修改为其他端口(比如 2222):
Port 2222
  1. 重启 SSH 服务:
sudo systemctl restart sshd
  1. 别忘了在防火墙放行新端口:
sudo ufw allow 2222/tcp

注意:修改端口前请确保你有其他方式访问服务器(如控制台),避免把自己锁在外面!

方法二:禁用 root 登录

root 是 Linux 的超级用户,也是攻击者的首要目标。禁用其直接登录可大幅提升安全性。

操作步骤:

编辑 /etc/ssh/sshd_config

PermitRootLogin no

然后重启 SSH:

sudo systemctl restart sshd

之后,你需要先用普通用户登录,再通过 su -sudo 提权。

方法三:启用密钥认证,禁用密码登录

密码容易被暴力破解,而 SSH 密钥几乎不可能被暴力攻破(前提是私钥保管好)。

操作步骤:

  1. 在本地生成密钥对(如果还没有):
ssh-keygen -t rsa -b 4096
  1. 将公钥上传到服务器:
ssh-copy-id -p 2222 user@your-server-ip
  1. 修改 /etc/ssh/sshd_config
PasswordAuthentication no
PubkeyAuthentication yes
  1. 重启 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();
    }
}

如何运行?

  1. 编译打包成 JAR:
mvn clean package
  1. 后台运行(需 root 权限,因为要执行 iptables):
sudo java -jar ssh-monitor.jar
  1. 查看日志:
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
Emerging Threats

示例:定时拉取 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

隐蔽性极强,但操作略繁琐,适合高安全场景。

测试你的防御是否生效

部署完上述措施后,务必进行测试:

  1. 端口扫描测试
nmap -p 22,2222 your-server-ip

应只看到 2222 开放(或完全无响应)。

  1. 暴力破解模拟(仅限自己服务器!):
hydra -l root -P /path/to/passwords.txt ssh://your-server-ip:2222 -t 4

观察是否被 Fail2ban 或 Java 工具封禁。

  1. 登录测试

确保你仍能通过密钥 + 2FA 正常登录。

总结:构建纵深防御体系

没有单一方案能 100% 防御 SSH 暴力破解。最佳实践是分层防御

每一层都是保险丝,即使某一层被绕过,下一层仍能阻止入侵。

结语

SSH 是我们管理 Linux 服务器的生命线,保护它就是保护我们的数字资产。通过本文介绍的十种方法 —— 从简单改端口,到编写 Java 自动防御程序 —— 你可以根据自身需求构建一套坚固、灵活、智能的防御体系。

记住:安全不是一次性任务,而是持续的过程。定期更新、审计、演练,才能在真正的攻击来临时从容应对。

保持警惕,安全无忧。

如果你觉得本文有帮助,欢迎分享给你的运维小伙伴!一起提升 Linux 服务器的安全水位!

本文所有代码示例均可在标准 Linux + Java 11+ 环境下运行。生产环境使用前请充分测试并做好备份。

以上就是Linux防止SSH暴力破解的多种方法的详细内容,更多关于Linux防止SSH暴力破解的资料请关注脚本之家其它相关文章!

相关文章

  • Linux Shell里面生成随机数的一些思路分析

    Linux Shell里面生成随机数的一些思路分析

    这篇文章主要介绍了Linux Shell里面生成随机数的一些思路分析,需要的朋友可以参考下
    2016-07-07
  • 在Ubuntu 16.10安装mysql workbench报未安装软件包 libpng12-0错误的解决方法

    在Ubuntu 16.10安装mysql workbench报未安装软件包 libpng12-0错误的解决方法

    这篇文章主要介绍了在Ubuntu 16.10安装mysql workbench报未安装软件包 libpng12-0错误的解决方法的相关资料,需要的朋友可以参考下
    2016-11-11
  • Centos7安装redis6.2.6全过程

    Centos7安装redis6.2.6全过程

    本文介绍了在CentOS7上安装Redis的过程,包括下载、安装依赖(gcc)、解压编译安装、修改配置、启动服务及自启配置等步骤
    2026-04-04
  • 在Linux中如何一次重命名多个文件详解

    在Linux中如何一次重命名多个文件详解

    这篇文章主要给大家介绍了关于在Linux中如何一次重命名多个文件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • linux定时top、netstat输出到文件方式

    linux定时top、netstat输出到文件方式

    这篇文章主要介绍了linux定时top、netstat输出到文件方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-08-08
  • Linux管理和清理日志文件的有效方法

    Linux管理和清理日志文件的有效方法

    在现代系统管理中,日志文件的管理是一个重要而复杂的任务,日志文件不仅记录了系统运行状态,还可以帮助我们排查问题、分析性能、进行审计等,文将详细介绍在 Linux 系统中如何管理和清理日志文件,包括按时间批量删除日志、保留日志文件以及其他有效的日志管理策略
    2024-10-10
  • linux常用命令之zip/unzip命令(压缩文件/解压缩文件)详解

    linux常用命令之zip/unzip命令(压缩文件/解压缩文件)详解

    这篇文章主要介绍了linux常用命令之zip/unzip命令(压缩文件/解压缩文件),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • Mac本地文件上传到CentOS云服务器方法

    Mac本地文件上传到CentOS云服务器方法

    Mac本地文件上传到CentOS中,可以使用scp指令可以完成。这篇文章给大家介绍了Mac本地文件上传到CentOS云服务器方法,需要的朋友跟随脚本之家小编一起看看吧
    2018-06-06
  • Apache Iceberg 底层数据查询原理解析

    Apache Iceberg 底层数据查询原理解析

    Apache Iceberg是一个开源表格格式,用于大型分析数据集,本文主要介绍了如何通过快照、Manifest文件和元数据文件查询Iceberg表的数据,通过解析元数据文件获取当前表的快照ID,进而读取对应的Avro文件和Manifest文件中的Parquet数据文件,感兴趣的朋友一起看看吧
    2024-09-09
  • linux服务器操作系统有哪些

    linux服务器操作系统有哪些

    很多朋友在选择linux系统的时候会不知道选择什么系统,这里为大家简单介绍一下
    2023-08-08

最新评论