⏱️ Reading time: 13 min

Every time you run ssh user@server to administer a remote machine, you’re relying on a protocol designed in 1995 by a Finnish researcher fed up with seeing passwords travel in plain text over telnet. Thirty years later, the SSH protocol remains the default method for administering Linux servers.

📑 En este artículo
  1. TL;DR
  2. What is the SSH protocol?
  3. Why encrypted remote access matters
  4. How SSH works internally: key exchange and authentication
    1. The SSH transport layer: how the encrypted channel is built
    2. SSH authentication: public key, password, or certificates
  5. Practical examples with SSH
  6. Getting started
  7. Real use cases
  8. Common mistakes and best practices
  9. Comparison with alternatives
  10. Going deeper
  11. Frequently Asked Questions
    1. What’s the difference between SSH and Telnet?
    2. How do I generate a secure SSH key pair?
    3. Is it necessary to disable password authentication in SSH?
    4. What is agent forwarding in SSH and when is it risky?
    5. Does SSH replace a VPN?
  12. References

It’s not just a remote terminal: it’s the foundation of how Git transfers code, how Ansible configures servers, and how millions of CI/CD pipelines deploy without exposing a single password over the network.

TL;DR

  • SSH encrypts every command and password between your terminal and the remote server from the very first key exchange.
  • Diffie-Hellman negotiates an ephemeral session key before a single byte of authentication data travels.
  • An ed25519 key pair replaces the password and blocks dictionary brute-force attacks.
  • ssh-agent and ControlMaster reuse an already authenticated connection so the exchange doesn’t repeat on every command.
  • Centralizing access with a bastion host avoids exposing internal servers directly to the internet.

What is the SSH protocol?

The SSH protocol is a cryptographic network protocol that allows you to open a terminal session, transfer files, or forward ports between two machines through an encrypted, authenticated channel. It replaces telnet, rlogin, and rsh, which sent credentials with no protection whatsoever.

Its full name, Secure Shell, comes from the fact that it originally just replaced Unix’s insecure remote shell. Today’s current standard, known as SSH-2 and described in RFC 4253, covers much more than a terminal: file copying via SFTP, arbitrary port forwarding, and even mounting remote filesystems with SSHFS. By default it listens on port 22, though any administrator can change that.

Tatu Ylönen, a researcher at the Helsinki University of Technology, created the first version in 1995 after a password-sniffing attack hit his university network. The implementation that ended up dominating the market is OpenSSH, developed by the OpenBSD project since 1999 and now preinstalled on practically every Linux distribution, on macOS, and, since 2018, on Windows too.

Why encrypted remote access matters

The SSH protocol became the de facto standard because it solves three problems at once: confidentiality, since no one intercepting the traffic sees passwords or commands; integrity, since any alteration of a packet in transit is detected and cuts the connection; and mutual authentication, since the server also proves its identity to the client, not just the other way around.

That combination made it an almost universal compliance requirement: standards like PCI-DSS require encrypted administrative access to any system that processes payments, and a typical SOC 2 audit checks that service account keys rotate and that no single key is shared across multiple operators. But its real weight is in automation: Git uses SSH to authenticate every push without asking for a password, Ansible runs its modules by connecting over SSH to each host in the inventory, and most GitHub Actions or GitLab CI runners deploy to production with a single-purpose SSH key.

OpenSSH has been the reference implementation of the protocol since the OpenBSD project released it in 1999. Foto de Albert Stoynov en Unsplash

How SSH works internally: key exchange and authentication

The SSH transport layer: how the encrypted channel is built

Before the client sends a single credential, SSH negotiates a secure channel in what’s known as the transport layer. The client opens a TCP connection to port 22, both ends exchange their protocol version and the list of algorithms they support (symmetric encryption, hash function for integrity, key exchange algorithm), and agree on which to use.

With the algorithms already chosen, client and server perform a Diffie-Hellman exchange: each generates a pair of ephemeral values, combines them mathematically, and both arrive at the same shared secret without ever transmitting it over the network. That secret derives the symmetric key that encrypts the rest of the session. The server signs that exchange with its host key (RSA, ed25519, or another) to prove it is who it claims to be; the client verifies that signature against the fingerprint stored in ~/.ssh/known_hosts or, on the first connection, accepts it under the trust-on-first-use (TOFU) model.

sequenceDiagram
    participant C as Client
    participant S as Server
    C->>S: opens TCP connection to port 22
    S-->>C: announces version and supported algorithms
    C->>S: picks algorithms and starts Diffie-Hellman
    Note over C,S: an ephemeral session key is derived
    S-->>C: signs the exchange with its host key
    C->>S: sends its public key to authenticate
    S-->>C: verifies the signature and grants access

SSH authentication: public key, password, or certificates

