はじめに
現場のIT環境において、安全なファイル転送は極めて重要な要件です。しかし、多くの開発者やシステム管理者が「FTP、FTPS、SFTP、SCP、どれを選べばいいの?」と迷うことがあります。
本ブログでは、これら4つのファイル転送プロトコルのセキュリティ面での違いを詳しく解説し、適切な選択ができるよう書いて行きたいと思います。
各プロトコルの基本概要
| 項目 | FTP | FTPS | SFTP | SCP | 
|---|---|---|---|---|
| 正式名称 | File Transfer Protocol | FTP over SSL/TLS | SSH File Transfer Protocol | Secure Copy Protocol | 
| 開発年 | 1971年 | 1990年代後半 | 1990年代後半 | 1990年代中期 | 
| 基盤技術 | TCP/IP | FTP + SSL/TLS | SSH-2 | SSH | 
| 暗号化 | ❌ なし | ✅ SSL/TLS | ✅ SSH暗号化 | ✅ SSH暗号化 | 
| 認証方式 | 平文パスワード | 証明書/パスワード | 公開鍵/パスワード/多要素 | 公開鍵/パスワード | 
| デフォルトポート | 21(制御), 20(データ) | 990(Implicit), 21(Explicit) | 22 | 22 | 
| ファイアウォール対応 | 🔴 困難(複数ポート) | 🔴 困難(複数ポート) | 🟢 簡単(単一ポート) | 🟢 簡単(単一ポート) | 
| 対話的操作 | ✅ 可能 | ✅ 可能 | ✅ 可能 | ❌ 不可 | 
| レジューム機能 | ✅ 対応 | ✅ 対応 | ✅ 対応 | ❌ 非対応 | 
| ディレクトリ操作 | ✅ 豊富 | ✅ 豊富 | ✅ 豊富 | ❌ 限定的 | 
| 設定複雑度 | 🟢 簡単 | 🔴 複雑 | 🟡 普通 | 🟢 簡単 | 
| セキュリティレベル | 🔴 極めて低い | 🟡 中程度 | 🟢 高い | 🟢 高い | 
| パフォーマンス | 🟢 高速 | 🟡 中程度 | 🟡 中程度 | 🟢 高速 | 
| 現在の位置づけ | ❌ 使用禁止レベル | ⚠️ レガシー移行用 | ✅ 現代的標準 | ✅ 限定用途推奨 | 
| 主な用途 | 内部テスト(非推奨) | レガシーシステム移行 | 汎用ファイル転送 | 単純ファイルコピー | 
| RFC仕様 | RFC 959 | RFC 4217 | RFC 4251-4254 | RFC 4251-4254 | 
セキュリティ詳細比較
🔓 FTP:セキュリティリスクの宝庫
リスク評価: ★★★★★ (最高危険度)
主要なセキュリティ問題
- 平文通信: ユーザー名、パスワード、ファイル内容すべてが暗号化されずに送信
 - パケット盗聴: Wiresharkなどで簡単に認証情報を取得可能
 - 中間者攻撃: 通信の改ざんや偽装が容易
 - ファイアウォール問題: アクティブ/パッシブモードの複雑なポート制御
 
実際の攻撃例
# Wiresharkでのパケット解析例
# FTPログイン時のキャプチャ結果
USER admin
PASS password123  # <- 平文で見える!
結論: 現在のセキュリティ基準では使用禁止レベル
🔒 FTPS:SSL/TLSによる暗号化FTP
リスク評価: ★★☆☆☆ (中程度)
セキュリティ機能
- 暗号化通信: SSL/TLSによる強力な暗号化
 - 証明書認証: X.509証明書による相互認証
 - データ完全性: ハッシュ値による改ざん検知
 
技術的実装
# Python ftplibでのFTPS接続例
import ftplib
from ftplib import FTP_TLS
# Explicit FTPS接続
ftps = FTP_TLS()
ftps.connect('ftp.example.com', 21)
ftps.auth()  # TLS認証開始
ftps.prot_p()  # データチャネル保護
ftps.login('username', 'password')
課題点
- ファイアウォール複雑性: データチャネルの動的ポート管理が困難
 - NAT環境での問題: 複数のコネクション管理
 - 設定複雑性: ImplicitとExplicitモードの選択
 
適用場面: レガシーFTPシステムのセキュリティ向上
🛡️ SFTP:SSH基盤の現代的ソリューション
リスク評価: ★☆☆☆☆ (低リスク)
セキュリティ優位性
- 単一チャネル: すべての通信がSSH上で実行
 - 強力な認証: 公開鍵、パスワード、多要素認証対応
 - 完全な暗号化: 制御・データ両チャネルの暗号化
 - ファイアウォールフレンドリー: ポート22のみ使用
 
実装例(Python)
import paramiko
# SFTP接続・操作例
def secure_file_transfer():
    # SSH接続初期化
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        # 公開鍵認証
        ssh.connect(
            hostname='sftp.example.com',
            username='user',
            key_filename='/path/to/private_key'
        )
        # SFTPセッション開始
        sftp = ssh.open_sftp()
        # セキュアファイル転送
        sftp.put('local_file.txt', '/remote/path/file.txt')
        sftp.get('/remote/data.csv', 'local_data.csv')
        sftp.close()
    except Exception as e:
        print(f"SFTP Error: {e}")
    finally:
        ssh.close()
高度なセキュリティ設定
# OpenSSH設定例 (/etc/ssh/sshd_config)
# SFTP専用ユーザーのchroot環境
Match User sftpuser
    ChrootDirectory /secure/sftp/%u
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no
    PasswordAuthentication no
    PubkeyAuthentication yes
適用場面: 現代的な本格運用環境の標準選択
⚡ SCP:シンプルで高速なコピー
リスク評価: ★☆☆☆☆ (低リスク)
セキュリティ特徴
- SSH基盤: SFTPと同等の暗号化レベル
 - シンプル性: 接続・転送・切断の単純フロー
 - 高速性: プロトコルオーバーヘッドが少ない
 
実用的な使用例
# 基本的なSCPコマンド
scp -P 22 -i ~/.ssh/id_rsa local_file.txt user@server:/remote/path/
# 再帰的ディレクトリコピー
scp -r -P 22 local_directory/ user@server:/remote/destination/
# 圧縮転送(帯域幅節約)
scp -C large_file.zip user@server:/backup/
Python実装例
import subprocess
import os
def secure_copy(local_path, remote_path, host, user, key_path):
    """
    SCPによるセキュアファイルコピー
    """
    cmd = [
        'scp',
        '-i', key_path,
        '-o', 'StrictHostKeyChecking=yes',
        '-o', 'UserKnownHostsFile=/home/user/.ssh/known_hosts',
        local_path,
        f'{user}@{host}:{remote_path}'
    ]
    try:
        result = subprocess.run(cmd, 
                              capture_output=True, 
                              text=True, 
                              check=True)
        return True
    except subprocess.CalledProcessError as e:
        print(f"SCP failed: {e.stderr}")
        return False
制限事項
- ディレクトリ操作不可: ファイルリスト表示などの対話的操作なし
 - レジューム非対応: 中断した転送の再開不可
 
適用場面: 単純なファイルコピー、自動化スクリプト
総合セキュリティ評価表
| プロトコル | 暗号化 | 認証強度 | ファイアウォール | 設定複雑度 | 推奨度 | 
|---|---|---|---|---|---|
| FTP | ❌ なし | ⭐ 弱い | 🔴 困難 | 🟢 簡単 | ❌ 使用禁止 | 
| FTPS | ✅ SSL/TLS | ⭐⭐⭐ 良い | 🔴 困難 | 🔴 複雑 | ⚠️ 条件付き | 
| SFTP | ✅ SSH | ⭐⭐⭐⭐⭐ 最強 | 🟢 簡単 | 🟡 普通 | ✅ 強く推奨 | 
| SCP | ✅ SSH | ⭐⭐⭐⭐⭐ 最強 | 🟢 簡単 | 🟢 簡単 | ✅ 用途限定で推奨 | 
実際の脅威シナリオと対策
シナリオ1:企業内部のファイル共有
要件: 部門間でのセンシティブファイル交換
# SFTP + 公開鍵認証の実装例
def enterprise_file_sharing():
    """
    企業レベルのセキュアファイル共有システム
    """
    import paramiko
    import logging
    from cryptography.fernet import Fernet
    # ファイル暗号化(追加セキュリティ層)
    def encrypt_file(file_path, key):
        fernet = Fernet(key)
        with open(file_path, 'rb') as file:
            encrypted_data = fernet.encrypt(file.read())
        encrypted_path = f"{file_path}.encrypted"
        with open(encrypted_path, 'wb') as encrypted_file:
            encrypted_file.write(encrypted_data)
        return encrypted_path
    # セキュアSFTP転送
    def secure_transfer(file_path, remote_path):
        ssh = paramiko.SSHClient()
        ssh.load_system_host_keys()
        ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
        try:
            ssh.connect(
                'secure-server.company.com',
                username='department_user',
                key_filename='/etc/ssh/department_key',
                timeout=30
            )
            sftp = ssh.open_sftp()
            sftp.put(file_path, remote_path)
            # 転送後にローカル暗号化ファイルを削除
            os.remove(file_path)
            logging.info(f"Secure transfer completed: {remote_path}")
        except Exception as e:
            logging.error(f"Transfer failed: {e}")
            raise
        finally:
            sftp.close()
            ssh.close()
シナリオ2:外部ベンダーとの安全なデータ交換
要件: 外部パートナーとの定期的なデータ交換
#!/bin/bash
# 外部ベンダー向けSFTPスクリプト
VENDOR_HOST="vendor-sftp.partner.com"
VENDOR_USER="company_upload"
KEY_FILE="/secure/keys/vendor_exchange_key"
LOCAL_DIR="/data/outgoing"
REMOTE_DIR="/incoming/company_data"
# ホスト鍵検証
ssh-keygen -F $VENDOR_HOST || {
    echo "Unknown host. Adding to known_hosts..."
    ssh-keyscan -H $VENDOR_HOST >> ~/.ssh/known_hosts
}
# バッチファイル転送
find $LOCAL_DIR -name "*.csv" -newer /tmp/last_transfer | while read file; do
    echo "Transferring: $file"
    sftp -i $KEY_FILE -o BatchMode=yes $VENDOR_USER@$VENDOR_HOST << EOF
put $file $REMOTE_DIR/
bye
EOF
done
# 転送完了マーカー更新
touch /tmp/last_transfer
AWS環境でのベストプラクティス実装
AWS Systems Manager Session Managerとの統合
import boto3
import json
def aws_secure_file_transfer():
    """
    AWS環境でのセキュアファイル転送
    Session Manager + SFTP
    """
    # Session Manager経由でのセキュアアクセス
    ssm = boto3.client('ssm')
    # セキュアトンネル確立
    response = ssm.start_session(
        Target='i-1234567890abcdef0',  # EC2インスタンスID
        DocumentName='AWS-StartPortForwardingSession',
        Parameters={
            'portNumber': ['22'],
            'localPortNumber': ['2222']
        }
    )
    # ローカルポート経由でSFTP接続
    ssh_config = {
        'hostname': 'localhost',
        'port': 2222,
        'username': 'ec2-user',
        'key_filename': '/path/to/aws-key.pem'
    }
    return ssh_config
S3との組み合わせによるハイブリッド転送
def hybrid_s3_sftp_transfer():
    """
    大容量ファイル: S3 + 署名付きURL
    小容量ファイル: SFTP
    """
    import boto3
    from botocore.exceptions import ClientError
    s3_client = boto3.client('s3')
    threshold_size = 100 * 1024 * 1024  # 100MB
    def transfer_file(file_path, file_size):
        if file_size > threshold_size:
            # 大容量ファイルはS3経由
            try:
                s3_client.upload_file(
                    file_path, 
                    'secure-transfer-bucket',
                    f'transfers/{os.path.basename(file_path)}'
                )
                # 署名付きURLを生成(1時間有効)
                presigned_url = s3_client.generate_presigned_url(
                    'get_object',
                    Params={'Bucket': 'secure-transfer-bucket', 
                           'Key': f'transfers/{os.path.basename(file_path)}'},
                    ExpiresIn=3600
                )
                return {"method": "S3", "url": presigned_url}
            except ClientError as e:
                print(f"S3 upload failed: {e}")
                return None
        else:
            # 小容量ファイルはSFTP
            return {"method": "SFTP", "status": "ready"}
