惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
T
ThreatConnect
SecWiki News
SecWiki News
F
Future of Privacy Forum
AWS News Blog
AWS News Blog
C
Cisco Blogs
A
Arctic Wolf
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
Scott Helme
Scott Helme
V
V2EX
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
K
Kaspersky official blog
G
Google Developers Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
P
Privacy International News Feed
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News | PayPal Newsroom
Schneier on Security
Schneier on Security
NISL@THU
NISL@THU
Microsoft Azure Blog
Microsoft Azure Blog
量子位
The Hacker News
The Hacker News
Stack Overflow Blog
Stack Overflow Blog
Security Latest
Security Latest
M
Microsoft Research Blog - Microsoft Research
Google Online Security Blog
Google Online Security Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
I
InfoQ
Google DeepMind News
Google DeepMind News
Y
Y Combinator Blog
The Cloudflare Blog
Microsoft Security Blog
Microsoft Security Blog
Martin Fowler
Martin Fowler
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Troy Hunt's Blog
F
Fox-IT International blog
S
Security @ Cisco Blogs
博客园 - 司徒正美
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
Comments on: Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LINUX DO - 最新话题
GbyAI
GbyAI
Project Zero
Project Zero
腾讯CDC
T
Tailwind CSS Blog

DEV Community

RAG 시스템 실전 구축 (v42) copilot cloud agent is becoming an automation api Cx Dev Log — 2026-04-23 Why Tesla Is Becoming the AI Enterprise Case Study Every Leader Should Understand ORA-00214 오류 원인과 해결 방법 완벽 가이드 SpecAgnt v2.0: The Agent Lifecycle Framework for AI-Native Engineering Optimizing Signal Latency and Weight Allocations in Algorithmic Pipelines دليل بوابات الدفع للتاجر العربي في 2026 (وكيف تختار المناسبة لمتجرك) Cómo Mi Configuración de Docker Me Salvó de un Ataque de Supply Chain (Y Por Qué la Tuya Debería Hacerlo También) How My Docker Setup Saved Me From a Supply Chain Attack (And Why Yours Should Too) Astro: The epitome of SEO Technical Update I Gave My AI Agent the Ability to Research Before It Writes — Here’s What Changed Kubernetes sem Cloud Provider (Parte 2): Criando Operators em Go para automação e self-service de plataforma AI Memory Needs an Authority Policy, Not Just More Context You've done tutorial after tutorial. Your GitHub is still empty. (Free 1‑page PDF, no signup) TypeScript 7.0: The Go Compiler That Makes TS 10x Faster Connecting Wallets the Right Way: wagmi v2 and EIP-6963 The 5-Layer Architecture Every Production Multi-Agent System Needs (And Why Most Skip Layers 4 and 5) CSS Scroll-Driven Animations: No JavaScript Required Vite 8 + Rolldown: Rust-Powered Builds That Are 10–30x Faster Core Architectural Components of Azure My Skills How I Use AI as a Senior Engineer Construí um motor ATS determinístico porque estava cansado de adivinhar por que meu currículo era rejeitado SCS-Lab1 — CloudTrail: Trail + S3 + KMS + Log Validation LuisCore MCP server — daily syndication · 2026-05-25 Cursor vs JetBrains Rider for C#/.NET in 2026: which to pay for I built a local-first movie recommender with Corrective-RAG (cited explanations, hybrid retrieval, runs entirely on Ollama) Scaling to 1 Million Users : Load Balancing & Caching Strategies How the Events Table That Looked Right Killed Our Queue Three Failures My AI Memory System Caught — And the Flaw It Revealed in Itself dotnet Framework life cycle tool LangGraph 워크플로우 템플릿 (v41) I built a free image compression API — no signup, just curl Designing TikTok from Scratch — A System Design Deep Dive PREDICTION-20260525-0007: boredom-with-asymmetric-leverage [2026-Q3 through 2027-Q3] [Boost] How to integrate the QuickBooks Invoice API in 2026 How I Cut My Anthropic API Bill by 50% With a Local Python Tool Vibe Coding Problems: 7 Visual Bugs AI Code Generators Always Ship Chinese AI Models 2026: The Agentic Revolution, Hardware Independence, and What It Means for Global Developers The Quiet AI War Inside Your Browser The 12-Line Anti-Bot Trick That Saved Our Airdrop Snapshot From Sybil Farms Building a production-ready SaaS dashboard in Next.js 16 — Recharts, TanStack Table, dark mode, and collapsible sidebar Why 2026 Belongs to Agentic AI (And How to Build Your First Local Agent) It Was 2024 When We Tried to Outsmart the Treasure Hunt Engine RAG 시스템 실전 구축 (v40) I Found a Tool That Generates a Complete .NET 8 or Java Spring Boot API From SQL Schema in 30 Seconds I Added a 4th Agent That Audits My Other Agents. It Caught My Strategist Procrastinating for 3 Weeks. Streaming LLM responses to the browser in Go (Server-Sent Events) How We Publish and Manage Educational Admission Updates at Scale on DailyAxom A prompt is not a conversation. It's a component contract. How to Pass the EAA 2025 Accessibility Audit — A Step-by-Step WCAG Checklist Building an Autonomous MCP Lead Generation System with Hermes Agent LangGraph 워크플로우 템플릿 (v40) How I Built 100 Browser-Based Image Tools With No Server (FFmpeg WASM, PDF-lib, AI Background Removal) Nginx CVE-2026-9256, AI Prompt Injection Defenses, and Claude AI Data Leak Demo Scaling RAG for 10M+ Docs, .md Agent Memory, & Claude Code for Motion Graphics Diagram as Code with draw.io DuckDB Delta, PostgreSQL 17 Migration, & SQLite Optimization Deep Dives Windows 11 Microsoft Account Login Recovery During Internet Restrictions The Linux Commands You Forgot Exist (And Why AI Workflows Make Them Relevant Again) Spec-Driven Development Without an IDE: I Generated NestJS, Go, Spring Boot, Laravel, and Rust Apps From a Single PRD File Components are states Edge SEO y Middleware: Cómo Interceptar a Googlebot y LLMs antes de llegar a tu Servidor Context window exceeded at turn 23. Here's how I track token usage without a tokenizer. My Hermes agent spent $3 before I noticed. Now it can't. My Hermes agent's stop condition was a 40-line if/elif chain. I replaced it with 3 lines. My agent kept hitting context limits. This one function fixed it. Create and configure Azure Firewall Your Hermes agent's audit log is leaking customer emails. Here's a 100-line lib that fixes that. My agent kept forgetting what it was doing. A scratchpad fixed it. I replaced 200 lines of ad-hoc state management in my Hermes agent with one object. Per-Key Rate Limiting for Agent Tool Calls: Stop One User From Breaking Everything Composable Output Guardrails: Filter Agent Responses Before They Reach Users Sanitize Your LLM Message Lists Before Every API Call Thread a Run ID Through Every Agent Call So You Can Debug Anything Normalize Provider Error JSON So Your Agent Can Actually Handle Failures Priority Queue for Agent Sub-Tasks: Stop Processing Low-Priority Work First Static Lint Rules for Your LLM Prompts (Before They Hit Production) tool-call-budgets: Stop Runaway Agent Loops Before They Hit Your Invoice Step Through Your Agent's Failures Like a Debugger The Simplest Stop Condition: A Hard Cap on Agent Loop Iterations Score Your Agent's Responses With a 0.0-1.0 Rubric (No LLM Judge Required) Fix Bad Structured Output by Feeding the Error Back to the Model Building an effective Storyblok Tool Plugin with SvelteKit How to Get Your Renault / Dacia Radio Code for Free RAG 시스템 실전 구축 (v39) Retraction — scrml’s Living Compiler I built a fitness app where the AI roasts you for eating pizza (and hypes you when you PR) The Top SaaS Founder Communities on Discord (Beyond the AI Hype) I Built a Production-Grade Async Job Queue from Scratch — Here's Everything That Actually Happened How to watch SMS from multiple Android phones in one iOS app We Didn’t Want Another AI Wrapper — So We Explored a High-Speed Hermes Orchestrator for Engineering Crews Multi-tenant além do TenantId: problemas reais e aprendizados em sistemas .NET After failing 23 times, I am sharing How I Actually Prepare for a Tech Interview Every Single Time Now. I built an app that works like a nutritionist for your brain. Here's what happened in 7 days. GoBadge Dynamic: From Module Stats to Universal Badges LangGraph 워크플로우 템플릿 (v39)
SSH Under the Hood: Protocols, Mechanisms, and the Full Technical Story
Mahafuzur Ra · 2026-05-26 · via DEV Community

What SSH Actually Is

SSH — Secure Shell — is a cryptographic network protocol for operating network services securely over an unsecured network. At its core, it is a client-server architecture: an SSH client initiates a connection, and an SSH daemon (sshd) listens on the server, typically on port 22. Every piece of data that travels between them is encrypted, authenticated, and integrity-protected.

What most developers interact with day-to-day — typing ssh user@host — is the tip of an enormous iceberg. Beneath that single command lies a precisely ordered sequence of cryptographic handshakes, key negotiations, and protocol layers that happen in milliseconds.


The Protocol Stack: Three Layers

The SSH protocol is formally defined in a family of RFCs (RFC 4251–4254) and is composed of three distinct sub-protocols stacked on top of TCP:

┌─────────────────────────────────────────┐
│         SSH Connection Protocol         │  ← channels, sessions, port forwarding
├─────────────────────────────────────────┤
│        SSH User Auth Protocol           │  ← password, publickey, keyboard-interactive
├─────────────────────────────────────────┤
│        SSH Transport Layer Protocol     │  ← encryption, MAC, key exchange
├─────────────────────────────────────────┤
│                  TCP                    │
└─────────────────────────────────────────┘

Enter fullscreen mode Exit fullscreen mode

Each layer has a precise, well-defined job. Let's walk through each one in order of execution.


Stage 1 — TCP Connection and Version Exchange

Everything begins with a plain TCP handshake to port 22. Once the TCP connection is established, both sides immediately send a cleartext version string:

SSH-2.0-OpenSSH_9.6

Enter fullscreen mode Exit fullscreen mode

This string declares the SSH protocol version (always 2.0 in modern usage — SSH-1 is deprecated and broken) and the software implementation. Both sides read each other's version string, and if they are incompatible, the connection closes immediately. This is the only plaintext exchange in the entire session. Everything after this is encrypted.


Stage 2 — The Transport Layer: Key Exchange (KEX)

This is the most cryptographically dense part of SSH. The goal is to establish a shared secret between client and server without ever transmitting that secret across the wire — even in encrypted form. This is achieved through a Key Exchange Algorithm, most commonly Diffie-Hellman (DH) or its elliptic-curve variant ECDH.

Algorithm Negotiation

Before the actual key exchange begins, client and server negotiate which algorithms to use. Both sides send a SSH_MSG_KEXINIT packet listing their supported algorithms in preference order for each category:

  • Key Exchange algorithms: curve25519-sha256, ecdh-sha2-nistp256, diffie-hellman-group14-sha256
  • Host key algorithms: ssh-ed25519, ecdsa-sha2-nistp256, rsa-sha2-512
  • Encryption ciphers: chacha20-poly1305@openssh.com, aes256-gcm@openssh.com, aes128-ctr
  • MAC algorithms: hmac-sha2-256, hmac-sha2-512, umac-128-etm@openssh.com
  • Compression: none, zlib@openssh.com

The agreed algorithm for each category is the first one in the client's list that the server also supports.

The Diffie-Hellman Key Exchange

The actual key exchange works as follows (using DH as the canonical example):

  1. Both sides agree on a large prime p and a generator g (these are public, standardized values).
  2. The client generates a random private integer x, computes e = g^x mod p, and sends e to the server.
  3. The server generates a random private integer y, computes f = g^y mod p, and sends f to the client.
  4. The client computes the shared secret K = f^x mod p.
  5. The server computes the shared secret K = e^y mod p.

Both arrive at the same value of K — the shared secret — without either side ever transmitting x or y. An eavesdropper who sees e and f cannot derive K without solving the discrete logarithm problem, which is computationally infeasible for sufficiently large primes.

With modern OpenSSH, Curve25519 is the preferred KEX algorithm. It uses elliptic-curve Diffie-Hellman (ECDH) over the Curve25519 elliptic curve, which offers 128-bit security with keys far smaller than classic DH, and has been designed to resist side-channel attacks.

Host Key Verification and the Exchange Hash

The key exchange alone doesn't prevent a man-in-the-middle attack. An attacker could intercept both sides and run two separate key exchanges. This is where the server's host key comes in.

After computing the shared secret K, the server assembles an exchange hash H:

H = hash(client_version || server_version || client_kexinit || server_kexinit || server_host_public_key || e || f || K)