Once the channel is encrypted, authentication itself begins. With public key authentication, the client proves to the server that it holds the private key matching a public key already registered in ~/.ssh/authorized_keys, by signing a challenge the server verifies; the private key never leaves the client’s machine. With password authentication, the text travels inside the already encrypted tunnel, but it’s still vulnerable to brute force if the server doesn’t limit attempts. With certificates, an authority signs user and host keys, so a server trusts any key signed by that authority without needing to maintain a manual list of authorized keys.

flowchart LR
    A["Client attempts to authenticate"] --> B{"Is there an accepted public key?"}
    B -->|"Yes"| C["Public key authentication"]
    B -->|"No"| D{"Is password auth enabled on the server?"}
    D -->|"Yes"| E["Requests password over the encrypted channel"]
    D -->|"No"| F["Connection rejected"]
💡 Tip: Prefer ed25519 keys over RSA: they’re shorter, faster to verify, and GitHub has recommended them as the default option for years.

Practical examples with SSH

Generating a key pair is the first step to stop depending on passwords:

$ ssh-keygen -t ed25519 -C "ana@laptop"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/ana/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ana/.ssh/id_ed25519
Your public key has been saved in /home/ana/.ssh/id_ed25519.pub

The command creates two files: id_ed25519 (private, never shared) and id_ed25519.pub (public, the one you’ll copy to each server). With the key generated, copying it to the server is a single command:

$ ssh-copy-id -i ~/.ssh/id_ed25519.pub ana@web-server01
Number of key(s) added: 1

Now try logging into the machine, with: "ssh 'ana@web-server01'"
and check to make sure that only the key(s) you wanted were added.

From there, ssh ana@web-server01 logs in without asking for a password. To avoid repeating the user and host every time, ~/.ssh/config stores shortcuts:

Host web01
    HostName web-server01.internal-network.local
    User ana
    IdentityFile ~/.ssh/id_ed25519
    ProxyJump bastion

Host bastion
    HostName bastion.internal-network.local
    User ana
    IdentityFile ~/.ssh/id_ed25519

With that configuration, ssh web01 connects first to the bastion and from there jumps to web-server01 in a single command, without having to authenticate twice by hand.

Getting started

On Linux and macOS, the OpenSSH client and server come preinstalled or are an apt install openssh-server away; on Windows 10 (build 1809 onward) and Windows 11, the OpenSSH client is an optional feature enabled by default. These steps assume a Linux server with systemd and a Linux or macOS client:

  1. Generate the key pair: ssh-keygen -t ed25519 -C "your_user@your_machine".
  2. Copy the public key to the server: ssh-copy-id user@server.
  3. Confirm you can log in without a password: ssh user@server.
  4. On the server, edit /etc/ssh/sshd_config and set PasswordAuthentication no and PermitRootLogin no.
  5. Apply the change: sudo systemctl restart sshd.
  6. Verify that the active configuration actually rejects passwords: sudo sshd -T | grep passwordauthentication should print passwordauthentication no.

Real use cases

A repository cloned with git clone [email protected]:user/repo.git uses SSH to authenticate every push without asking for a username or password, as long as the public key is registered on the account. Transfer tools like scp and rsync -e ssh reuse the same encrypted channel to move complete files or just the differences.

Port forwarding is another common use: ssh -L 5432:localhost:5432 ana@bastion exposes on your local machine a database that’s only accessible from the internal network, without opening that port to the internet or installing a VPN for a one-off task. ssh -D 1080 ana@bastion serves a similar purpose, but creates a SOCKS proxy that routes any application through the tunnel.

Ansible relies on SSH for every task it runs against a server inventory; the Remote-SSH extension in Visual Studio Code opens a full development environment inside a remote server by reusing the same connection; and most continuous integration runners deploy to production with a single-purpose key, restricted in authorized_keys to one allowed command.

SFTP transfers travel inside the same encrypted channel that an SSH session opens.

Common mistakes and best practices

Permission denied (publickey) almost always means incorrect permissions, not a bad key: the server ignores authorized_keys if the ~/.ssh directory doesn’t have 700 permissions or the file doesn’t have 600. Fix it with chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys.

The warning WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED isn’t a cosmetic error: it means the server’s fingerprint changed, which could be a legitimate restart with a new key or a man-in-the-middle attack. Confirm the fingerprint through a different channel before removing the old entry from known_hosts with ssh-keygen -R.

Another common error, especially with many keys loaded in the agent, is Too many authentication failures: the server cuts the connection after a limited number of attempts because the client offers every key in the agent before trying the one specified in IdentityFile. Adding IdentitiesOnly yes to the host entry in ~/.ssh/config forces it to use only the key configured for that host.

