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

推荐订阅源

P
Privacy & Cybersecurity Law Blog
WordPress大学
WordPress大学
Last Week in AI
Last Week in AI
腾讯CDC
人人都是产品经理
人人都是产品经理
小众软件
小众软件
V
Visual Studio Blog
S
Secure Thoughts
J
Java Code Geeks
V
V2EX
量子位
The Hacker News
The Hacker News
酷 壳 – CoolShell
酷 壳 – CoolShell
Security Latest
Security Latest
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Spread Privacy
Spread Privacy
博客园 - 叶小钗
T
Threat Research - Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Tailwind CSS Blog
Cloudbric
Cloudbric
S
SegmentFault 最新的问题
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Application and Cybersecurity Blog
Application and Cybersecurity Blog
IT之家
IT之家
T
Tenable Blog
S
Security @ Cisco Blogs
月光博客
月光博客
雷峰网
雷峰网
博客园 - 【当耐特】
Know Your Adversary
Know Your Adversary
C
Cybersecurity and Infrastructure Security Agency CISA
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hugging Face - Blog
Hugging Face - Blog
爱范儿
爱范儿
Attack and Defense Labs
Attack and Defense Labs
博客园 - 三生石上(FineUI控件)
Hacker News - Newest:
Hacker News - Newest: "LLM"
有赞技术团队
有赞技术团队
N
News and Events Feed by Topic
阮一峰的网络日志
阮一峰的网络日志
TaoSecurity Blog
TaoSecurity Blog
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The Cloudflare Blog
K
Kaspersky official blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Understanding known_hosts and Host Key Verification: What It Protects Against and How TOFU Works
Mahafuzur Ra · 2026-05-28 · via DEV Community

That "authenticity of host can't be established" message isn't just noise. Here's what's actually happening — and why blindly typing "yes" is a security mistake.


Every developer has seen this:

The authenticity of host 'example.com (203.0.113.1)' can't be established.
ED25519 key fingerprint is SHA256:abc123xyz...
Are you sure you want to continue connecting (yes/no/[fingerprint])?

Enter fullscreen mode Exit fullscreen mode

Almost everyone types yes without reading it. Then they move on.

This message is SSH trying to protect you from one of the most dangerous attacks in network security: the man-in-the-middle attack. Understanding what's happening here — and what the ~/.ssh/known_hosts file actually does — will change how you think about every SSH connection you make.


The Problem SSH Is Solving

When you connect to ssh user@example.com, how do you know you're actually talking to example.com?

You can't rely on the IP address — IP addresses can be spoofed or rerouted. You can't rely on DNS — DNS can be poisoned. You can't rely on the network path — traffic can be intercepted at any point between you and the server.

Without verification, an attacker positioned between you and the server could intercept the connection, pose as the server, decrypt everything you send, re-encrypt it, and forward it along. You'd type your password or authenticate with your key and never know the attacker saw every keystroke.

This is a man-in-the-middle (MITM) attack. It's not theoretical. It happens on compromised networks, corporate proxies, malicious Wi-Fi hotspots, and misconfigured infrastructure.

SSH's defense is host key verification. Every SSH server has a unique cryptographic identity — its host key. Before you exchange any sensitive data, the server proves it holds the private key corresponding to a public key you've previously verified. If the keys don't match, SSH warns you — loudly.


What a Host Key Actually Is

When OpenSSH is installed on a server, it automatically generates a set of host key pairs. These live in /etc/ssh/:

ls /etc/ssh/ssh_host_*

Enter fullscreen mode Exit fullscreen mode

/etc/ssh/ssh_host_ed25519_key       # Private key (600 permissions, root only)
/etc/ssh/ssh_host_ed25519_key.pub   # Public key (shared with clients)
/etc/ssh/ssh_host_rsa_key
/etc/ssh/ssh_host_rsa_key.pub
/etc/ssh/ssh_host_ecdsa_key
/etc/ssh/ssh_host_ecdsa_key.pub

Enter fullscreen mode Exit fullscreen mode

The private key never leaves the server. The public key is what the server presents during the SSH handshake.

When you connect for the first time, the server presents its public key. SSH calculates a fingerprint of that key and shows it to you — that's the SHA256:abc123xyz... in the prompt. If you confirm, SSH stores the public key in your ~/.ssh/known_hosts file. On every future connection, SSH checks that the server presents the same key. If it doesn't, SSH refuses to connect and shows a stern warning.


Trust On First Use (TOFU)

The model SSH uses is called Trust On First Use, or TOFU.

The logic:

  1. First connection: no existing record. SSH shows you the fingerprint and asks you to verify it.
  2. You confirm (type yes). The key is stored as trusted.
  3. All future connections: SSH silently verifies the key matches. If it does, you connect. If it doesn't, you get a warning.

TOFU is a pragmatic compromise. The theoretically correct approach would be to verify the server's fingerprint through a separate, trusted channel every single time — checking it against a known-good record before accepting the connection. In practice, almost no one does this for every server.

TOFU's weakness is that first connection. If an attacker intercepts your very first SSH connection to a server, you might accept their key and never know. After that point, the attacker is locked out (the wrong key is now stored) but they've already seen your first session.

For this reason, the first connection to a sensitive server should ideally involve fingerprint verification through an out-of-band channel — the cloud provider's console, a configuration management tool, or a colleague who can confirm the key directly on the server.

How to Verify a Fingerprint Before Connecting

On the server (accessed through another channel — the cloud console, serial port, etc.):

ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub

Enter fullscreen mode Exit fullscreen mode

256 SHA256:abc123xyz... root@server (ED25519)

Enter fullscreen mode Exit fullscreen mode

Compare this fingerprint to what SSH showed you during the first connection prompt. If they match, the connection is genuine.


Anatomy of ~/.ssh/known_hosts

The known_hosts file is a simple text database. Each line represents a trusted server:

example.com,203.0.113.1 ssh-ed25519 AAAA...base64encodedkey...
|1|hashedhostname= ssh-ed25519 AAAA...base64encodedkey...

Enter fullscreen mode Exit fullscreen mode

Fields:

  1. Hostnames/IPs: A comma-separated list of names and addresses that identify this server. Can be a plain hostname, an IP, or both.
  2. Key type: ssh-ed25519, ecdsa-sha2-nistp256, ssh-rsa, etc.
  3. Public key: Base64-encoded public key.

Hashed vs. Plain Hostnames

Notice the second line above starts with |1| — that's a hashed hostname. Many systems hash known_hosts entries by default (controlled by HashKnownHosts yes in ~/.ssh/config or the system config).

Hashing means that if someone gets read access to your known_hosts file, they can't easily see which servers you connect to. The hash is a one-way function of the hostname — SSH can check if a hostname matches, but an attacker can't reverse-engineer the hostname list.

Whether to use hashing is a privacy/convenience trade-off:

  • Hashed: More private, but you can't grep for a hostname to check if it's stored
  • Plain: Easier to manage manually, readable by any text editor

For most users, either is fine. Hashed is slightly better practice.

Checking a Known Host Manually

# Check if a specific host is in known_hosts
ssh-keygen -F example.com

# With hashed hosts (searches by computing the hash)
ssh-keygen -F 203.0.113.1

Enter fullscreen mode Exit fullscreen mode


The Warning You Should Never Ignore

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key was just changed.

Enter fullscreen mode Exit fullscreen mode

This message means the key presented by the server doesn't match the one stored in known_hosts. SSH refuses to connect.

Two explanations — one benign, one dangerous:

Benign: The server was rebuilt, migrated to a new IP, the OS was reinstalled, or an admin regenerated the host keys. The server is legitimate but its key genuinely changed.

Dangerous: Someone is intercepting your connection and presenting their own key — a man-in-the-middle attack.

What to do:

  1. Don't just clear the entry and reconnect. Investigate first.
  2. Verify through an out-of-band channel — the cloud provider console, a colleague, or direct physical/serial access.
  3. On the server, check the current key fingerprint: ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
  4. If the key genuinely changed for a legitimate reason, update your known_hosts.
  5. If you can't confirm it's legitimate, don't connect.

Removing a Stale Entry

If you've verified the key change is legitimate:

ssh-keygen -R example.com
ssh-keygen -R 203.0.113.1  # Also remove by IP if both were stored

Enter fullscreen mode Exit fullscreen mode

Then reconnect. SSH will present the new key and ask you to confirm.


Common Scenarios and How to Handle Them

Scenario 1: Newly Provisioned Cloud Server

You spin up a new EC2 instance. You want to SSH in. Best practice:

  1. Get the fingerprint from the AWS console under EC2 → Instance → Actions → Monitor and troubleshoot → Get system log (the host key fingerprints are printed on first boot)
  2. Or use EC2 Instance Connect through the browser to get to the console, then run ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
  3. Compare against what SSH shows you on first connection

Takes 60 extra seconds. Closes the TOFU window completely.

Scenario 2: Server Rebuilt With Same IP

Old key is in known_hosts. New server, new key. SSH screams at you.

ssh-keygen -R the-server-ip

Enter fullscreen mode Exit fullscreen mode

Reconnect, verify the new fingerprint if sensitive, proceed.

Scenario 3: Ephemeral Infrastructure (Containers, Auto-Scaling)

You're SSHing into containers or ephemeral VMs that share an IP but have different keys each time. Standard known_hosts checking breaks here.

For truly ephemeral infrastructure:

Host container-dev
    HostName 10.0.0.5
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

Enter fullscreen mode Exit fullscreen mode

StrictHostKeyChecking no skips the prompt. UserKnownHostsFile /dev/null prevents any key from being stored. This is acceptable for ephemeral local dev environments — not for anything production or sensitive.

