🔥 BRAVE 質押池已上線 — 在錢包搜尋 BRAVE 即可委託

你的服务器正被黑客扫密码?如何设置更安全的SSH 密钥登录

    • #34384

      你买了一台 Ubuntu 服务器,或者在自己的旧电脑上装了 Ubuntu Server 当家庭服务器用。你每次登录都在终端里敲:

      ssh user@你的IP

      然后老老实实输密码。

      这个场景你大概不陌生。但你很可能不知道的是——在你敲下回车的那一刻,全世界有无数台机器正在对你的服务器做同样的事。只不过它们是在自动跑脚本,每秒尝试几十上百个常见的用户名和密码组合。

      如果你不信,现在就登录你的服务器看一下:

      sudo journalctl -u sshd | grep "Failed password" | tail -20

      或者(经典路径):

      sudo cat /var/log/auth.log | grep "Failed password" | tail -20

      你十有八九会看到一堆登录失败的记录,来自各种你听都没听过的 IP 地址。这不是你的服务器”被盯上了”——这就是互联网的常态。任何一台暴露在公网上的 SSH 服务器,从上线第一秒就在被暴力破解脚本扫描。

      那怎么办?禁用密码登录,改用 SSH 密钥认证


      一、为什么密码认证从根上就是错的

      密码认证有三个无法修补的结构性缺陷:

      1. 密码可以被暴力破解

      暴力破解不需要多高深的技术。一个简单的脚本用常见密码字典(”admin”、”123456″、”password”、”root” 等),每秒试几十个组合,配合多线程、IP 轮换,24 小时内能试几百万次。只要你的密码不够复杂——坦白说,大多数人的密码都不够复杂——终归会被试出来。

      2. 密码在传输时可能被暴露

      很多人以为 SSH 连接是加密的。是的,连接建立之后的数据传输确实加密了。但问题在于:密码是在连接建立过程中传输的。如果有人在你和服务器之间做了中间人攻击(MITM),你的密码可能在传输过程中被截获。

      3. 密码是”共享秘密”——你知道,服务器也知道,甚至别人也知道

      密码的本质是一个”你知我知”的共享秘密。凡是共享的秘密,就有泄露的风险:

      • 你可能在不同服务器上用了相同的密码,一台失守全部沦陷
      • 你可能把密码告诉了同事、家人,或者记在某个云笔记里
      • 服务器的 /etc/shadow 文件虽然存的是哈希,但如果哈希算法不够强(比如旧的 DES),可以被离线彩虹表破解

      三个问题加在一起,结论很清晰:用密码做 SSH 认证,不是”不够好”,而是”迟早出事”。


      二、SSH 密钥认证是如何工作的?用一把锁就能讲明白

      想象你有一套这样的东西:

      • 一把打开的锁——锁是开着的状态,任何人都能看、能摸、能挂到任何地方。它本身锁不住任何东西,但可以用来验证
      • 一把匹配的钥匙——能把这把锁锁上,也能把它打开。

      SSH 密钥认证的原理,和这套”锁-钥匙”机制几乎对应:

      • 公钥(public key,.pub 文件) = 那把打开的锁。你可以大大方方地把它放在服务器上、发给别人、贴到墙上——它本身不提供任何准入权限。
      • 私钥(private key,没有后缀的文件) = 那把唯一的钥匙。必须藏好,谁也不给。 谁拿到它,谁就能以你的身份登录任何放了对应公钥的服务器。

      整个登录过程是这样走的:

      你(持有私钥)                       服务器(存有你的公钥)
           │                                        │
           │───────── 1. "我是 user,我要登录" ──────→│
           │                                        │
           │←──── 2. "证明一下,请签名这段随机数据" ───│
           │                                        │
           │── 3. 用私钥签名,发回签名结果 ──────────→│
           │                                        │
           │                  4. 用公钥验证签名 ✓    │
           │←─────────────────── 登录成功 ───────────│

      关键点在于第 3 步:私钥只在你自己的电脑上被调用,它从未离开过你的机器。 发出去的只是一段签名结果。

      你给了服务器一个”零知识证明”式的回答:我证明了”我拥有私钥”这件事,而不需要把私钥本身交出去。

      相比密码认证,做一次完整的对比:

      维度密码认证密钥认证
      凭证谁保存你知道,服务器也存了(哈希)只有你持有私钥
      传输过程密码明文或哈希值在网络中传递私钥不出本地,只发签名
      暴力破解在线脚本每秒试几百次数学上不可能暴力破解私钥
      中间人攻击可能被截获密码签名无法反推私钥
      钓鱼攻击假服务器可以骗取密码假服务器无法骗取私钥签名
      日常使用每次都要输入,麻烦一次设置,之后免密
      多服务器管理记住 N 组不同密码(或重复使用)一个私钥对 N 台服务器
      撤销权限改密码(影响所有知道新密码的人)从 authorized_keys 删一条公钥即可

      三、实操:从零到一,彻底禁用密码登录

      以下操作分为四步,每一步有明确的验证点。强烈建议按顺序执行,不要跳过验证步骤。

      第一步:在你的本地机器生成密钥对

      打开你的本地终端(不是服务器的终端——是你自己电脑的终端)。

      ssh-keygen -t ed25519 -C "你的邮箱@example.com"

      参数说明:

      • -t ed25519:指定密钥类型为 Ed25519。比传统的 RSA 更快(生成和签名都在毫秒级)、更安全(同等安全强度下密钥更短),是目前 SSH 密钥的推荐选择。
      • -C "你的邮箱":一条注释,方便你日后管理多个密钥时区分。

      执行后你会看到类似这样的交互:

      Generating public/private ed25519 key pair.
      Enter file in which to save the key (/Users/你/.ssh/id_ed25519):

      直接按回车接受默认路径即可。

      然后会问你要不要设置 passphrase(稍后解释):

      Enter passphrase (empty for no passphrase):
      Enter same passphrase again:

      对于这一步,你可以选择先留空,等全部配置完成后,再回过头来给私钥加一道锁。我们会在后面的”进阶加固”一节专门讲这个。

      生成完成后,你会得到两个文件:

      • ~/.ssh/id_ed25519——私钥,绝不给任何人看
      • ~/.ssh/id_ed25519.pub——公钥,需要放到服务器上

      第二步:把公钥传送到服务器

      你有三种方式可以实现这一步。任选其一。

      方式 A(推荐,最简单):使用 ssh-copy-id

      ssh-copy-id user@你的服务器IP

      它会自动把你的公钥追加到服务器 ~/.ssh/authorized_keys 文件中。

      方式 B(手动操作,适合需要精细控制的情况):

      在本地查看公钥内容:

      cat ~/.ssh/id_ed25519.pub

      复制输出的全部内容,然后 SSH 登录服务器:

      ssh user@你的服务器IP

      在服务器上执行:

      mkdir -p ~/.ssh
      chmod 700 ~/.ssh
      echo "粘贴你复制的公钥内容" >> ~/.ssh/authorized_keys
      chmod 600 ~/.ssh/authorized_keys
      exit

      方式 C(带外注入):

      如果你是云服务器(AWS、阿里云、腾讯云等),绝大多数厂商在创建实例时允许你直接粘贴公钥。这种情况下你第一次 SSH 登录就已经是密钥认证了,不需要密码。


      ⚠️ 不管你用哪种方式,这一步都要输入一次密码。 因为服务器上还没有你的公钥,只能用密码让你进去。这是你最后一次输入密码——忍一下。

      第三步:验证密钥登录(最重要的一步)

      保持当前 SSH 会话不要断开。 打开一个新终端窗口,测试:

      ssh user@你的服务器IP

      如果一切正常,你应该直接登录成功,不需要输入任何密码。这证明密钥认证已经生效。

      如果登录失败,请检查:

      • 本地私钥路径是否正确:ls -la ~/.ssh/
      • 服务器上公钥是否写入正确:登录服务器后执行 cat ~/.ssh/authorized_keys
      • 权限是否正确(常见错误):~/.ssh 必须是 700authorized_keys 必须是 600

      在确认密钥登录可用之前,千万不要关闭你原有的 SSH 会话。 这是最后一道安全网——如果密钥登录有问题,你还可以通过旧会话排错。一旦你两边都断了,而密钥又不好使,你就会被锁在门外。

      第四步:禁用密码登录(最后一刀)

      密钥确认能用之后,接下来就是把密码认证彻底关掉。

      在服务器上编辑 SSH 配置文件:

      sudo vim /etc/ssh/sshd_config

      如果没有 vim,用 sudo nano /etc/ssh/sshd_config 也一样。

      找到并修改(或新增)以下三行:

      PasswordAuthentication no
      PubkeyAuthentication yes
      ChallengeResponseAuthentication no

      保存退出,然后重启 SSH 服务:

      sudo systemctl restart sshd

      现在再试一次新开终端的 SSH 连接。 你应该只能通过密钥登录,密码方式已经彻底失效。


      如果你对 vim 不熟悉,这里有个偷懒的方法:用 sed 直接改。

      # 备份原配置(建议养成习惯)
      sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
      
      # 修改配置
      sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
      sudo sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
      sudo sed -i 's/^#\?ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config
      
      # 重启服务
      sudo systemctl restart sshd

      四、”服务器会接受任意密钥吗?”

      这个问题非常好,也是很多初次接触密钥认证的人最大的困惑。

      答案很明确:不,服务器只接受你明确授权过的公钥。

      当你禁用密码登录后,服务器只认 ~/.ssh/authorized_keys 这个文件里的公钥。这个文件里有什么公钥,才允许什么人登录——一条不多,一条不少。 不是你拿着任意一把私钥就能登的。

      而且这个文件的修改权限是受限的:

      • 只有文件所有者(也就是你登录的那个用户)和 root 才能写它
      • 文件权限应该是 600(只有所有者可读写)
      • 父目录 ~/.ssh 权限应该是 700(只有所有者可读可写可执行)

      所以完整的安全链条是这样的:

      你有服务器的账号密码 → 你才能 SSH 进去 → 才能修改 authorized_keys
      → 才能添加你的公钥 → 才能用密钥登录

      或者(从攻击者的视角):

      攻击者没有你的私钥 → 无法签名 → 无法通过密钥认证 → 拒绝登录
      (密码认证已禁用,暴力破解通道彻底关闭)

      根本不存在”任意密钥都能登”的情况。 你往 authorized_keys 里写谁的公钥,谁才能登。你写你自己的,就只有你能登。你要撤销某个人的访问权限,从他对应的服务器上删掉他那一行公钥就行了——不用改密码、不用通知其他人、不影响其他有效密钥。

      至于私钥本身的安全性:Ed25519 密钥是 256 位随机数。暴力破解一个 256 位私钥需要的计算量,用目前全人类所有计算机加在一起算到宇宙热寂也解不出来。这个”安全”不是”比较安全”或”一般安全”,而是数学上可行性的边界之外的安全


      五、进阶加固:让你的密钥更安全

      5.1 给私钥加一道口令(passphrase)

      回想第一步生成密钥时被问到的那个”passphrase”——它不是用来登录服务器的,而是用来加密你本地的私钥文件的。

      没有 passphrase:私钥文件以明文存放在 ~/.ssh/id_ed25519 里。谁拿到这个文件,谁就能直接用它登录你的服务器。

      有 passphrase:私钥文件是加密存储的。别人即使拿到了你的私钥文件,没有口令也无法使用。私钥的泄漏就不再是灾难级别的——攻击者还得多过一道关。

      如果你生成密钥时没设 passphrase,现在可以补上:

      ssh-keygen -p -f ~/.ssh/id_ed25519

      它会提示你输入旧的口令(留空表示没有)和新口令。

      设置之后,每次使用这个私钥(SSH 登录、Git 推送等)都需要输入一次口令。如果你觉得麻烦,可以用 ssh-agent 把解密的私钥暂存在内存中:

      eval "$(ssh-agent -s)"
      ssh-add ~/.ssh/id_ed25519

      输入一次 passphrase 后,整个终端会话期间,再次使用这个私钥的时候不会再问你要口令。关机或退出终端后,缓存自动清除。

      5.2 管理多个密钥对

      你可以针对不同的用途生成不同的密钥对:

      # 一台密钥用于个人服务器
      ssh-keygen -t ed25519 -C "personal" -f ~/.ssh/id_ed25519_personal
      
      # 另一台用于工作服务器
      ssh-keygen -t ed25519 -C "work" -f ~/.ssh/id_ed25519_work

      然后在 ~/.ssh/config 中配置每个主机使用哪个密钥:

      Host personal-server
          HostName 192.168.1.100
          User kola
          IdentityFile ~/.ssh/id_ed25519_personal
      
      Host work-server
          HostName work.example.com
          User kola
          IdentityFile ~/.ssh/id_ed25519_work

      这样,ssh personal-serverssh work-server 会自动使用对应的密钥。

      5.3 定期轮换密钥

      虽然私钥泄露概率极低,但良好的安全习惯是定期更换。建议——不需要太频繁,一年一次足够了:

      1. 生成新密钥对
      2. 把新公钥上传到服务器(追加到 authorized_keys,保留旧公钥作为后备)
      3. 验证新密钥能登录
      4. 删除旧公钥

      六、万一出了事怎么办

      场景一:我把自己锁在外面了

      这是 SSH 密钥迁移过程中最常见的事故。如果你禁用了密码登录但密钥认证又没配置好,你就被拒之门外了。

      救援方案(取决于你的环境):

      • 云服务器:几乎所有云厂商都提供 VNC 控制台或”救援模式”,可以从网页端直接登录服务器的终端(不需要 SSH)。登录进去之后修正配置。
      • 本地服务器/树莓派:接上显示器和键盘,物理登录。
      • 家庭服务器(无显示器):重启服务器,在引导过程中进入单用户模式,或者从 U 盘启动 Live 系统挂载硬盘修改。

      修改 /etc/ssh/sshd_config,临时把 PasswordAuthentication no 改回 yes,重新检查密钥配置,认证通了之后再次关掉。

      场景二:我的私钥丢了

      私钥丢了 = 你再也登不上服务器。没有找回途径,没有密码重置功能,没有客服电话。

      这不是 Bug,这是设计。 如果私钥可以”找回”,那别人也能找。

      预防方案只有一个:备份。

      # 用加密压缩备份私钥
      tar czf ssh-keys-backup.tar.gz ~/.ssh/
      gpg -c ssh-keys-backup.tar.gz  # 加密

      然后把加密后的文件存到:

      • 密码管理器(1Password、Bitwarden 等)
      • 离线的 U 盘(物理隔离)
      • 打印成二维码存到保险柜(如果你真的很 paranoid)

      场景三:私钥被泄露了

      1. 立即从所有服务器的 authorized_keys 中删除对应的公钥行
      2. 生成一对新密钥
      3. 分析泄露路径,确保同样的错误不会发生第二次
      4. 永远不要把你的私钥发给任何人,永远不要粘贴到网页里,永远不要上传到 GitHub

      七、补充:一些扫尾操作

      如果你有多个用户

      每个用户各自在自己的目录下执行同样的流程(~/.ssh/authorized_keys 是每个用户独立的)。

      Root 登录建议直接用 sudo 而不是直接 SSH 登录,做法是禁用 root 的 SSH 登录:

      PermitRootLogin no

      日常用一个普通用户登录,需要提权时用 sudo

      如果你改了 SSH 端口

      如果你把默认的 22 端口改了(建议改,能减少 99% 的暴力扫描日志),在连接时加上 -p 参数:

      ssh -p 端口号 user@服务器IP

      或者在 ~/.ssh/config 中配置:

      Host my-server
          HostName 你的IP
          Port 端口号
          User user

      验证配置是否生效

      你可以用 SSH 的调试模式验证密码登录是否确实被禁用:

      ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no user@服务器IP

      如果配置正确,这条命令应该返回 Permission denied (publickey)——服务器拒绝了密码认证,只接受密钥。这正是你想要的效果。


      八、常见问题速查

      问题回答
      密钥登录安全吗?安全。私钥 256 位随机数,数学上不可暴力破解。
      服务器会接受谁的密钥?只接受 ~/.ssh/authorized_keys 里列出的公钥。
      Mac/Linux 怎么操作?本文步骤适用于所有类 Unix 系统(macOS、Linux、WSL)。
      Windows 怎么操作?推荐用 PowerShell 或 Terminal,也支持 OpenSSH 客户端。
      设置需要多久?10 分钟。
      以后还能再加别人吗?可以,把新人的公钥追加到 authorized_keys 即可。
      私钥能不能用 U 盘带着走?可以,但不推荐。私钥应该留在你自己信任的设备上。
      passphrase 忘了怎么办?私钥作废,重新生成一对。
      多个设备登录同一台服务器?每个设备生成自己的密钥对,各自的公钥都加到服务器 authorized_keys
      重装系统后怎么登录?如果你备份了私钥,把备份的私钥恢复回来即可。如果没备份,通过带外方式重新加公钥。

      九、所以说到底

      前面讲了很多原理和步骤,但对小白来说,核心记住三句话就够了:

      1. 密码认证不安全——你的服务器每天都在被暴力扫描,迟早被攻破。
      2. 密钥认证从根本上解决了这个问题——私钥不出本地,数学上不可破解,服务器只认你授权过的公钥。
      3. 设置很简单——三步:生成密钥 → 推送公钥 → 关闭密码。10 分钟,一劳永逸。

      花 10 分钟做完这件事之后,你可以把”SSH 安全”这个担忧彻底从脑子里删除——暴力破解不再关你的事,中间人攻击不再关你的事,密码泄露不再关你的事。

      你的服务器会感谢你的。确切地说,是未来的你自己会感谢现在的你。

  • 抱歉,回覆主題必需先登入。