1024-bit RSA keys and DSA keys are already deprecated in modern OpenSSH for being cryptographically weak; migrate to ed25519 or, if you need compatibility with old systems, to RSA with at least 3072 bits. And the most silent mistake of all is editing sshd_config and forgetting to restart the service: changes don’t take effect until the restart, so running sudo sshd -T after every change avoids falsely confirming that something was applied.

Comparison with alternatives

OptionEncryptionWhen to use itLimitation
SSHEnd-to-end encryption from the initial exchangeRemote terminal, tunnels, Git, SFTPRequires a stable TCP connection to the configured port
TelnetNone, plain textOnly isolated labs with no external networkAnyone intercepting the traffic sees the credentials
MoshUses SSH for the initial login, then encrypts over UDPUnstable mobile or satellite connectionsDoesn’t support port forwarding or X11
VPN + remote desktopEncrypts the whole network, not just one sessionAccess to full graphical desktopsMore complex to deploy and maintain than an SSH key

Going deeper

SSH certificates solve the scaling problem of loose keys: instead of distributing each public key to every server, a certificate authority signs user keys with ssh-keygen -s ca_key -I user_id -n ana -V +52w id_ed25519.pub, and each server only needs to trust that authority’s public key. The certificate also expires on its own, something a key with no expiration date never does by itself.

SSH agent forwarding (enabled with ssh -A) lets an intermediate server use your private key to authenticate on a third server, without the key ever physically traveling. It’s convenient for jumping through a bastion host, but if that bastion is compromised, any process with sufficient privileges there can ask the agent to sign on your behalf while the session is active.

⚠️ Watch out: Only enable agent forwarding on bastions you trust as much as your own laptop; otherwise, use ProxyJump without forwarding, as in the configuration example above.
flowchart TD
    A["Your laptop"] --> B["Bastion host (jump box)"]
    B --> C["Internal server web01"]
    B --> D["Internal server db01"]
    subgraph "Private network with no internet access"
    C
    D
    end

For repeated sessions to the same host, ControlMaster reuses the TCP connection and key exchange already made: by adding ControlMaster auto and ControlPath ~/.ssh/sockets/%r@%h-%p to ~/.ssh/config, the second connection and subsequent ones open in milliseconds because they share the first connection’s socket. SSHFS, meanwhile, mounts a remote directory as if it were local, reusing the same encrypted session: useful for editing files on a server with graphical desktop tools, though every file operation pays the round-trip network latency. Recent OpenSSH versions also offer a hybrid post-quantum key exchange, designed so that traffic encrypted today doesn’t become readable within a decade if someone recorded it and later gets access to a quantum computer capable of breaking classical Diffie-Hellman.

Your next step: generate an ed25519 key pair on your machine, copy the public one to a test server with ssh-copy-id, and confirm with ssh -v that the line Authentication succeeded (publickey) appears before you’re prompted for any password.

📬 Get new articles by email

We only email about big articles (1-2 a month).

Frequently Asked Questions

What’s the difference between SSH and Telnet?

SSH encrypts the entire session, including credentials; Telnet sends everything in plain text, so anyone with network access can read passwords and commands with a simple packet sniffer.

How do I generate a secure SSH key pair?

With ssh-keygen -t ed25519: it’s the key type recommended today for being fast to verify and resistant to known attacks against short RSA keys.

Is it necessary to disable password authentication in SSH?

It’s not mandatory, but it is recommended on any server exposed to the internet: it completely eliminates the risk of brute force against passwords, even weak or reused ones.

What is agent forwarding in SSH and when is it risky?

It’s the feature that lets you use your local private key to authenticate on a third server through an intermediate one. It’s risky if the intermediate server is compromised, because anyone with privileges there can ask the agent to sign on your behalf.

Does SSH replace a VPN?

Not entirely: SSH encrypts a session or a specific port, while a VPN encrypts all of the device’s network traffic. For administering a single server, SSH is enough; for accessing a full corporate network, a VPN is still necessary.

References

  • OpenSSH: official site of the project and the protocol’s reference implementation.
  • RFC 4253 (IETF): specification of the SSH-2 transport layer.
  • GitHub Docs: official guide to generating and using SSH keys with GitHub.
  • Wikipedia: history and technical summary of the Secure Shell protocol.

📱 Enjoying this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day. @programacion

Featured image: Foto de Sasun Bughdaryan en Unsplash

Did it work for you? Got a different error? Say so below: questions get answered and help the next reader.

Leave a comment
Categories: SecurityTutorials

Clara Vásquez

Cybersecurity analyst focused on critical vulnerabilities, zero-days, and emerging threats. Covers high-impact CVEs, malware analysis, ransomware incidents, and security trends with a LATAM lens.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

You can include code inside <code>…</code> or, for several lines, <pre><code>…</code></pre>.

This site uses Akismet to reduce spam. Learn how your comment data is processed.