Enter fullscreen mode Exit fullscreen mode

The server then signs H with its private host key (e.g., an Ed25519 key stored in /etc/ssh/ssh_host_ed25519_key). This signature, along with the server's public host key, is sent to the client.

The client must now decide: do I trust this host key?

  • If the client has seen this server before, it checks ~/.ssh/known_hosts for a matching entry.
  • If the key matches, the client verifies the signature over H using that public key. A valid signature proves that whoever sent this data possesses the corresponding private host key — i.e., this is the real server.
  • If the key is new, the client prompts the user: The authenticity of host X can't be established. Are you sure you want to continue?

This is the famous TOFU (Trust On First Use) model. It is the primary defense against man-in-the-middle attacks.

Session Key Derivation

From the shared secret K and the exchange hash H, both sides independently derive the same set of symmetric session keys using a KDF (Key Derivation Function):

Encryption key (client → server):  hash(K || H || "C" || session_id)
Encryption key (server → client):  hash(K || H || "D" || session_id)
IV (client → server):              hash(K || H || "A" || session_id)
IV (server → client):              hash(K || H || "B" || session_id)
MAC key (client → server):         hash(K || H || "E" || session_id)
MAC key (server → client):         hash(K || H || "F" || session_id)

Enter fullscreen mode Exit fullscreen mode

Separate keys for each direction means a compromise of one direction doesn't compromise the other. After this, both sides send SSH_MSG_NEWKEYS to signal that all subsequent packets will be encrypted with these session keys. The transport layer is now live.


Stage 3 — The User Authentication Protocol

With an encrypted, integrity-protected channel established, the server now knows the client can communicate securely — but it doesn't yet know who the client is. That's what the User Auth protocol resolves.

The client sends SSH_MSG_SERVICE_REQUEST for ssh-userauth. The server confirms, and authentication begins.

Password Authentication

The simplest method. The client sends the username and password, encrypted inside the already-secure channel. The server verifies against /etc/shadow (or PAM, or LDAP). If correct, authentication succeeds.

This method is discouraged in production because it is vulnerable to brute force and credential stuffing. Most hardened servers disable it entirely with PasswordAuthentication no in sshd_config.

Public Key Authentication

This is the gold standard. The protocol works like this:

  1. The client declares intent: "I want to authenticate as user alice using this public key."
  2. The server checks whether that public key is listed in ~/.ssh/authorized_keys for user alice.
  3. If found, the server sends a challenge: a unique blob of data.
  4. The client signs the challenge with the corresponding private key (stored in ~/.ssh/id_ed25519 or similar).
  5. The client sends the signature back.
  6. The server verifies the signature with the public key. Only the holder of the private key could have produced a valid signature. Authentication succeeds.

The private key never leaves the client machine. Not even a fragment of it crosses the wire. This is what makes public key authentication so robust.

SSH Agent and Agent Forwarding

In practice, private keys are often protected by passphrases. Typing the passphrase every time would be impractical. The SSH agent (ssh-agent) solves this: it holds decrypted private keys in memory and performs signing operations on behalf of the SSH client. The client communicates with the agent over a local Unix socket (stored in $SSH_AUTH_SOCK).

Agent forwarding (ssh -A) extends this: when you SSH from machine A to machine B, and then from B to machine C, the signing requests can be forwarded back through the chain to the agent on machine A. Machine B never sees the private key. This is extremely convenient but carries risk — anyone with root access on machine B can use your agent socket to impersonate you on machine C.

Certificate-Based Authentication

Modern SSH deployments at scale use SSH certificates instead of raw public keys. A Certificate Authority (CA) signs user or host public keys, embedding authorized principals, validity periods, and extension flags. The authorized_keys approach requires every server to list every authorized user's public key. With certificates, every server only needs to trust the CA's public key, and the CA issues short-lived certificates to users. This dramatically simplifies key management at scale.


Stage 4 — The Connection Protocol: Multiplexed Channels

After authentication, the SSH Connection Protocol takes over. It multiplexes multiple logical channels over the single encrypted TCP connection. Each channel is identified by a number and can carry different types of traffic simultaneously.

Channel Types

  • session: A remote command execution or interactive shell. This is what you get when you simply run ssh user@host.
  • direct-tcpip: Local port forwarding. Traffic sent to a local port is tunneled to a destination via the SSH server.
  • forwarded-tcpip: Remote port forwarding. The server forwards traffic from one of its ports through the SSH tunnel to the client.
  • x11: X11 forwarding — tunneling graphical application display sessions.

How Channels Work

Opening a channel:

Client → Server:  SSH_MSG_CHANNEL_OPEN (type="session", sender_channel=0, window_size=2MB, max_packet=32KB)
Server → Client:  SSH_MSG_CHANNEL_OPEN_CONFIRMATION (recipient_channel=0, sender_channel=1, ...)

Enter fullscreen mode Exit fullscreen mode

Each channel has independent flow control via window sizes — the sender cannot push more data than the receiver's advertised window allows. When the receiver processes data, it sends SSH_MSG_CHANNEL_WINDOW_ADJUST to expand the window.

Data flows inside SSH_MSG_CHANNEL_DATA packets. The channel is closed with SSH_MSG_CHANNEL_CLOSE. Multiple channels can be open simultaneously, all multiplexed over the single TCP connection.

Interactive Shell Sessions

When you want an interactive shell, the client requests a PTY (Pseudo-Terminal):

SSH_MSG_CHANNEL_REQUEST (request-type="pty-req", term="xterm-256color", columns=220, rows=50, ...)
SSH_MSG_CHANNEL_REQUEST (request-type="shell")

Enter fullscreen mode Exit fullscreen mode

The server allocates a PTY pair: a master side (controlled by sshd) and a slave side (the terminal seen by the remote shell). Terminal resize events are sent with SSH_MSG_CHANNEL_REQUEST (request-type="window-change"). For non-interactive commands (ssh user@host ls -la), the PTY request is skipped and only exec is requested.


Port Forwarding: SSH as a Tunnel

SSH's channel mechanism enables powerful tunneling capabilities.

Local Port Forwarding

ssh -L 5432:db.internal:5432 user@jumphost

Enter fullscreen mode Exit fullscreen mode

Instructs the SSH client to listen on local port 5432. Any connection to that port is wrapped in an SSH channel and forwarded to db.internal:5432 as seen from the jump host. The traffic between your machine and the jump host is encrypted; the jump host connects to the database in plaintext (or its own encrypted connection).

Remote Port Forwarding

ssh -R 8080:localhost:3000 user@publicserver

Enter fullscreen mode Exit fullscreen mode

The SSH server listens on publicserver:8080. Connections to that port are tunneled back through SSH to localhost:3000 on your machine. This is how developers expose local development servers to the internet without a public IP.

Dynamic Port Forwarding (SOCKS Proxy)

ssh -D 1080 user@host

Enter fullscreen mode Exit fullscreen mode

Creates a SOCKS5 proxy on local port 1080. Applications configured to use this proxy send their traffic through the SSH tunnel, with the server making outbound connections on their behalf. This turns an SSH server into a makeshift VPN exit node.


Packet Structure: What's Actually on the Wire

Every SSH packet after the transport layer handshake follows this binary structure:

┌──────────────────────┬──────────────┬───────────┬─────────────────┬──────────────┐
│  packet_length (4B)  │ padding (1B) │  payload  │ random_padding  │  MAC (0-32B) │
└──────────────────────┴──────────────┴───────────┴─────────────────┴──────────────┘

Enter fullscreen mode Exit fullscreen mode

  • packet_length: Length of everything that follows, excluding the MAC.
  • padding_length: How many bytes of random padding follow the payload.
  • payload: The actual message (message type byte + content).
  • random_padding: Random bytes to ensure the total packet size is a multiple of the cipher's block size, and to prevent traffic analysis based on message length.
  • MAC: Message Authentication Code, computed over sequence_number || packet_length || padding_length || payload || random_padding. This detects any tampering in transit.

With AEAD ciphers like chacha20-poly1305 or aes256-gcm, the MAC is integrated into the cipher tag — there is no separate MAC field.


The known_hosts File and TOFU

Every time you connect to a new SSH server, OpenSSH stores the server's host key in ~/.ssh/known_hosts:

github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl

Enter fullscreen mode Exit fullscreen mode

On every subsequent connection, OpenSSH verifies that the server presents the same host key. If it has changed, you see the famous warning:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Enter fullscreen mode Exit fullscreen mode

This can mean a legitimate server rebuild — or a man-in-the-middle attack. Never dismiss this warning casually.


The sshd_config and ssh_config Files

