← All posts

FTPS and self-signed certificates: when allowing one is safe

An FTPS server with a self-signed certificate still encrypts your traffic, but the certificate proves nothing about who you're talking to. Anyone able to intercept the connection can present their own self-signed certificate, and a client that accepts any certificate can't tell the difference. So allowing an FTPS self-signed certificate is reasonable only for a server you control, reached over a network you trust, after you've checked its fingerprint yourself. Everywhere else, the fix is a certificate the client can verify. This post explains why, how to check a certificate from the terminal, and the better alternatives.

What certificate validation actually protects

TLS does two jobs. It encrypts the connection, and it authenticates the server: the certificate must be signed by an authority your system trusts, and it must name the hostname you connected to. Encryption without authentication protects you from someone passively listening, but not from someone in the middle. An attacker on the path (a hostile Wi-Fi network, a compromised router) completes TLS with you using their own certificate, and a second TLS session with the real server. Both legs are encrypted; the attacker reads everything in between, including the FTP USER and PASS commands.

Certificate validation is what stops that. A self-signed certificate fails validation because nothing vouches for it. Turning the check off makes the connection work, and also removes the protection.

When allowing an FTPS self-signed certificate is reasonable

It's a trade-off, not a rule. Accepting a self-signed certificate is defensible when all of these are true:

It's not reasonable for a server on the public internet that you reach from cafés and hotels, for a third party's server, or as a quick fix for "the certificate expired".

Checking an FTPS certificate from the terminal

OpenSSL's s_client can connect to either kind of FTPS and show you the certificate. For explicit FTPS on port 21, which upgrades with AUTH TLS:

openssl s_client -connect ftp.example.com:21 -starttls ftp \
  -servername ftp.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -fingerprint -sha256

For implicit FTPS on port 990, which is TLS from the first byte, drop -starttls ftp:

openssl s_client -connect ftp.example.com:990 \
  -servername ftp.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -fingerprint -sha256

Then, on the server itself (over SSH, or at its console), print the fingerprint of the certificate file it's configured with:

openssl x509 -in /etc/ssl/certs/vsftpd.pem -noout -fingerprint -sha256

If the two SHA-256 fingerprints match, the certificate you received is the one on the server. If they don't, stop and find out why. The difference between the two FTPS modes is covered in FTPS explicit vs implicit.

Better options than a self-signed certificate

A free public certificate

If the server has a public DNS name, a certificate from Let's Encrypt costs nothing, renews automatically and validates everywhere. FTP servers can use the same certificate files as a web server on the same host. For hosts that aren't reachable from the internet, the ACME DNS challenge proves control of the domain through a DNS record instead of an HTTP request, so internal servers with public names can still get certificates. See the Let's Encrypt documentation for the challenge types.

Your own certificate authority

For a fleet of internal machines, a small private CA lets every server have a properly signed certificate. You trust the CA once on each client, and validation then works normally, including hostname checks. This is more set-up than a public certificate, and only worth it when public names aren't an option.

A different protocol

If you control the server, SFTP sidesteps certificates entirely. It authenticates the server with an SSH host key, which a client pins on first connection and warns about if it ever changes. The SFTP vs FTP vs FTPS comparison covers when each makes sense.

If you must generate a self-signed certificate

Make it name the host properly, so at least the hostname matches:

openssl req -x509 -newkey rsa:2048 -nodes -days 825 \
  -keyout ftp.key -out ftp.crt \
  -subj "/CN=ftp.internal.example" \
  -addext "subjectAltName=DNS:ftp.internal.example"

How Wharf handles FTPS certificates

Wharf validates FTPS certificates by default. The certificate must chain to a trusted authority and match the hostname in the site's settings, and Wharf won't negotiate anything older than TLS 1.2. It also sends PROT P after login, so file contents on the data connection are encrypted as well as the commands, and refuses the connection if the server won't protect the data channel.

When validation fails, the connection stops with an error saying the server's certificate was not trusted, followed by OpenSSL's reason, such as a self-signed certificate. For a server you control, edit the site and turn on Allow untrusted certificate under Advanced. The option only appears for FTPS sites, and the note beneath it turns orange while it's on: "Only enable this for a server you control with a self-signed certificate. It disables the check that the server is who it claims to be."

Be clear about what that switch does. It's per site, so other sites keep full validation. But it doesn't remember a particular certificate: while it's on, any certificate that site presents is accepted. That is why the fingerprint check above matters, and why the option belongs only on servers reached over networks you trust. The support page describes the same setting under "My FTPS server's certificate is rejected".

If a connection fails for some other reason, View › Server Log… (⌘⇧K) shows the FTP conversation, including the AUTH TLS, PBSZ and PROT replies, with passwords redacted. Wharf's FTPS support, explicit and implicit, is listed on the features page.

The FTPS mechanics are specified in RFC 4217, if you want the details of how AUTH TLS and PROT P fit together.