■ FTPの特徴
FTPとは File Transfer Protocol
の略称で、ネットワーク上でのファイル転送を目的に開発されたクライアント/サーバシステムである。通常、FTPサーバはデータ転送用にTCP20番ポートを使用し、同21番ポートを制御用に使用する。FTPのようなクライアント/サーバ型のシステムではサーバとクライアントのOSが異なっていることが珍しくないので、サーバプログラムはOSの相違(文字コードやファイルフォーマットなど)を吸収するための機能を持っている。例えば
Windows クライアントから UNIX サーバにファイルを転送する場合、文字コードをEUCに直したり改行コードを変更したりする必要があるが、ftp
はファイルの種類によって ascii 転送モードまたは binary 転送モードを使い分けて対処する。
開発時点が古いことから、FTPは後述するコマンド群によって構成されている。ソフトウェア本体は非常にシンプルで無駄がないため、動作が高速で通信アプリケーションとしての信頼性も高い。このような理由でFTPは現在でも広く使用されているが、その一方でFTPそのものにはセキュア概念が貧弱、という重大な欠点がある。よく知られている通り、FTPや
telnet のような「古い」アプリケーションはログイン情報を平文で送信する。したがって、誰もがパケットキャプチャできるような環境で無造作にFTPを使うことは、セキュリティ上避けなければならない。同じ理由で、インターネット越しのアクセスが必要ならば
SSL によるトンネリングを考慮する必要がある。
また、FTPアプリケーションのセキュリティホールはシステム全体の致命傷となりうるので、常にアップデートを行う必要があることは言うまでもない。
※ TFTPについて(参考)
TFTPとは
Trivial FTP の略称であり、FTPからファイル転送機能だけを取り出したような非常に単純なプロトコルである。これはディスクレスコンピュータ(X端末など)をブートするために必要なファイルを転送するために使われたり、ヤマハのRTシリーズルータのファームやコンフィグを転送(GET/PUT)したりする際に使われることで知られている。TFTPは大変コンパクトで面白いプロトコルだが、基本的にサーバ上では使うべきでない通信手法である。というのは、FTPではユーザー名とパスワードを用いた最低限の認証を行うが、TFTPにはそもそも認証機能すら存在せず、セキュリティ上の問題となるからである。そのため事情があってTFTPを使用する場合は、ファイルのアクセス制御に配慮し、基本的に使用できるユーザを限定しなければならない。
ところで、データ通信の観点から考えると両者は明らかに別のものである。FTPでは下位のトランスポート層にTCPを用いることにより、信頼性のある転送を実現しているが、TFTPではUDPを用いる。このため、データを受信すると受信確認を送り、データの紛失を防いでいる。このようなTCPとUDPの使い分けを考えれば、両者の設計思想の違いがおのずから理解できるだろうと思う。
■ wu-FTPDについて
Linux にバンドルされている ftp デーモンである
wu-ftpd の特徴と利用上のメリットは以下のとおり。
1.アクセス数の制限が可能。これによってFTPアクセスによる回線帯域の占有を未然に防ぐことができる。
2.ユーザーの移動できるディレクトリの制限が可能。システム運用環境によってはパスワードによる認証が不可欠であり、セキュリティ上、ユーザが移動できるディレクトリを制限する必要がある。
3.圧縮ファイルへの融通性がある。wu-ftpd では圧縮ファイルを展開しながら転送したり、逆に圧縮しながらの転送も行なえる。
4.アクセス制限を細かく設定できる柔軟性がある。
5.応答性や速度、負荷に関する優秀性がある。
以上のメリットから、wu-ftpd
はインターネット上の Linux−FTPサーバとして、一時デファクトスタンダードの地位を占めていた。しかし、バグフィックスが相次いだことや、chroot
の設定が少し煩雑なこともあり、最近は ProFTPD の方が好まれているようである。あるいはProFTPD
のディレクティブ方式による設定が apache と似ていてわかりやすいせいかもしれない。
※ 重要:
wu-ftpd が急速に退潮した理由の一つとして、過去のバージョンにおける深刻なセキュリティホールの存在が指摘できる。これはバッファオーバフローを引き起こすもので、RedHat Linux 7.1
における wu-ftpd-2.6.1-16 を外向けサービスとして使っているシステムではいとも簡単に
root 権限が奪取され、rootkit 等を仕掛けられることが報告されている。wu-ftpd
を使用する場合は必ずバージョンを調べ、セキュリティホールのないものにアップグレードしておくこと。
■ FTPセッション
まず、基礎知識としてFTPのセッションフローを示す。
1.ホストコンピュータへの接続
2.ユーザーの認証とログイン
3.ターゲットファイルまたはディレクトリの探索
4.ターゲットファイルのダウンロードまたはアップロード
5.セッションの終了と接続の解除
■ FTPコネクション
FTP のコネクションの張り方は他の TCP アプリケーションと比べると異質である。まず、FTP では制御用とデータ転送用の2つのコネクションが張られる。制御用にはtcp/21 番ポート、データ転送用には tcp/20 番ポートを使い、それぞれ ftp および ftp-data と呼ぶ。興味深いのはこれらのコネクションの手順と方向である。
以下に ftp コネクションの手順を示す。
1.クライアントからサーバへの制御コネクション
a. クライアントからサーバの21番ポートへ接続要求を行う。このときクライアント側では暫定的に1024 以上のポート番号を使う。
b. サーバ側ではクライアントの接続要求に対してユーザ認証を行う。ユーザ認証に成功しなかった場合、サーバは接続を切断しセッションを終了させる。
c. ユーザ認証が成功した場合はクライアントからの制御コネクションが成立する。
d. 確立した制御コネクションを使ってユーザはコマンド操作(別表参照)を行う。このとき制御コネクションはクライアントの 1024 以上の非特権ポートからサーバの21番ポートへ張られる。
2.データ転送コネクション
a. コマンド操作によってデータ転送が行われる場合、サーバはクライアントへのデータコネクションを張る。なお、データコネクションはファイル転送のみならず、コマンドの結果を転送するのにも使われる。(参考として Linux 上で採取したキャプチャの結果を別項に上げておくので ls コマンドの結果がデータコネクションで転送されていることを確認せよ。)
データコネクションの張り方には2通りあり、それぞれ active モードと passive モードと呼ばれる。通常 FTP サーバにログインした直後はデフォルトで active モードであり、passive モードに入るにはクライアント側から passiv コマンドを発行する。passive コマンドを発行した後は、PORT コマンドの代わりに PASV コマンドが発行されるようになり、PORT コマンド同様の IP アドレス通知を行う。
a-1. active (サーバ能動)モード
コネクションはサーバの20番ポートからクライアントの不定ポート(1024 以上)へ張られる。active モードによるデータコネクション確立時、クライアントは自分のIPアドレスと待ち受けポート番号を PORT コマンドを使ってサーバに伝える。サーバは PORT コマンドによる情報を元にクライアントの指定ポートへの接続を行う。
a-2. passive (サーバ受動)モード
コネクションはクライアントの不定ポート(1024 以上)からサーバの20番ポートへ張られる。ただし、コネクション確立にあたっては、まずクライアントが PASV コマンドを使ってコネクションに使用するべきポート番号の通知をサーバに要求する。サーバは PASV コマンドに応答し、クライアントに対して自分のIPアドレスと待ち受けポート番号を通知する。(別項のキャプチャデータからクライアントが投げた PASV コマンドに対してサーバが IP アドレスの通知を行っていることを確認せよ。また、PASV コマンドではポートの通知を IP アドレスに続ける2つの引数で指定する。その値が毎回異なり、指定するポート番号が { 第 2 引数 * 256 + 第 3 引数 } で計算できることを確認せよ。)
以下にキャプチャを示す。なお、無駄に長くなるのを避けるため、以下のキャプチャからは window サイズ等は省いた。また、ここでは ls コマンドの応答としてデータチャネルが使われることを示すため、あえてデータのダウンロードアップおよびロードは行っていないことに注意されたい。
-----------------
※クライアントよりサーバへの 3-way handshake
10.10.5.203 10.10.5.105 TCP 55311 > ftp [SYN] Seq=0 Ack=0
10.10.5.105 10.10.5.203 TCP ftp > 55311 [SYN, ACK] Seq=0 Ack=1
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=1 Ack=1
※サーバからクライアントへの ready 応答
10.10.5.105 10.10.5.203 FTP Response: 220 ready, dude (vsFTPd 1.1.0: beat me, break me)
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=1 Ack=52
※サーバによる認証と login chat
10.10.5.203 10.10.5.105 FTP Request: AUTH GSSAPI
10.10.5.105 10.10.5.203 TCP ftp > 55311 [ACK] Seq=52 Ack=14
10.10.5.105 10.10.5.203 FTP Response: 530 Please login with USER and PASS.
10.10.5.203 10.10.5.105 FTP Request: AUTH KERBEROS_V4
10.10.5.105 10.10.5.203 FTP Response: 530 Please login with USER and PASS.
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=32 Ack=128
10.10.5.203 10.10.5.105 FTP Request: USER ******* ★ユーザ名
10.10.5.105 10.10.5.203 FTP Response: 331 Please specify the password.
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=45 Ack=162
10.10.5.203 10.10.5.105 FTP Request: PASS ************ ★パスワード
10.10.5.105 10.10.5.203 FTP Response: 230 Login successful. Have fun.
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=60 Ack=195
10.10.5.203 10.10.5.105 FTP Request: SYST
10.10.5.105 10.10.5.203 FTP Response: 215 UNIX Type: L8
※PASVコマンドの発行(クライアントのデフォルト動作)
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=66 Ack=214
10.10.5.203 10.10.5.105 FTP Request: PASV
10.10.5.105 10.10.5.203 FTP Response: 227 Entering Passive Mode (10.10.5.105,151,45)
※クライアントよりサーバへの 3-way handshake と LIST コマンドの発行(ユーザアクション)
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=72 Ack=264
10.10.5.203 10.10.5.105 TCP 55314 > 38701 [SYN] Seq=0 Ack=0
10.10.5.105 10.10.5.203 TCP 38701 > 55314 [SYN, ACK] Seq=0 Ack=1 ★151×256+45=38701
10.10.5.203 10.10.5.105 TCP 55314 > 38701 [ACK] Seq=1 Ack=1
10.10.5.203 10.10.5.105 FTP Request: LIST
※サーバ側レスポンスによる DATA コネクションの確立と Directory data の転送
10.10.5.105 10.10.5.203 FTP Response: 150 Here comes the directory listing.
10.10.5.105 10.10.5.203 FTP-DATA FTP Data: 263 bytes
10.10.5.105 10.10.5.203 FTP Response: 226 Directory send OK.
※DATA コネクションの終了
10.10.5.105 10.10.5.203 TCP 38701 > 55314 [FIN, ACK] Seq=264 Ack=1
10.10.5.203 10.10.5.105 TCP 55314 > 38701 [ACK] Seq=1 Ack=264
10.10.5.203 10.10.5.105 TCP 55314 > 38701 [FIN, ACK] Seq=1 Ack=265
10.10.5.105 10.10.5.203 TCP 38701 > 55314 [ACK] Seq=265 Ack=2
※QUIT コマンドの発行
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=78 Ack=327
10.10.5.203 10.10.5.105 FTP Request: QUIT
10.10.5.105 10.10.5.203 FTP Response: 221 Goodbye.
※FTP セッションの終了
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=84 Ack=341
10.10.5.203 10.10.5.105 TCP 55311 > ftp [FIN, ACK] Seq=84 Ack=341
10.10.5.105 10.10.5.203 TCP ftp > 55311 [FIN, ACK] Seq=341 Ack=85
10.10.5.203 10.10.5.105 TCP 55311 > ftp [ACK] Seq=85 Ack=342
-----------------
■ FTPのためのファイアウォール
前項で述べたとおり、FTP ではサーバ側からとクライアント側からのコネクションが別々に張られるため、アクティブモードの転送を行うサーバに対しては、クライアント側でデータ転送コネクション用の 20 番ポートを開けておかなければならない。これはとりもなおさず、ファイアウォールにおいても外向けに 20 番ポートを開けておかなければ、アクティブモードの FTP サーバとは通信できないということである。
しかし、外部に向けて特権ポートを空けておくことは、セキュリティ上決して好ましいことではない。このため、近年の FTP サーバはパッシブモードで転送を行うのがデフォルトとなっているものが多い。ところが、パッシブモードの FTP にも問題がないわけではない。a-2 で述べたとおり、データ転送時にサーバが指定する待ち受けポートは 1024 番以上であるものの、毎回計算ではじき出す不定ポートとなっている。これはパッシブモードの FTP サーバと通信を行いたいなら、クライアント側で送信用の 1024 番以上のポートと受信用の 1024 番以上のポートをすべて開けておかなければならないことを意味する。
これはある意味で 20 番を開けておくよりも厄介で危険かもしれない。そこで現在のルータやファイアウォールでは FTP の通信仕様に真正直に付き合おうとするのではなく、POST や PASV コマンドが使われたとき前もって内容を調べておき、内部のエントリに登録するようになっている。この機能を特にステートフルインスペクションと呼ぶが、Linux では connection track と呼び、カーネルモジュールとしてロードされる。
Linux での conntrack モジュールのステータステーブルは以下のようになっている。
NEW 新しくテーブルに追加されたパケット
ESTABLISHED 状態テーブルにエントリがあるパケット
RELATED 新しくテーブルに追加されたパケットだが既存の接続と関連しているもの(FTP はこれに相当)
INVALID テーブルにエントリがないパケット
パケットを受信すると、カーネルはそのパケットのステータスをエントリに照らし合わせて、新しく送られてきたものか、既存のコネクションに関連のあるものか、単に戻りパケットなのかを判断する。この機能によって FTP サーバのポート指定要求は既存のコマンドチャネルに関連のあるパケットとして解釈され、指定ポートの使用が可能になる。言い換えれば conntrack モジュールをローダブルモジュールとしてカーネルに組んでおき、iptables に -m --state オプションを設定すれば、1024 番ポート以上を動的に開放してくれる。
■ FTP用 iptables の設定
Linux router でのファイアウォール設定は iptables を使って行う。本稿はファイアウォールの設定を解説するのが目的ではないので、ここでは簡略に FORWARD の設定のみを記述するが、実際には ip_connntrack モジュールのロードなど前段階が必要であることをお断りしておく。
以下にアクティブモード FTP 用の iptables の設定例を示す。
iptables -A FORWARD -i OUTSIDE_IF -p tcp --sport 20 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i INSIDE_IF -p tcp --dport 20 -m state --state ESTABLISHED -j ACCEPT
以下にパッシブモード FTP 用の iptables の設定例を示す。
iptables -A FORWARD -i OUTSIDE_IF -p tcp --sport 1024: --dport 1024: -m state --state ESTABLISHED -j ACCEPT
iptables -A FORWARD -i INSIDE_IF -p tcp --sport 1024: --dport 1024: -m state --state ESTABLISHED,RELATED -j ACCEP
■ 参考資料;ftpコマンド一覧
別表のコマンド一覧を見れば、それぞれのアクションにFTPコマンドが対応していることがわかる。近年、FTPサーバへのユーザインタフェースはWEBアプリケーションの形で提供されることがほとんどだが、本来はコマンド群によって構成されるツールである。一般ユーザがコマンドに触れることはほとんどないものの、管理者ユーザはスクリプトによる自動ファイル転送を実行したり、その動作を理解するためにも一通りのコマンドを使える必要がある。以下にFTPコマンドの一覧を示す。
FTPコマンド一覧
! |
サブシステムからシェルへの復帰 |
? 又は help |
コマンドヘルプの表示 |
append |
現在のファイル転送モードの種類の設定を使って、ローカルファイルをサーバのファイルに追加する |
ascii |
ファイル転送モードをASCIIに設定する(既定値) |
bell |
各ファイル転送コマンドが完了するたびにベルを鳴らす |
binary |
ファイル転送モードをバイナリに設定(圧縮ファイルなどはbinaryを指定すること) |
bye |
サーバのFTPセッションを終了して、コマンドインタプリタに復帰 |
cd |
サーバの現在の作業ディレクトリの変更 |
close |
リモートサーバとのFTPセッションを終了して、コマンドインタプリタに復帰 |
debug |
デバッグ機能(オンになっているとき、サーバに送信する各コマンドの先頭に文字列「<」を付けて出力する
) |
delete |
サーバ上のファイルを削除 |
dir |
リモートディレクトリのファイルとサブディレクトリの一覧表示 |
disconnect |
プロンプトを残したまま、サーバから切断する |
get又はrecv |
現在のファイル転送モードを使って、リモートファイルをローカルコンピュータにコピーする |
glob |
ファイル名のglobbing切替(globbingにより、ローカルファイル又はパス名にワイルドカード文字<*と?>を使用可能) |
hash |
送られるデータブロックごとのハッシュ記号(#)の出力機能を切り替える。(データブロックのサイズは2048バイト) |
lcd |
ローカルコンピュータの作業ディレクトリを変更(デフォルトでは、FTPを開始したディレクトリが作業ディレクトリ) |
literal |
引数を変更せずリモートFTPサーバに送信する。(予期される戻り値は単一のFTP返信コード) |
ls |
一覧を表示したいディレクトリ又はファイルを指定 |
mdelete |
サーバの複数のファイルを削除 |
mdir |
リモートディレクトリのファイルとサブディレクトリの一覧表示(mdirを使って、ローカルコンピュータにコピーする) |
mget |
現在のファイル転送モードで複数のリモートファイルをローカルコンピュータにコピーする。 |
mkdir |
リモートディレクトリの作成 |
mls |
リモートディレクトリのファイルとサブディレクトリの省略形の一覧表示 |
mput |
現在のファイル転送モードで複数のローカルファイルをサーバにコピー |
open |
指定したFTPサーバに接続 |
prompt |
プロンプト機能切替(複数ファイルの転送時にはファイルを選択して取り出すか格納できるようにFTPによりプロンプトが表示される) |
put又はsend |
現在のファイル転送モードでローカルファイルをサーバにコピー
|
pwd |
サーバの現在のディレクトリを表示 |
quit |
サーバとのFTPセッションを終了してFTPを終了 |
quote |
引数を変更せずリモートFTPサーバに送信(予期される戻り値は単一のFTP返信コード) |
remotehelp |
リモートコマンドのヘルプ表示 |
rename |
リモートファイルの名前の変更 |
rmdir |
リモートディレクトリの削除 |
status |
FTP接続と切り替えコマンドの現在の状態表示 |
trace |
パケットトレース機能切替(FTPコマンドを実行するときに、traceにより各パケットのルートが表示される) |
type |
ファイル転送モードの設定又は表示 |
user |
サーバに対するユーザの指定 |
verbose |
詳細モードの切替(詳細モードがオンになっているとFTPの応答をすべて表示する。ファイル転送が完了すると転送効率に関する統計情報も表示される) |
|
|
|
ここでクライアントからコマンドによってFTPセッションを開くスクリプトの原型を示す。サンプルにおけるテキスト転送モードはアスキーを指定してあるが、バイナリ転送が必要ならばモードをバイナリに指定すること。なお、冒頭に述べたとおりFTPはネットワーク上にユーザ名とパスワードを平文で流してしまうので、プロトコルとしてセキュアとは言えない。よってこのスクリプトを使う場合はSSHでトンネリングするなど、セキュリティに配慮する必要がある。cron
に仕掛けて実行させる場合は、sftp を使うことが望ましい。
#!/bin/bash
ftp
open FTPserver
username
password
ascii
get /target ←ユーザのホームディレクトリから見たファイル
quit
※ 赤字部分は固有名に置き換えること
|
もちろん
quit は bye でもよい。
■ wu-ftpd のインストール
RedHat
のようなメジャーなディストリビューションでは wu-ftpd
がバンドルされているので、インストール時にパッケージとして選択することができる。 通常
wu-ftpd は Linux インストール時にサーバ設定、またはカスタム設定で FTP を選択すればインストールされるが、もしインストールされていなかったり、バージョンアップやバグフックス版が出た場合は、Red
Hat から rpm パッケージをダウンロードすればよい。 rpmパッケージの展開とインストールについてはrpmの
man ページを参照すること。通常、wu-ftpd をインストールすると古い ftp は自動的に上書きされるが、別の
ftp が先にインストールされていた場合は、念のため旧パッケージをアンインストールしておいた方が確実である。
rpm -e 旧パッケージ (依存関係を無視する場合は
--nodeps オプションを使うこと)
ソースファイルからコンパイルしてインストールする場合は、http://www.wu-ftpd.org/ から最新版をダウンロードする。これをたとえば
/usr/local/src などの適切なディレクトリに置き、定石通り.configureからmakeすればよい。このときpath
の変更が必要ならば、コンパイル時に pathname.h を編集し、環境に合わせたPathを記述しておく。
(ちなみに本稿執筆時点の最新バージョンは wu-ftpd2.6.2 となっている。上記URLの開発元は英文サイトだが、マニュアル等の資料もすべてそろっているので、詳しい情報が必要な向きはこちらの参照をおすすめする。)
|