What TOFU actually means
Trust On First Use is a security model where, the first time you talk to a server, you record an identifier for it. From then on, any future connection that comes back with a different identifier is suspicious.
For SSH, the identifier is the server's public host key — typically an Ed25519 or RSA fingerprint. The first time you SSH to prod-server.example.com, the server presents its public key. You either trust it (because you set up the server) or you don't (because you didn't and someone is impersonating it). Once trusted, the fingerprint goes into your known_hosts file. Future connections must present the same key or OpenSSH refuses to proceed.
TOFU isn't perfect — the first connection is unverified — but it's pragmatic. After the first handshake, you're protected against active man-in-the-middle attacks even on hostile networks.
Why it matters on mobile
Mobile devices roam between trustable and untrustable networks several times a day: home Wi-Fi, office Wi-Fi, cellular, the hotel network with the deeply suspicious captive portal, the coffee shop with the network whose name is suddenly Free WiFi Login. Each of those is a place an attacker could sit on path and impersonate the SSH server you're trying to reach.
If your SSH client has properly stored the server's host key, none of that matters. The attacker's key won't match; the connection fails closed. If your client doesn't store host keys (or accepts whatever the server sends each time), you have no protection at all. You're handing your credentials to whoever responds on port 22.
What most mobile clients do (badly)
I tested several popular mobile SSH apps when designing TermAI's behavior. The most common defaults:
- Prompt-on-first-connect, never re-verify. The first time you connect, the app shows a fingerprint dialog. You tap Accept. The key is recorded. Future connections silently match it. This is the OpenSSH behavior — and it's fine when implemented correctly.
- Prompt-on-first-connect with a confusing warning UI. Same as above but the dialog says something like "Cannot verify server" with two buttons "Accept" and "Cancel". Most users tap Accept reflexively. Worse, when the key changes later, the UI gives them the same dialog and they accept again. A key change should look very different from a first connection.
- Never bother. Some apps just connect, never showing fingerprints, never warning on changes. This is broken-by-design.
The third category was more common than I'd have guessed, even among paid products.
How OpenSSH does it
OpenSSH's known_hosts file is the reference implementation. The behavior:
# First connection
$ ssh prod-server
The authenticity of host 'prod-server' can't be established.
ED25519 key fingerprint is SHA256:abc...xyz.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'prod-server' to the list of known hosts.
# Later, if key changes
$ ssh prod-server
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Host key verification failed. The first prompt is polite. The mismatch is hostile and refuses to connect. You have to manually remove the old entry to proceed. That asymmetry — calm on first use, aggressive on change — is the design TermAI inherits.
How TermAI does it
We store host fingerprints in an AES-encrypted Hive box on the device. The encryption key lives in the iOS Keychain (or Android Keystore equivalent), so even if someone gets the database file off the device, they can't read it without the system keystore unlocking.
- First connection. Server presents its host key. TermAI computes the SHA256 fingerprint and shows you:
SHA256:abc...xyz. You can tap to copy. The Accept button is the default; Cancel is also clearly available. Once accepted, the fingerprint is stored. - Subsequent connections. Fingerprint matches stored value → connection proceeds silently. No prompt, no warning, no interruption.
- Mismatch on later connection. Hard stop. Red dialog. The dialog explains what's happened, shows both the stored fingerprint and the new one, and explicitly does not have a one-tap Accept. Continuing requires going into Settings → Known Hosts and explicitly deleting the stored fingerprint, then reconnecting and re-trusting. Two-step process by design.
The two-step requirement is the important part. If a single tap can override a key mismatch warning, users will tap through it under time pressure. The interruption is what protects you.
Spotting an actual MITM
If your client warns about a key change, the question is: did the server's key actually change for a legitimate reason, or is something attacking the connection?
Legitimate reasons:
- You reinstalled the OS on the server
- You rotated the host key on purpose
- You DNS-pointed the hostname at a different server
- The server is behind a load balancer and you hit a different backend (configure same key on all backends)
Suspicious reasons:
- You're on a new Wi-Fi network you don't fully trust
- You just got a captive portal you didn't expect
- The server admin didn't tell you to expect a key change
- The fingerprint is one you've never seen even after looking at the legitimate server
The right move when in doubt: don't connect. Open the server's web console / cloud provider's serial console / a known-good machine on the same network, run ssh-keyscan against the server, and compare fingerprints. If they match the new key your client sees, you're fine. If they don't, you're talking to a MITM.
Legitimate key changes
If you legitimately rotate the host key (best practice: every couple of years), there's a clean way to do it without confusing every client:
- Generate the new host key but keep the old one accepted
- Let
sshdserve both for a transition period - Distribute the new fingerprint to users via a channel they trust (your team's password manager, a signed announcement)
- After everyone's updated, remove the old key
Or, for personal servers, just tell people: "I'm rotating the host key on Tuesday, here's the new fingerprint." A heads-up turns a warning dialog into an expected confirmation.
Free on iOS and Android. 3 SSH connections + 20 AI calls/day on the free tier.