A better solution for ephemeral production infrastructure is SSH certificates, where the CA's public key is trusted rather than individual host keys. The host presents a signed certificate; the client trusts anything signed by the CA.

Scenario 4: Automating SSH in Scripts

Scripts that run ssh non-interactively will hang on the first-connection prompt.

The right approach: pre-populate known_hosts before the script runs.

# Add a host key to known_hosts programmatically
ssh-keyscan -H example.com >> ~/.ssh/known_hosts

Enter fullscreen mode Exit fullscreen mode

ssh-keyscan fetches the host's public key without connecting. The -H flag hashes the hostname.

Important: ssh-keyscan alone doesn't verify authenticity — it just retrieves whatever key the server presents. It's vulnerable to MITM if used on an untrusted network. For maximum security, compare the retrieved key against a known-good fingerprint before adding it.

For CI/CD pipelines, a common pattern is to pre-populate known_hosts during pipeline setup with known, verified fingerprints from a trusted source (your infrastructure code, a Vault secret, etc.) rather than using ssh-keyscan blindly.


Managing known_hosts at Team Scale

Individual known_hosts management is fine for personal use. For teams, it creates inconsistency — different engineers have different records, first connections happen under different network conditions, and there's no central source of truth.

Option 1: Distribute a Shared known_hosts File

Maintain a team known_hosts file in your infrastructure repository. Engineers include it via ~/.ssh/config:

GlobalKnownHostsFile /etc/ssh/ssh_known_hosts ~/.ssh/known_hosts

Enter fullscreen mode Exit fullscreen mode

Or configure system-wide:

# /etc/ssh/ssh_config
GlobalKnownHostsFile /etc/ssh/ssh_known_hosts

Enter fullscreen mode Exit fullscreen mode

The team file lives at /etc/ssh/ssh_known_hosts and is managed by configuration management (Ansible, Puppet, Chef).

Option 2: SSH Certificates (The Proper Solution)

With SSH certificate authorities, you configure every server to trust the CA's public key:

# /etc/ssh/sshd_config
TrustedUserCAKeys /etc/ssh/trusted_ca.pub

Enter fullscreen mode Exit fullscreen mode

And every client to trust host certificates signed by the CA:

# ~/.ssh/known_hosts
@cert-authority *.example.com ssh-ed25519 AAAA...ca-public-key...

Enter fullscreen mode Exit fullscreen mode

Now instead of tracking individual host keys, you trust the CA. Servers present CA-signed host certificates. The TOFU problem goes away entirely — you verify the CA once, and all future connections are verified cryptographically.

This is how large organizations solve the known_hosts problem at scale.


Security Configuration Reference

Key settings in ~/.ssh/config related to host verification:

# Never skip host key checking (this is the default — keep it)
StrictHostKeyChecking yes

# Accept new host keys automatically, but warn if they change
# (A reasonable middle ground for non-sensitive environments)
StrictHostKeyChecking accept-new

# Hash stored hostnames (privacy)
HashKnownHosts yes

# Use a separate known_hosts for untrusted/ephemeral hosts
Host dev-ephemeral
    StrictHostKeyChecking no
    UserKnownHostsFile ~/.ssh/known_hosts_ephemeral

Enter fullscreen mode Exit fullscreen mode

StrictHostKeyChecking values:

Value Behavior Use When
yes Reject unknown hosts. Require manual verification. Production, sensitive systems
accept-new Accept and store new keys. Reject changed keys. Internal dev infrastructure
no Accept any key. Never warn. Ephemeral local-only containers

accept-new is the pragmatic middle ground for most teams — you still get protection against key changes (the dangerous case) while avoiding the friction of manually confirming every new host.


The Five-Minute Audit

Right now, open your known_hosts file:

cat ~/.ssh/known_hosts | wc -l   # How many entries?
ssh-keygen -F example.com         # Is a specific host stored?

Enter fullscreen mode Exit fullscreen mode

Consider:

  • Are there entries for servers that no longer exist?
  • Are there entries for IP addresses you don't recognize?
  • Are hostnames hashed or plain text?

Clean up stale entries with ssh-keygen -R hostname. It's low-effort hygiene with real security value.


The Bottom Line

The known_hosts file and host key verification are SSH's defense against impersonation. TOFU is an imperfect but practical trust model — its weakness is the first connection, its strength is that every connection after that is cryptographically verified.

Three habits make all the difference:

  1. Verify fingerprints on first connection to sensitive servers through an out-of-band channel
  2. Investigate host key change warnings rather than clearing the entry and proceeding
  3. Use accept-new as your default StrictHostKeyChecking value unless you need stricter controls

That warning message isn't noise. It's SSH doing its job. Learn to read it, and you'll catch the attacks it's designed to surface.


Follow for more practical SSH and infrastructure security content.