What Is SSH? Secure Shell Protocol Explained for Developers
The Protocol That Replaced Plaintext Networking
In February 1995, a Finnish computer scientist named Tatu Ylönen discovered that a password sniffer had been installed on his university's network. The attacker had captured thousands of usernames and passwords — all transmitted in plaintext by Telnet, rsh, and rlogin, the standard remote access tools of the era. Within three months, Ylönen had written SSH (Secure Shell) to replace them. He released it publicly in July 1995, and within four months it had spread to over 20,000 users in 50+ countries.
Today, SSH is the backbone of server administration, DevOps, and cloud infrastructure. According to Landbase's 2026 technology dataset, 1,204 verified companies list SSH as a core infrastructure component — and that number reflects only companies that publicly declare it. In practice, virtually every Linux server on the internet runs OpenSSH. The SSH protocol suite is standardized in four IETF RFCs: 4251 (architecture), 4252 (authentication), 4253 (transport layer), and 4254 (connection protocol), all published in January 2006.
This guide covers how SSH actually works at the protocol level — not just "run ssh user@host." Understanding the internals makes you a better sysadmin, a better security reviewer, and better at diagnosing the failures that happen when SSH does not behave as expected.
Key Takeaways
- ▸SSH is a three-layer protocol: the transport layer handles encryption and host authentication; the authentication layer proves client identity; the connection layer multiplexes channels (shell, port forward, file transfer) over the single encrypted tunnel.
- ▸Key exchange uses Diffie-Hellman or Curve25519-Diffie-Hellman (X25519) to derive shared secret material without ever transmitting the session key — making it impossible to decrypt recorded sessions retroactively (perfect forward secrecy).
- ▸Ed25519 is the correct key type in 2026. It is faster, more compact, and has fewer implementation pitfalls than RSA-2048 or ECDSA. Generate with
ssh-keygen -t ed25519. - ▸Disable password authentication entirely (
PasswordAuthentication no). Automated bots scan port 22 continuously — password auth is always one credential leak away from compromise. - ▸CVE-2025-32433 (Erlang/OTP SSH, CVSS 10.0) allowed unauthenticated remote code execution — a reminder that SSH implementations, not just configuration, must be kept updated.
SSH Architecture: Three Protocols in One
SSH is not a single monolithic protocol. RFC 4251 defines it as three layered protocols that stack on top of each other, each with a distinct responsibility:
| Layer | RFC | What It Does | Runs On Top Of |
|---|---|---|---|
| Transport Layer | RFC 4253 | Key exchange, host authentication, encryption, integrity, compression | TCP (port 22) |
| User Auth Layer | RFC 4252 | Authenticates the client to the server (publickey, password, keyboard-interactive) | SSH Transport Layer |
| Connection Layer | RFC 4254 | Multiplexes logical channels (shell, exec, subsystem, port-forward) over the tunnel | SSH User Auth Layer |
This layered design is why SSH can do so much: the same encrypted tunnel carries an interactive shell, SFTP file transfers, Git pushes, and multiple port-forwarded connections simultaneously, all multiplexed as separate channels at the Connection Layer.
The Connection Sequence: Step by Step
When you run ssh [email protected], here is precisely what happens:
Algorithm Negotiation: What OpenSSH Uses by Default
Modern OpenSSH (9.x) has strong defaults. But understanding which algorithms are negotiated — and which are disabled — is essential for compliance requirements and debugging failed connections to legacy systems:
| Purpose | Preferred (2026) | Also Supported | Removed/Deprecated |
|---|---|---|---|
| Key Exchange | curve25519-sha256 | diffie-hellman-group16-sha512 | diffie-hellman-group1-sha1 (Logjam) |
| Host Key | ssh-ed25519 | rsa-sha2-512, ecdsa-sha2-nistp256 | ssh-dss (DSA), ssh-rsa (SHA-1) |
| Symmetric Encryption | [email protected] | [email protected], aes128-ctr | 3des-cbc, arcfour, blowfish-cbc |
| MAC | Implicit (AEAD ciphers) | hmac-sha2-256-etm, hmac-sha2-512-etm | hmac-md5, hmac-sha1 |
# See what algorithms your client and server negotiated ssh -v [email protected] 2>&1 | grep -E "kex|cipher|mac|compression" # Example output: # debug1: kex: algorithm: curve25519-sha256 # debug1: kex: host key algorithm: ssh-ed25519 # debug1: kex: server->client cipher: [email protected] MAC: <implicit> # debug1: kex: client->server cipher: [email protected] MAC: <implicit> # Check supported algorithms on the server ssh -Q kex # key exchange algorithms ssh -Q cipher # symmetric ciphers ssh -Q mac # MAC algorithms ssh -Q key # host/user key types
SSH Key Types: Ed25519, RSA, ECDSA — Which to Use
The cryptographic properties of your SSH key type determine both security strength and operational characteristics. Ed25519 has been the right answer for new keys since OpenSSH 6.5 (2014). Here is why, and when the other types still matter:
Ed25519 (Recommended)
Ed25519 is an EdDSA (Edwards-curve Digital Signature Algorithm) scheme operating on Curve25519. Its advantages over RSA and ECDSA are substantial:
- Fixed key size: always 256 bits (32 bytes). An Ed25519 public key is 68 characters in OpenSSH format. An RSA-4096 public key is 726 characters.
- Faster: signature generation and verification are faster than RSA-2048 and competitive with ECDSA, per OpenSSH benchmarks.
- Deterministic: the same message + key always produces the same signature. ECDSA requires a random nonce for each signature — a poor random number generator leaks the private key (the Sony PlayStation 3 hack, the Android Bitcoin wallet vulnerability).
- No known timing side-channels in constant-time implementations (libsodium, OpenSSL 1.1.1+).
# Generate an Ed25519 key (recommended) ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_ed25519 # If connecting to legacy systems that don't support Ed25519 (rare but exists): ssh-keygen -t rsa -b 4096 -C "[email protected]" -f ~/.ssh/id_rsa_legacy # View the key fingerprint (SHA-256 hash of the public key) # This is what appears in authorized_keys audit logs ssh-keygen -l -f ~/.ssh/id_ed25519 # 256 SHA256:abc123... [email protected] (ED25519) # Copy public key to server ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected] # This appends your public key to ~/.ssh/authorized_keys on the server # Verify: try connecting (should not prompt for password) ssh -i ~/.ssh/id_ed25519 [email protected] # Manual deployment (if ssh-copy-id not available) cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"
RSA: Still Viable, Use 4096 Bits
RSA keys are supported everywhere and remain secure at 4096 bits. The weak point is implementation complexity — RSA requires careful padding (PKCS#1 v1.5 is vulnerable to Bleichenbacher attacks when misused; use RSA-OAEP for encryption or RSA-PSS for signatures). In SSH specifically, OpenSSH uses RSA only for signatures (not encryption), and has used rsa-sha2-256/512 since OpenSSH 7.2 — no longer the vulnerable SHA-1 variant. Use RSA-4096 if your organization has compliance requirements specifying RSA, or if connecting to hardware that does not support Ed25519.
ECDSA: Avoid for New Keys
ECDSA on NIST curves (P-256, P-384, P-521) is faster than RSA but shares the random-nonce-dependency risk mentioned above. The NIST curves are also under ongoing scrutiny regarding potential backdoors in their generation parameters. If you are choosing between ECDSA and Ed25519, always prefer Ed25519. ECDSA's only advantage over Ed25519 is compatibility with FIPS 140-2 validated modules, which are required in some U.S. government environments.
SSH Authentication Methods Compared
| Method | sshd_config Option | Security | Recommendation |
|---|---|---|---|
| Public Key | PubkeyAuthentication yes | Excellent | Use exclusively |
| Password | PasswordAuthentication | Weak (brute-force) | Disable: set to no |
| Certificate (CA-signed) | TrustedUserCAKeys | Excellent | Best for large fleets (centralized expiry) |
| GSSAPI (Kerberos) | GSSAPIAuthentication | Good (SSO) | For enterprise Active Directory integration |
| Keyboard-Interactive | KbdInteractiveAuthentication | Varies | Used for MFA/TOTP integration |
SSH Certificate Authentication (for Teams)
For organizations with more than a handful of servers, managing individual public keys in authorized_keys files is operationally painful. A developer leaves; you must manually remove their key from 200 servers. SSH certificates solve this: a central Certificate Authority (CA) signs user keys with an expiry time. Servers trust the CA — not individual keys. Revocation is trivial.
# 1. Create a CA key (stored securely, never distributed) ssh-keygen -t ed25519 -f /etc/ssh/ca_key -C "Corp SSH CA" # 2. Configure sshd to trust certificates from this CA # /etc/ssh/sshd_config TrustedUserCAKeys /etc/ssh/ca_key.pub # 3. Sign a user's public key (8-hour certificate valid for 'deploy' principal) ssh-keygen -s /etc/ssh/ca_key -I "alice@corp-2026-05-01" # certificate identity (for audit logs) -n "alice,deploy" # valid principals (usernames on target servers) -V +8h # valid for 8 hours from now alice_id_ed25519.pub # This creates alice_id_ed25519-cert.pub # Alice uses it automatically: ssh -i alice_id_ed25519 user@server # 4. Inspect the certificate ssh-keygen -L -f alice_id_ed25519-cert.pub # Type: [email protected] user certificate # Valid principals: alice, deploy # Valid: from 2026-05-01T09:00:00 to 2026-05-01T17:00:00 # Signature key: ED25519 SHA256:...
SSH Beyond Remote Login: SFTP, Tunneling, and Git
SFTP and SCP File Transfer
SFTP (SSH File Transfer Protocol) runs as an SSH subsystem — it is not related to FTP despite the name. Since OpenSSH 9.0, the scp command uses the SFTP protocol internally rather than the legacy SCP/RCP protocol, making it safer and more correct. The command-line interface for scp remains unchanged.
# Copy file to server scp local-file.txt user@server:/remote/path/ # Copy file from server scp user@server:/remote/path/file.txt ./local-file.txt # Copy directory recursively scp -r ./local-dir/ user@server:/remote/path/ # SFTP interactive session sftp user@server # sftp> ls, put, get, mkdir, rm, chmod # rsync over SSH (preferred for large/repeated transfers) rsync -avz --progress ./local-dir/ user@server:/remote/path/ # Resumes partial transfers; only syncs changed files
SSH Tunneling and Port Forwarding
SSH's Connection Layer supports three types of port forwarding, all tunneled through the encrypted SSH connection:
# LOCAL forwarding: access remote service via local port # Use case: access a database on server that's only listening on localhost ssh -L 5432:localhost:5432 user@server # Now psql -h localhost -p 5432 connects through the SSH tunnel # REMOTE forwarding: expose local port on the server # Use case: test a local dev server from a remote machine ssh -R 8080:localhost:3000 user@server # Now server's port 8080 forwards to your local port 3000 # DYNAMIC forwarding: SOCKS5 proxy via the SSH server # Use case: route browser traffic through the server's network ssh -D 1080 user@server # Configure browser to use SOCKS5 proxy at localhost:1080 # Keep tunnels alive in background ssh -fNL 5432:localhost:5432 user@server # -f: fork to background, -N: don't execute remote command
SSH Agent and Key Management
# Start ssh-agent (usually handled by your OS/desktop) eval "$(ssh-agent -s)" # Add key to agent (prompts for passphrase once) ssh-add ~/.ssh/id_ed25519 # Add key that auto-removes after 4 hours ssh-add -t 14400 ~/.ssh/id_ed25519 # List loaded keys ssh-add -l # Agent forwarding: use your local keys on a remote server # (for jumping through bastion hosts, accessing private repos) ssh -A user@bastion # Now from bastion: ssh internal-server # Uses your local key via agent forwarding — private key never leaves your machine # ~/.ssh/config — cleaner than per-command flags Host prod-bastion HostName 203.0.113.5 User deploy IdentityFile ~/.ssh/id_ed25519 ForwardAgent yes ServerAliveInterval 60 # keepalive every 60 seconds ServerAliveCountMax 3 # disconnect after 3 missed keepalives Host internal-* ProxyJump prod-bastion # automatically tunnel through bastion User appuser IdentityFile ~/.ssh/id_ed25519
Hardening sshd_config: The Settings That Matter
The default OpenSSH configuration is reasonably secure but leaves room for improvement. Per Shodan.io 2025 data, there are 24 million+ internet-accessible SSH servers — all facing automated scanners 24/7. These settings meaningfully reduce your attack surface:
# Disable password authentication — most important setting PasswordAuthentication no ChallengeResponseAuthentication no KbdInteractiveAuthentication no # Disable root login (use sudo on the server) PermitRootLogin no # Enable only public key auth PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys # Whitelist specific users (add more with space separation) AllowUsers deploy alice bob # Restrict to specific groups instead of listing all users # AllowGroups sshusers # Require SSH2 only (SSH1 removed in OpenSSH 7.0, this is belt-and-suspenders) # Protocol 2 is now the only option in modern OpenSSH # Restrict key exchange to modern algorithms KexAlgorithms curve25519-sha256,[email protected],diffie-hellman-group18-sha512 # Restrict host key types HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256 # Restrict ciphers Ciphers [email protected],[email protected],[email protected] # Restrict MACs (AEAD ciphers make explicit MACs redundant, but for non-AEAD) MACs [email protected],[email protected] # Disconnect idle sessions ClientAliveInterval 300 ClientAliveCountMax 2 # Limit login attempts before disconnecting MaxAuthTries 3 # Reduce login grace time LoginGraceTime 30 # Disable unused features AllowAgentForwarding no # only if you don't need agent forwarding AllowTcpForwarding no # only if you don't need port forwarding X11Forwarding no # After editing: validate config before reloading # (to avoid locking yourself out) sshd -t && systemctl reload sshd
One often-overlooked detail: file permissions on ~/.ssh are load-bearing. OpenSSH refuses to use a private key or authorized_keys file if the permissions are too permissive. The required permissions are documented in the Linux file permissions guide, and the BytePane chmod calculator can help verify the octal values.
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chmod 600 ~/.ssh/id_ed25519 # private key: owner read/write only chmod 644 ~/.ssh/id_ed25519.pub # public key: world-readable is fine chmod 644 ~/.ssh/known_hosts chmod 600 ~/.ssh/config # client config file
SSH Security Incidents: What Goes Wrong
CVE-2025-32433: Erlang/OTP SSH Remote Code Execution
In May 2025, a critical CVSS 10.0 vulnerability was disclosed in the Erlang/OTP SSH implementation. The flaw allowed arbitrary remote code execution without authentication on any service using the Erlang SSH library. According to Black Hat MEA 2026 analysis, approximately 70% of detections came from firewalls protecting OT (operational technology) networks — industrial control systems running Erlang-based management interfaces exposed on SSH. Affected products included RabbitMQ, CouchDB, and Riak nodes with SSH management enabled.
The lesson: SSH security is not just about configuration. The SSH implementation must also be kept patched. This applies to every SSH library in your stack — OpenSSH, libssh, Erlang/OTP, Apache MINA SSHD, and paramiko (Python).
SSH in Lateral Movement Attacks
According to MITRE ATT&CK incident analysis for 2024, SSH/SMB/Windows admin shares accounted for 14% of lateral movement techniques in reported incidents. Attackers who compromise one server look for SSH private keys in home directories, CI/CD system configuration, and cloud instance metadata. A single exposed key without a passphrase, combined with agent forwarding or password-less sudo, can cascade through an entire infrastructure.
Essential SSH Commands Quick Reference
# ── CONNECTING ────────────────────────────────────────────── ssh user@host # Basic connection ssh -p 2222 user@host # Non-standard port ssh -i ~/.ssh/id_ed25519 user@host # Specify identity file ssh -v user@host # Verbose (debug connection issues) ssh -vvv user@host # Very verbose # ── REMOTE COMMAND EXECUTION ───────────────────────────────── ssh user@host "df -h" # Run single command ssh user@host "sudo systemctl status nginx" ssh user@host < local-script.sh # Run local script remotely # ── FILE TRANSFER ──────────────────────────────────────────── scp file.txt user@host:/path/ # Upload file scp user@host:/path/file.txt . # Download file rsync -avz ./dir/ user@host:/path/ # Sync directory (preferred) # ── KEY MANAGEMENT ─────────────────────────────────────────── ssh-keygen -t ed25519 -C "comment" # Generate key ssh-copy-id user@host # Deploy public key ssh-keygen -l -f ~/.ssh/id_ed25519 # Show fingerprint ssh-keygen -p -f ~/.ssh/id_ed25519 # Change passphrase # ── KNOWN HOSTS ────────────────────────────────────────────── ssh-keyscan host >> ~/.ssh/known_hosts # Add host key (scripted) ssh-keygen -R host # Remove host from known_hosts # ── TUNNELING ──────────────────────────────────────────────── ssh -L local_port:dest_host:dest_port user@jump # Local forward ssh -R remote_port:local_host:local_port user@server # Remote forward ssh -D 1080 user@host # SOCKS5 proxy # ── MULTIPLEXING (reuse existing connection, faster) ───────── # ~/.ssh/config Host * ControlMaster auto ControlPath ~/.ssh/cm-%r@%h:%p ControlPersist 10m
Frequently Asked Questions
What is SSH used for?
SSH is primarily used for secure remote login to Linux/Unix servers and executing commands over an encrypted connection. It also handles secure file transfer (SFTP/SCP), port forwarding and tunneling, and Git operations (GitHub, GitLab). SSH replaced Telnet and rsh because those protocols transmitted credentials in plaintext — interceptable by any network observer.
What port does SSH use?
SSH defaults to TCP port 22, registered with IANA for this purpose since 1995. Some admins move SSH to a non-standard port (like 2222) to reduce automated scan noise, but this is obscurity, not security — port scanners find it quickly. Use fail2ban and firewall IP allowlisting for real protection.
What is the difference between SSH1 and SSH2?
SSH1 (1995) had a CRC-32-based integrity check (vulnerable to insertion attacks) and RSA-only key exchange with known weaknesses. SSH2 (RFC 4251-4254, 2006) replaced the entire protocol: Diffie-Hellman key exchange, HMAC-based integrity, multiple algorithm support. OpenSSH dropped SSH1 in version 7.0 (2015). SSH2 is the only version in use.
What is the difference between SSH and SSL/TLS?
Both provide encrypted communication, but they operate differently. TLS is a security layer applied to existing protocols (HTTP→HTTPS, SMTP→SMTPS) and uses a certificate authority hierarchy for authentication. SSH is itself an application-layer protocol that manages both transport security and its own authentication via public keys or passwords — without a CA hierarchy.
Is SSH password authentication safe?
Passwords are transmitted encrypted, so they cannot be intercepted in transit. However, password auth is vulnerable to brute-force attacks — automated bots scan port 22 continuously. The right answer: disable password auth entirely (PasswordAuthentication no in sshd_config) and use public key authentication, which is immune to brute-force by design.
What SSH key type should I use in 2026?
Ed25519 — always, for new keys. It uses Curve25519, produces 256-bit keys, is faster than RSA-4096, is deterministic (no nonce-randomness risk like ECDSA), and has no known timing side-channels. Generate with: ssh-keygen -t ed25519 -C "[email protected]". Only use RSA-4096 if connecting to legacy systems that do not support Ed25519.
How do I harden an SSH server?
Core settings: PasswordAuthentication no, PermitRootLogin no, AllowUsers (whitelist), ClientAliveInterval 300, MaxAuthTries 3. Restrict algorithms to modern ones (curve25519 for key exchange, chacha20-poly1305 for encryption). Install fail2ban to block brute-force IPs. Restrict SSH access at the firewall level to known IP ranges where operationally feasible.
How does SSH key authentication work?
You generate a key pair: private key (stays on your machine) and public key (added to server's ~/.ssh/authorized_keys). When you connect, the server challenges you with a random value encrypted by your public key. Only your private key can produce the correct signed response. No secret is transmitted — authentication is proved by signature, not by a shared password.
Developer Tools for Your Workflow
Verify file permissions, generate secure passwords for SSH passphrases, and encode keys — all free, browser-side tools.