The server's behavior is controlled by /etc/ssh/sshd_config. Key directives:

Port 22
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
AllowUsers alice bob
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 60
ClientAliveCountMax 3

Enter fullscreen mode Exit fullscreen mode

The client's behavior is controlled by ~/.ssh/config, which allows per-host configuration:

Host myserver
    HostName 203.0.113.42
    User alice
    IdentityFile ~/.ssh/id_ed25519_myserver
    ForwardAgent yes
    ServerAliveInterval 60

Enter fullscreen mode Exit fullscreen mode


Algorithm Security Reference

Algorithm Type Security Notes
curve25519-sha256 KEX ✅ Strong Default in modern OpenSSH
diffie-hellman-group14-sha1 KEX ⚠️ Weak SHA-1 deprecated
ssh-ed25519 Host/User key ✅ Strong Preferred key type
ecdsa-sha2-nistp256 Host/User key ✅ Good NIST curve, widely supported
ssh-rsa Host/User key ⚠️ Legacy Disabled by default in OpenSSH 8.8+
chacha20-poly1305 Cipher ✅ Strong Resistant to timing attacks
aes256-gcm Cipher ✅ Strong Hardware-accelerated on modern CPUs
aes128-ctr Cipher ✅ Acceptable Requires separate MAC
hmac-sha2-256-etm MAC ✅ Strong Encrypt-then-MAC is the correct order
hmac-sha1 MAC ❌ Broken SHA-1 is cryptographically deprecated

What Happens When You Type ssh user@host

A complete timeline:

t=0ms    TCP SYN → server:22
t=1ms    TCP SYN-ACK ← server
t=1ms    TCP ACK → server
t=1ms    "SSH-2.0-OpenSSH_9.6\r\n" → server
t=2ms    "SSH-2.0-OpenSSH_9.6\r\n" ← server
t=2ms    SSH_MSG_KEXINIT → server  (client algorithm list)
t=2ms    SSH_MSG_KEXINIT ← server  (server algorithm list)
t=2ms    SSH_MSG_KEX_ECDH_INIT → server  (client's ephemeral public key)
t=3ms    SSH_MSG_KEX_ECDH_REPLY ← server  (server host key + ephemeral key + signature)
         [client verifies host key against known_hosts]
         [client verifies signature]
         [both sides derive session keys]
t=3ms    SSH_MSG_NEWKEYS → server
t=3ms    SSH_MSG_NEWKEYS ← server
         *** All subsequent packets are encrypted ***
t=3ms    SSH_MSG_SERVICE_REQUEST("ssh-userauth") → server
t=4ms    SSH_MSG_SERVICE_ACCEPT ← server
t=4ms    SSH_MSG_USERAUTH_REQUEST(publickey, ed25519, pubkey) → server
t=4ms    SSH_MSG_USERAUTH_PK_OK ← server  (yes, I know that key)
t=4ms    SSH_MSG_USERAUTH_REQUEST(publickey, ed25519, pubkey, signature) → server
t=5ms    SSH_MSG_USERAUTH_SUCCESS ← server
t=5ms    SSH_MSG_CHANNEL_OPEN("session") → server
t=5ms    SSH_MSG_CHANNEL_OPEN_CONFIRMATION ← server
t=5ms    SSH_MSG_CHANNEL_REQUEST("pty-req") → server
t=5ms    SSH_MSG_CHANNEL_SUCCESS ← server
t=5ms    SSH_MSG_CHANNEL_REQUEST("shell") → server
t=5ms    SSH_MSG_CHANNEL_SUCCESS ← server
         *** Interactive shell is live ***

Enter fullscreen mode Exit fullscreen mode

Total elapsed time: ~5–20ms on a typical LAN.


Conclusion

SSH is one of the most elegantly engineered protocols in existence. Its layered architecture cleanly separates transport security, identity verification, and application multiplexing. The cryptographic foundations — ephemeral key exchange, asymmetric authentication, symmetric encryption with integrity protection — work in concert to provide confidentiality, authentication, and integrity across an untrusted network.

Every time you type ssh user@host, this entire machinery executes in under a second. Understanding it not only demystifies a tool you use daily, but gives you the knowledge to configure it securely, debug it when things go wrong, and reason clearly about what guarantees it actually provides — and where those guarantees end.


SSH Mastering series:

SSH in 2026: Why Every Developer Should Know It Cold