セキュリティ設定のチェックリスト
🔐 SFTP/SCP共通セキュリティ設定
# /etc/ssh/sshd_config での推奨設定
# 基本セキュリティ
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# 接続制限
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
MaxSessions 10
# 暗号化強化
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group16-sha512
# プロトコル制限
Protocol 2
X11Forwarding no
AllowTcpForwarding no
🔑 公開鍵認証のベストプラクティス
# 強力な鍵ペア生成
ssh-keygen -t ed25519 -b 4096 -f ~/.ssh/secure_transfer_key -C "secure-transfer@company.com"
# 鍵ファイルのアクセス権限設定
chmod 600 ~/.ssh/secure_transfer_key
chmod 644 ~/.ssh/secure_transfer_key.pub
# authorized_keysでの制限設定
echo 'from="192.168.1.0/24",command="/usr/bin/rsync --server --daemon .",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAAC3NzaC1lZDI1NTE5...' >> ~/.ssh/authorized_keys
パフォーマンス比較とチューニング
転送速度テスト結果
import time
import os
def benchmark_protocols():
    """
    各プロトコルの転送速度測定
    """
    test_file_size = 1024 * 1024 * 100  # 100MB
    results = {}
    # テストファイル生成
    with open('test_file.bin', 'wb') as f:
        f.write(os.urandom(test_file_size))
    protocols = [
        ('SFTP', sftp_transfer),
        ('SCP', scp_transfer),
        ('FTPS', ftps_transfer)
    ]
    for name, transfer_func in protocols:
        start_time = time.time()
        transfer_func('test_file.bin')
        end_time = time.time()
        transfer_time = end_time - start_time
        speed_mbps = (test_file_size / (1024 * 1024)) / transfer_time
        results[name] = {
            'time': transfer_time,
            'speed_mbps': speed_mbps
        }
    return results
# 実測結果例(1Gbps環境)
# SFTP: 85-95 Mbps
# SCP: 95-105 Mbps
# FTPS: 75-85 Mbps
パフォーマンスチューニング
# SSH設定でのパフォーマンス向上
# ~/.ssh/config
Host production-server
    HostName prod.company.com
    User deploy
    IdentityFile ~/.ssh/prod_key
    # 圧縮有効化(低帯域環境)
    Compression yes
    # 接続の多重化
    ControlMaster auto
    ControlPath ~/.ssh/control_%h_%p_%r
    ControlPersist 1h
    # 暗号化アルゴリズム最適化
    Ciphers aes128-ctr,aes192-ctr,aes256-ctr
結論と推奨事項
🎯 プロトコル選択指針
2024年現在の推奨順位
- 
SFTP – 汎用的なファイル転送の標準選択
- 大部分のユースケースに最適
 - 豊富な機能と優れたセキュリティのバランス
 - 企業環境での標準プロトコル
 
 - 
SCP – シンプルなファイルコピー専用
- 自動化スクリプトでの単純転送
 - 高速性が要求される場面
 - 対話的操作が不要な場合
 
 - 
FTPS – レガシーシステム移行期のみ
- 既存FTPインフラの段階的セキュリティ向上
 - 長期的にはSFTPへの移行を計画
 
 - 
FTP – 使用禁止
- セキュリティリスクが高すぎる
 - 内部テスト環境でも避けるべき
 
 
🔐 セキュリティ実装の要点
- 公開鍵認証の必須化: パスワード認証の完全禁止
 - ネットワーク制限: 送信元IPアドレスの制限
 - ログ監視: 転送活動の詳細ログ記録
 - 定期的な鍵ローテーション: 3-6ヶ月での鍵更新
 - 多層防御: VPN + SFTP の組み合わせ
 
🚀 今後の技術動向
- Zero Trust Architecture: 信頼性前提の排除
 - Quantum-Safe Cryptography: 量子コンピューター耐性
 - API Based Transfer: REST/GraphQL経由のファイル操作
 - Container Native Solutions: Kubernetes環境での新しいアプローチ
 
安全なファイル転送は現代のシステム設計において妥協できない要件です。適切なプロトコル選択と正しい実装により、セキュリティと利便性を両立したソリューションを構築しましょう。

