「FTPSとSFTPは名前が似ているが、結局どちらを使えばいいのか判断できない」
この2つは名前が似ているだけで、中身がまったく別のプロトコルです。FTPSは従来のFTPをTLSで暗号化したもの、SFTPはSSHのサブシステムとして動く別設計のプロトコルで、互換性はありません。ここを取り違えたまま設定を進めると、いつまでも接続できません。
この記事では、FTPSの明示モードと暗黙モードの違い、curlとlftpでの実際の接続手順、vsftpdでのサーバー側設定、そして実務でFTPSとSFTPのどちらを選ぶかの判断基準までを解説します。curlの実行例は公開テストサーバー(test.rebex.net)とftp.gnu.orgに対してcurl 8.5.0で実際に接続し、取得した出力をそのまま掲載しています。lftp・vsftpd.conf・firewalldの各ブロックは設定例であり、実行結果ではありません。
この記事のポイント
・FTPSはFTP+TLS、SFTPはSSHのサブシステムで別物
・明示モードはcurl --ssl-reqd ftp://、暗黙モードはftps://
・RFC 4217が定義するのは明示モードのみ。暗黙モードは規格外
・新規案件で選べるならSFTPが有利。FTPSは相手都合で使う
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
FTPSとSFTPは何が違うのか|名前が似ているだけの別プロトコル
現場でもっとも多い混乱が、この2つの取り違えです。結論から書くと、両者に互換性はありません。・FTPS:従来のFTP(RFC 959)に、TLSによる暗号化を後付けした拡張。制御用とデータ用の2本のコネクションを使うFTPの構造をそのまま引き継いでいる
・SFTP:SSHプロトコルのサブシステムとして動く、ゼロから設計された別プロトコル。コネクションはSSHの1本だけ
名前に「FTP」が入っているのでFTPの仲間に見えますが、SFTPの実体はSSHです。
sftpコマンドで接続できるのはSSHサーバー(22番ポート)であり、FTPSサーバーには接続できません。逆も同じです。| 比較項目 | FTPS | SFTP |
|---|---|---|
| ベースになる技術 | FTP + TLS | SSH v2のサブシステム |
| 規格 | RFC 4217(RFC 2228のTLS実装) | RFCなし(IETFドラフトのまま失効) |
| ポート | 21番(明示)/ 990番(暗黙) | 22番(SSHと共用) |
| コネクション本数 | 制御とデータで2本 | 1本のみ |
| ファイアウォール通過 | パッシブポート範囲の開放が必要 | 22番だけで完結 |
| 認証方式 | ID・パスワード+サーバー証明書 | ID・パスワードまたは公開鍵認証 |
| Linuxでのクライアント | curl --ssl-reqd / lftp |
sftp / scp |
SFTPをシェルスクリプトから自動化したい場合は、シェルスクリプトでSFTPファイル転送を自動化する設計でバッチモードとSSH鍵認証の実装方法を解説しています。
FTPSの明示モードと暗黙モード|どちらを使うべきか
FTPSにはTLSを開始するタイミングが異なる2つのモードがあります。ここを理解していないと、ポート番号もオプションも決まりません。1. 明示モード(Explicit FTPS / FTPES)
まず平文でFTPの21番ポートに接続し、その後AUTH TLSコマンドでTLSに切り替える方式です。RFC 4217が規定しているのはこの明示モードだけで、FTPSと言えば通常こちらを指します。TLS化された後の手順もRFC 4217で決まっています。
・AUTH TLS:制御コネクションのTLSネゴシエーションを要求する。サーバーは受理時に234を返す
・PBSZ 0:保護バッファサイズの指定。TLSがストリーム保護を行うため、RFC 4217では必ず0を指定する
・PROT P:データコネクションも暗号化(Private)する。
PROT Cを指定するとデータは平文(Clear)のまま流れる2. 暗黙モード(Implicit FTPS)
990番ポートに接続した直後、FTPの応答を受け取る前にいきなりTLSハンドシェイクを始める方式です。IANAにはftps=990番(制御)、ftps-data=989番(データ)として登録されています。ただし暗黙モードはRFC 4217では一切定義されていません。RFC 4217の前身にあたるIETFドラフト(draft-murray-auth-ftp-ssl-07)の付録で「歴史的な手法」として触れられており、そこには「この方式はIETFに支持されておらず、新規のFTP-TLS実装で使うべきではない」と明記されています。990番を使う慣行は、規格化される前から存在した事実上の標準にすぎません。
「RFC 4217は暗黙モードも定義している」と書いている解説を見かけますが、これは誤りです。新規に構築するなら明示モードを選んでください。暗黙モードは、相手のサーバーがそれしか受け付けない場合の互換用と考えるのが実務的です。
curlでFTPSに接続する実践手順
curlはFTPSに標準対応しています。まず自分の環境のcurlがftpsを話せるか確認します。$ curl --version curl 8.5.0 (aarch64-unknown-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.10 Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.13 Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
Protocols:の行にftpsとsftpの両方が並んでいます。curl一本でどちらも扱えますが、指定するスキームはまったく別になります。1. 明示モードで接続する
明示モードはスキームはftp://のまま、--ssl-reqdを付けるのが正解です。公開テストサーバーに接続し、ディレクトリ一覧を取得します。# --ssl-reqd はTLSに昇格できなければ接続を打ち切る(平文にフォールバックしない) $ curl -v --ssl-reqd --user demo:password --list-only ftp://test.rebex.net/ < 220-Welcome to test.rebex.net! > AUTH SSL < 234 Switching to TLS. * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 > USER demo < 331 Anonymous login OK, send your complete email address as your password. > PASS password < 230 User 'demo' logged in. > PBSZ 0 < 200 PBSZ OK. > PROT P < 200 PROT OK. > EPSV < 229 Entering Extended Passive Mode (|||1039|) < 226 Transfer complete. pub readme.txt
AUTHで234が返ってTLSに切り替わり、その中でUSER・PASSを送り、PBSZ 0とPROT Pでデータコネクションも暗号化しています。なお、curlが最初に送るのは
AUTH SSLです。サーバーが拒否した場合にAUTH TLSを再送する実装になっています。2. 暗黙モードで接続する
暗黙モードはスキームをftps://にするだけです。ポートを省略すると990番に接続します。$ curl -v --user demo:password --list-only ftps://test.rebex.net/ * Connected to test.rebex.net (194.108.117.16) port 990 * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 < 220-Welcome to test.rebex.net! > USER demo < 331 Anonymous login OK, send your complete email address as your password. > PASS password < 230 User 'demo' logged in. > PBSZ 0 < 200 PBSZ OK.
AUTHコマンドが1回も送られていません。これが暗黙モードの正体です。接続先ポートも990番になっています。3. 平文FTPとの違いを目で確認する
--ssl-reqdを外すと何が起きるかも見ておきます。$ curl -v --user demo:password --list-only ftp://test.rebex.net/ < 220-Welcome to test.rebex.net! > USER demo < 331 Anonymous login OK, send your complete email address as your password. > PASS password < 230 User 'demo' logged in.
AUTHもTLSハンドシェイクもありません。USERとPASSがそのままネットワークに流れています。同じ21番ポートでも、オプション1つで平文と暗号化が切り替わるということです。4. --ssl と --ssl-reqd を混同しない
curlには似た名前のオプションが2つあり、意味が決定的に違います。・--ssl-reqd:TLSを必須にする。昇格できなければ接続を終了する。実務ではこちらを使う
・--ssl:TLSを試みるが、失敗したら平文接続のまま続行する。暗号化された保証がない
--sslは旧名--ftp-ssl、--ssl-reqdは旧名--ftp-ssl-reqdで、古い記事や既存スクリプトには旧名が残っていることがあります。どちらの旧名も現在は別名として動作しますが、curl 8.5.0のmanで「将来のバージョンで削除される可能性がある」と注記されているのは--ssl(--ftp-ssl)の項だけで、--ssl-reqdの項は旧名の併記にとどまります。いずれにせよ新規に書くなら現行名を使ってください。TLS非対応のサーバーに
--ssl-reqdで接続すると、次のようにAUTHが拒否されて接続が終了します。$ curl --ssl-reqd --user anonymous:user@example.com --list-only ftp://ftp.gnu.org/ > AUTH SSL < 530 Please login with USER and PASS. > AUTH TLS < 530 Please login with USER and PASS. $ echo $? 64
CURLE_USE_SSL_FAILED)で落ちています。平文に落ちずに失敗する、これが--ssl-reqdを使う理由です。スクリプトではこの終了コードで異常を検知できます。5. ファイルを送受信する
接続が通れば、あとは通常のcurlと同じです。# ダウンロード(-O でリモート側のファイル名のまま保存) $ curl --ssl-reqd --user demo:password -O ftp://test.rebex.net/readme.txt # アップロード(-T でローカルファイルを送る) $ curl --ssl-reqd --user user01:secret -T backup.tar.gz ftp://ftps.example.com/upload/ # 自己署名証明書のサーバーへ、CA証明書を明示して検証する $ curl --ssl-reqd --cacert /etc/pki/tls/certs/company-ca.crt --user user01:secret -T backup.tar.gz ftp://ftps.example.com/upload/
--userを直接書かず、~/.netrc(パーミッション600)に資格情報を置いて--netrcで読ませてください。lftpでFTPSを使う設定
対話的な操作やミラーリングにはlftpが向いています。lftpはftp:ssl-allowが既定でtrueのためTLSのネゴシエーションは試みますが、サーバーがTLS非対応だった場合は平文のまま接続を続行してしまう点に注意が必要です(curlの--sslと同じ挙動)。~/.lftprcに次のように書いておきます。# ~/.lftprc # TLS非対応サーバーへ平文パスワードを送らない(既定はfalse) set ftp:ssl-force true # データ転送にもTLSを要求する(PROT P 相当。既定はtrue) set ftp:ssl-protect-data true # サーバー証明書をCA署名済み・未失効であることまで検証する set ssl:verify-certificate yes
ftp:ssl-forceがlftpにおける--ssl-reqd相当の設定です。この3行を入れておけば、明示モードでTLSが張れないサーバーには接続しません。証明書検証を通せない検証環境では
set ssl:verify-certificate noで一時的に外せますが、本番環境で恒久的に無効化しないでください。中間者攻撃を検知できなくなり、暗号化する意味が薄れます。接続とミラーリングは次のように実行します。
# 明示モードで接続する(スキームは ftp:// のまま) $ lftp -u user01,secret ftp://ftps.example.com # ローカルディレクトリをリモートへ同期する(lftpのプロンプト内) lftp user01@ftps.example.com:/> mirror -R ./localdir /remote/dir
ftp:ssl-auth(AUTHコマンドに渡す引数。SSL / TLS / TLS-P / TLS-Cから選ぶ)とftps:initial-prot(FTPS接続時の初期PROT設定)でネゴシエーションの細部を調整できます。相手サーバーが特定のAUTH引数しか受け付けない場合の逃げ道になります。なお、lftpで暗黙モードを使う指定方法については、man pageの記述だけでは
ftps://スキームが暗黙モードに直結するのかを確定できませんでした。暗黙モードのサーバーに接続する必要がある場合は、まずcurl ftps://で疎通を確認し、lftp側は使用するバージョンのman pageで挙動を確認してから設定してください。vsftpdでFTPS対応のFTPサーバーを構築する
受け側を自分で用意する場合の最小構成です。/etc/vsftpd/vsftpd.confに次を追記します。# /etc/vsftpd/vsftpd.conf # TLSサポートを有効化する(既定はNO) ssl_enable=YES rsa_cert_file=/etc/pki/tls/certs/vsftpd.pem rsa_private_key_file=/etc/pki/tls/private/vsftpd.key # ローカルユーザーのログインとデータ転送にTLSを必須化する force_local_logins_ssl=YES force_local_data_ssl=YES # 古いプロトコルを閉じる # ssl_sslv2 / ssl_sslv3 の既定はNO、ssl_tlsv1(TLS 1.0)の既定はYES。 # TLS 1.0 を閉じるには ssl_tlsv1=NO を明示的に書く必要がある ssl_sslv2=NO ssl_sslv3=NO ssl_tlsv1=NO # パッシブモードのポート範囲を固定する(ファイアウォール設定とセットで必須) pasv_enable=YES pasv_min_port=50000 pasv_max_port=50100
ssl_tlsv1=NOを明示しているのが要点です。ssl_sslv2とssl_sslv3は既定値がすでにNOなので書いても実質何も変わりませんが、ssl_tlsv1(TLS 1.0)の既定はYESです。この行を書かないと、SSLv2/v3を閉じたつもりでもTLS 1.0は開いたままになります。なお
ssl_tlsv1_1・ssl_tlsv1_2という指定を紹介している解説がありますが、これらは上流vsftpdのman 5 vsftpd.confには存在しません。vsftpd 3.0.4以降、またはRHEL系ディストリビューションのパッチ適用版にのみ存在するオプションです。Ubuntu 18.04・20.04が同梱する素の3.0.3に書くと、次のエラーでvsftpdが起動しなくなります。# 素のvsftpd 3.0.3 に ssl_tlsv1_2 を書いて起動した場合 500 OOPS: unrecognised variable in config file: ssl_tlsv1_2
man 5 vsftpd.confで自分の環境のvsftpdにそのオプションが載っているかを確認してから書いてください。載っていなければ書いてはいけません。暗黙モード(990番)で待ち受けさせたい場合は
implicit_ssl=YESを追加しますが、前述のとおり規格外の方式なので、相手の要求がない限り有効にしないでください。ファイアウォール側も制御ポートとパッシブポート範囲の両方を開けます。
# firewalldでFTPSの制御ポートとパッシブポート範囲を開放する # firewall-cmd --permanent --add-port=21/tcp # firewall-cmd --permanent --add-port=50000-50100/tcp # firewall-cmd --reload # 実際にListenしているか確認する # ss -lntp | grep vsftpd
mod_tlsを有効にし、TLSEngine on、TLSRSACertificateFile、TLSRSACertificateKeyFile、TLSRequired onを設定します。ユーザーを自分のホームディレクトリに閉じ込めるchrootなどのアクセス制御についてはproFTPDでアクセス制限設定を行うで解説していますが、こちらはTLSを扱っていないため、FTPS化する場合は上記のmod_tls設定を別途追加してください。FTPSがつながらない時のトラブルシュート
1. 「522 SSL connection failed; session reuse required」が出る
vsftpdでもっとも遭遇するエラーです。原因はrequire_ssl_reuseで、デフォルトはYESです。これはデータコネクションに対して、制御コネクションと同じTLSセッションの再利用を要求する仕組みです。データコネクションの乗っ取りを防ぐセキュリティ機構ですが、クライアント側の実装が対応していないと接続に失敗します。
相手のクライアントを変更できない場合は、次の設定で緩和します。
# /etc/vsftpd/vsftpd.conf # クライアント互換性のためセッション再利用の要求を外す(安全性は下がる) require_ssl_reuse=NO
2. 認証は通るがファイル一覧の取得で固まる
制御コネクションは通っているのにLISTで止まる場合、ほぼ確実にデータコネクション側のポートが塞がっています。FTPは制御用とデータ用で別々のコネクションを張るプロトコルです。制御コネクションがTLSで暗号化されると、ファイアウォールが中身を覗いてデータ用ポートを動的に開ける従来の仕組みが機能しなくなります。これがFTPSでパッシブポート範囲の明示設定が必須になる理由です。
サーバー側でポートが実際に開いてListenしているかを確認します。ss・netstat・lsofを使ったポート確認手順はLinux ポート確認の全コマンドで解説しています。
# パッシブポート範囲がファイアウォールで開いているか確認する # firewall-cmd --list-ports # クライアント側からパッシブポートへ到達できるか確認する $ nc -zv ftps.example.com 50000
3. 証明書の検証に失敗する
SSL certificate problemが出る場合は、サーバー証明書がクライアント側のCAストアで検証できていません。# サーバー証明書の内容と有効期限を確認する(明示モードのFTPSを指定) $ openssl s_client -connect ftps.example.com:21 -starttls ftp # 社内CAの証明書を指定して検証を通す $ curl --ssl-reqd --cacert /etc/pki/tls/certs/company-ca.crt --user user01:secret --list-only ftp://ftps.example.com/
-k(--insecure)で検証を飛ばせば確かにつながりますが、それは暗号化の意味を捨てる操作です。切り分けの一時手段にとどめ、恒久運用では正しいCA証明書を配置してください。実務でFTPSとSFTPのどちらを選ぶか
技術的な優劣より、選択の余地があるかどうかで決まります。・自分で決められるなら、SFTPを選ぶ:ポートは22番の1つだけで、パッシブポート範囲もファイアウォールの追加設定も要りません。公開鍵認証が使えるためパスワードレスの自動化と相性がよく、鍵の失効管理も既存のSSH運用に乗せられます
・相手がFTPSを指定してきたら、FTPSを使う:金融機関やEDI、既存の業務パッケージなど、FTPS前提で作られたシステムは現実に数多く動いています。この場合は明示モードを第一候補にし、暗黙モードは相手が990番しか受け付けない場合に限る
・既存の平文FTPが残っているなら、まずFTPSで暗号化する:SFTPへの移行はクライアント側の変更も伴うため時間がかかります。サーバー側に
ssl_enable=YESを入れて平文を閉じるほうが、着手までが速く効果も即座に出ますつまりFTPSは「積極的に選ぶプロトコル」ではなく、相手の都合と既存資産の都合で使うプロトコルです。新規に設計できる場面でわざわざFTPSを選ぶ理由は、ほとんどありません。
本記事のまとめ
| やりたいこと | コマンド・設定 |
|---|---|
| curlがftpsに対応しているか確認する | curl --version |
| 明示モードでFTPS接続する | curl --ssl-reqd --user ID:PW ftp://host/ |
| 暗黙モード(990番)でFTPS接続する | curl --user ID:PW ftps://host/ |
| FTPSでファイルをアップロードする | curl --ssl-reqd --user ID:PW -T file ftp://host/dir/ |
| 社内CAで証明書検証を通す | curl --ssl-reqd --cacert /path/ca.crt ftp://host/ |
| lftpでTLSを強制する | set ftp:ssl-force true(~/.lftprc) |
| vsftpdでFTPSを有効化する | ssl_enable=YES(vsftpd.conf) |
| 522 session reuse エラーを回避する | require_ssl_reuse=NO(vsftpd.conf) |
| FTPS用の証明書を確認する | openssl s_client -connect host:21 -starttls ftp |
ファイル転送とサーバー構築を体系的に学びたい方へ
FTPSとSFTPを正しく選び分けるには、TLSとSSHの仕組み、ファイアウォールとポートの関係、証明書の扱いまでを地続きで理解している必要があります。オプションを個別に暗記するより、サーバー構築の全体像を押さえたほうが、初見のトラブルにも自力で対処できるようになります。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、「Linuxサーバー構築入門マニュアル(図解60P)」を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:proFTPDでアクセス制限設定を行う
- この記事の属するカテゴリ:FTPサーバー管理へ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます