Why SSH Key Authentication Beats Password Authentication — What Is Actually Being Proven
Why SSH Key Authentication Beats Password Authentication — What Is Actually Being Proven
为什么 SSH 密钥认证优于密码认证——它究竟证明了什么
“Use key-based auth instead of a password” is common advice for connecting to a server over SSH. But what exactly does key authentication prove, and how does that differ from what a password proves? This post works through the mechanics of what each method is actually demonstrating. “使用基于密钥的认证而不是密码”是连接 SSH 服务器时的常见建议。但密钥认证究竟证明了什么?它与密码认证有何不同?本文将深入探讨这两种方法在机制上究竟证明了什么。
Authentication is an act of proof. Note: authentication is the process of confirming that whoever just connected really is who they claim to be, based on some kind of evidence. It’s often confused with authorization, which is a separate concept covering what a confirmed identity is then allowed to do. Every authentication scheme ultimately comes down to a choice: what evidence counts as proof of identity? Password authentication and public-key authentication answer that question in fundamentally different ways. 认证是一种证明行为。注:认证是基于某种证据,确认连接者确实是其所声称身份的过程。它常与“授权”混淆,授权是一个独立的概念,涵盖了已确认身份的用户被允许执行哪些操作。每种认证方案最终都归结为一个选择:什么样的证据可以作为身份证明?密码认证和公钥认证以截然不同的方式回答了这个问题。
Password authentication: proving you know a secret by stating it
密码认证:通过陈述秘密来证明你知道它
Password authentication is structurally simple. Client and server both know the same secret in advance. The client sends that secret to the server, the server compares it (usually after hashing) against its own stored record, and a match means “authenticated.” The weakness lives in that structure itself: the secret gets stated, directly, as part of the exchange. 密码认证在结构上很简单。客户端和服务器预先都知道同一个秘密。客户端将该秘密发送给服务器,服务器将其(通常在哈希处理后)与自己存储的记录进行比较,匹配则意味着“认证成功”。其弱点恰恰存在于这种结构本身:秘密作为交换的一部分被直接陈述了出来。
SSH’s transport is encrypted, so eavesdropping isn’t the main concern — the problems lie elsewhere: SSH 的传输是加密的,因此窃听不是主要担忧——问题在于其他方面:
- Passwords people can actually remember tend to have far lower entropy than machine-generated random values, which makes them susceptible to dictionary and brute-force attacks.
- 人们能记住的密码通常比机器生成的随机值具有低得多的熵,这使得它们容易受到字典攻击和暴力破解攻击。
- Reusing the same password across services turns one leak into an entry point for every other server that shares it (credential stuffing).
- 在不同服务间重复使用同一个密码,会导致一次泄露成为所有共享该密码服务器的入口(撞库攻击)。
- If the value stored server-side (a hash) ever leaks, it opens the door to offline brute-forcing.
- 如果服务器端存储的值(哈希值)泄露,就会为离线暴力破解打开大门。
In short, password authentication proves “I know the secret” by transmitting the secret — or a value derived directly from it. 简而言之,密码认证通过传输秘密(或直接从秘密派生的值)来证明“我知道这个秘密”。
Public-key authentication: proving possession without ever handing over the secret
公钥认证:在不交出秘密的情况下证明拥有权
Public-key authentication proves identity a completely different way, built on asymmetric cryptography — a public/private key pair. Note: asymmetric cryptography uses two mathematically linked but distinct keys: a private key for signing or decrypting, and a public key for verifying or encrypting. Deriving one key from the other is designed to be computationally infeasible. 公钥认证以一种完全不同的方式证明身份,它建立在非对称加密的基础上——即公钥/私钥对。注:非对称加密使用两个数学上相关但不同的密钥:用于签名或解密的私钥,以及用于验证或加密的公钥。从一个密钥推导出另一个密钥在计算上被设计为不可行的。
Only the public key gets registered on the server ahead of time. The private key never leaves the client machine. During authentication, the server sends a random challenge value; the client signs it with the private key and sends the signature back. The server verifies that signature using the registered public key — a valid signature is proof that the client holds the matching private key, without the private key itself ever being transmitted. 只有公钥会提前在服务器上注册。私钥永远不会离开客户端机器。在认证过程中,服务器发送一个随机挑战值;客户端用私钥对其进行签名并将签名发回。服务器使用注册的公钥验证该签名——有效的签名证明了客户端持有匹配的私钥,而私钥本身从未被传输过。
That’s the decisive difference. Password authentication sends the secret itself over the wire. Public-key authentication only ever sends proof of possession (a signature), and by design, that proof can’t be reverse-engineered back into the private key. 这就是决定性的区别。密码认证在网络上传输秘密本身。公钥认证只发送拥有权的证明(签名),并且根据设计,该证明无法被反向工程还原为私钥。
What happens if server-side data leaks
如果服务器端数据泄露会发生什么
The difference becomes stark when you consider what a leak of server-side authentication data actually costs. With password authentication, what the server stores is a password hash. If that hash leaks, an attacker can brute-force it offline — and a weak password will eventually fall. With public-key authentication, what the server stores is the public key itself — information that’s meant to be public by definition. A leak of that data costs nothing, because there was never a secret sitting on the server side to begin with. Not putting anything secret on the server is the core of why public-key authentication is safer. 当你考虑服务器端认证数据泄露的代价时,这种差异就变得非常明显。对于密码认证,服务器存储的是密码哈希。如果哈希泄露,攻击者可以离线暴力破解——弱密码最终会被攻破。对于公钥认证,服务器存储的是公钥本身——根据定义,这些信息本就是公开的。泄露这些数据没有任何代价,因为服务器端从一开始就没有存储任何秘密。不在服务器上放置任何秘密,正是公钥认证更安全的核心原因。
A real example: narrowing authentication down to one explicit key
一个真实的例子:将认证范围缩小到一个明确的密钥
This app’s SSH connection code (core/ssh_utils.py::get_ssh_connection()) doesn’t just use public-key authentication — it also deliberately narrows which key gets tried.
该应用的 SSH 连接代码 (core/ssh_utils.py::get_ssh_connection()) 不仅使用了公钥认证,还特意缩小了尝试使用的密钥范围。
connect_kwargs = {'look_for_keys': False, 'allow_agent': False}
if 'ssh_key_path' in site and site['ssh_key_path']:
...
pkey = load_any_ssh_key(key_path, passphrase=ssh_passphrase)
connect_kwargs['pkey'] = pkey
return Connection(
host=site['ssh_host'],
user=site['ssh_user'],
port=site.get('ssh_port', 22),
connect_timeout=15,
connect_kwargs=connect_kwargs
)
look_for_keys=False and allow_agent=False turn off the SSH client library’s (paramiko’s) default behavior of trying every key under ~/.ssh/ and every key registered in a running SSH agent. Only the one key specified in that site’s configuration is passed in explicitly via pkey.
look_for_keys=False 和 allow_agent=False 关闭了 SSH 客户端库(paramiko)的默认行为,即尝试 ~/.ssh/ 下的每个密钥以及运行中的 SSH 代理中注册的每个密钥。只有该站点配置中指定的那个密钥通过 pkey 显式传入。
This isn’t a matter of taste. In an environment managing many sites, leaving automatic key discovery on means a single connection attempt can end up trying several candidate keys back to back — quickly running into OpenSSH’s MaxAuthTries limit (6 by default). If the server side has protection like fail2ban or OpenSSH’s PerSourcePenalties, a legitimate administrator can end up temporarily blocking their own IP by accident. Pinning “this connection uses exactly this one key” isn’t about the strength of authentication itself — it’s a structural fix for a pitfall specific to managing many sites at once.
这并非个人喜好问题。在管理多个站点的环境中,开启自动密钥发现意味着单次连接尝试可能会连续尝试多个候选密钥,从而迅速达到 OpenSSH 的 MaxAuthTries 限制(默认为 6 次)。如果服务器端有 fail2ban 或 OpenSSH 的 PerSourcePenalties 等保护机制,合法的管理员可能会意外地暂时封锁自己的 IP。锁定“此连接仅使用这一个密钥”并非为了增强认证本身的强度,而是针对同时管理多个站点时常见陷阱的一种结构性修复。
Turning password login off entirely, server-side
在服务器端彻底关闭密码登录
Beyond using key auth on the client, it’s also standard practice to set PasswordAuthentication no in sshd_config, so the server won’t even accept password login attempts. That’s a well-documented, standard OpenSSH setting — not some private operational trick — and it follows a simple principle: eliminate the weaker authentication path rather than just discouraging its use. No matter how strong a key is, if the same account can still be reached with a password, that’s where an attacker will aim.
除了在客户端使用密钥认证外,在 sshd_config 中设置 PasswordAuthentication no 也是标准做法,这样服务器甚至不会接受密码登录尝试。这是一个有据可查的标准 OpenSSH 设置,而非什么私有的操作技巧,它遵循一个简单的原则:消除较弱的认证路径,而不是仅仅劝阻其使用。无论密钥有多强,如果同一个账户仍然可以通过密码访问,那就会成为攻击者的目标。
How this connects to choosing a key type
这与选择密钥类型有何关联
An earlier post, SSH Key Types: RSA vs. ED25519 vs. ECDSA — Which Should You Use?, covered which key algorithm to pick once you’re using public-key authentication. This post is one layer earlier in that decision: why choose public-key. 之前的一篇文章《SSH 密钥类型:RSA vs. ED25519 vs. ECDSA — 你应该使用哪种?》介绍了在使用公钥认证时该选择哪种算法。而本文处于该决策的前一个层面:为什么要选择公钥认证。