FTP 프로토콜 실전 활용 | Active·Passive·FTPS·SFTP와 파일 전송 운영

이 글의 핵심

로그인과 디렉터리 목록은 되는데 파일 전송만 멈춘다면 대개 데이터 채널 포트가 막힌 경우입니다. 레거시 연동 때문에 FTP를 아직 써야 하는 상황을 전제로 PASV 포트 범위 지정, 계정 권한과 chroot, 평문 자격 증명 노출 위험을 짚고, 흔한 오류별 확인 순서를 정리해 운영 중 막혔을 때 바로 참고할 수 있게 했습니다.

들어가며

FTP(File Transfer Protocol)는 1970년대부터 쓰인 파일 전송 프로토콜로, 웹 이전 시대의 배치·레거시 시스템·대용량 교환에서 아직 흔히 등장합니다. 제어 채널과 데이터 채널이 분리되고, Active/Passive 모드에 따라 방화벽 규칙이 달라져 “로컬에선 되는데 운영만 안 된다”는 유형이 많습니다. 동시에 평문 인증이라는 근본적 약점 때문에, FTPS(FTP over TLS)나 SFTP(SSH 파일 전송)로 대체·병행하는 것이 2026년 기준 실무 표준에 가깝습니다. 이 글은 동작을 이해하고, 클라이언트·서버 설정과 보안 선택을 한 번에 정리합니다.

왜 아직 FTP를 다루나요?

신규 서비스의 기본 선택은 HTTPS·S3 API·SFTP 쪽으로 가는 편이지만, 은행·제조·장비 등에서는 여전히 FTP 연동 명세가 남아 있습니다. 문제는 프로토콜 자체가 아니라 평문·방화벽·NAT와의 조합이므로, 레거시를 유지하더라도 Active/Passive·FTPS·SFTP 이관 로드를 문서로 고정해 두어야 합니다.

프로덕션에서 주의할 점

  • 평문 FTP는 자격 증명·파일 내용이 노출될 수 있으므로, 가능한 한 즉시 FTPS 또는 SFTP로 전환합니다.
  • Passive 모드에서는 데이터 포트 범위가 방화벽·NAT와 한 세트로 관리되어야 합니다. 21만 열고 끝이면 실패가 납니다.
  • 서버 공인 IP와 NAT 뒤 실제 IP가 다르면 pasv_address(또는 동등 설정)를 반드시 맞춥니다.

프로토콜 개요

역사 및 개발 배경

FTP는 RFC 114(1971) 시절부터 이어져 RFC 959(1985)가 사실상의 전통적 기준이었으며, 이후 확장 명령·IPv6·보안 강화(FTPS)가 붙었습니다. HTTP·S3·클라우드 스토리지가 보급되면서 새 시스템의 기본 선택에서는 밀렸지만, 메인프레임·산업 장비·구형 MFT 등에서는 여전히 FTP 인터페이스가 남아 있습니다.

OSI 7계층에서의 위치

FTP는 응용 계층 프로토콜이며, 전송 계층에서는 일반적으로 TCP를 사용합니다(제어 21, 데이터는 모드에 따라 다름). SFTP는 이름이 비슷하지만 SSH 위의 별도 프로토콜입니다.

핵심 특징

특징설명
이중 채널명령·응답은 제어 연결, 실제 파일은 데이터 연결.
세션 상태로그인·CWD 등 대화형 세션.
전송 모드스트림·블록·압축(구현·용도별).
텍스트 명령USER, PASS, RETR, STOR 등 명령어 문자열.

동작 원리

제어 채널과 데이터 채널

  • 제어 채널(기본 21/tcp): 인증, 디렉터리 이동, 전송 준비 명령.
  • 데이터 채널: 디렉터리 목록(LIST)·파일 내용 전송의 실제 바이트가 흐릅니다.

Active 모드

  1. 클라이언트가 서버 21로 제어 연결.
  2. 데이터 전송 시 클라이언트가 로컬 데이터 포트를 열고 서버에 알림(PORT).
  3. 서버가 클라이언트 쪽으로 데이터 연결을 적극적으로 열음(서버가 “액티브”하게 연결). 전통적으로 서버의 출발 포트는 20/tcp입니다.

클라이언트 방화벽 뒤에서 서버의 인바운드가 막히면 실패하기 쉽습니다. 요즘 클라이언트는 대부분 공유기(NAT) 뒤에 있어서, 클라이언트가 PORT로 알려 주는 주소가 192.168.0.10 같은 사설 IP가 되고, 서버는 인터넷 너머에서 그 주소로 연결할 방법이 없습니다. Active 모드가 사실상 사내망이나 서버 간 전송에서만 쓰이는 이유입니다.

Passive 모드

  1. 제어 연결은 동일.
  2. 클라이언트가 PASV(또는 EPSV)로 서버의 데이터 주소·포트를 받음.
  3. 클라이언트가 서버의 데이터 포트로 연결(서버는 패시브로 대기). 서버 측 데이터 포트 범위가 방화벽에서 열려 있어야 합니다. Passive 흐름을 한 줄로 보면 제어 21번으로 명령을 주고, PASV로 알려준 데이터 포트로 클라이언트가 붙어 파일 바이트를 주고받습니다.

PASV 응답은 227 Entering Passive Mode (203,0,113,10,195,80) 같은 형태입니다. 앞의 네 숫자가 IP 주소(203.0.113.10)이고, 뒤의 두 숫자로 포트를 계산합니다. 포트는 195 × 256 + 80 = 50000입니다. 전송이 멈출 때 이 줄을 직접 읽어 보면 원인이 바로 보이는 경우가 많습니다. 여기에 10,0,0,5처럼 서버의 내부 IP가 찍혀 있다면 pasv_address 설정 문제이고, 포트가 방화벽에서 연 범위 밖이라면 pasv_min_port/pasv_max_port 설정 문제입니다. IPv6와 NAT 환경을 위해 만들어진 EPSV(RFC 2428)는 229 Entering Extended Passive Mode (|||50000|)처럼 IP 없이 포트만 알려 주므로, 클라이언트가 제어 연결과 같은 주소로 붙게 되어 이런 주소 문제 자체를 피할 수 있습니다.

sequenceDiagram
  participant C as Client
  participant S as Server
  C->>S: 제어 연결
  C->>S: PASV
  S->>C: 데이터 주소 안내
  C->>S: 데이터 연결
  S->>C: 파일 전송

자주 쓰는 명령(개념)

명령의미
USER / PASS인증(평문—위험).
PWD / CWD현재 경로·이동.
LIST / NLST목록.
RETR다운로드.
STOR / APPE업로드·이어 붙이기.
TYPEASCII/IMAGE(바이너리) 등.
PASV / EPSV패시브 모드.

서버 응답은 세 자리 숫자 코드로 시작하며, 운영 중에 자주 보는 코드는 몇 개 되지 않습니다. 530 Login incorrect는 인증 실패, 425 Can't open data connection은 데이터 채널 연결 실패(대개 방화벽·NAT), 550 Permission denied나 550 Failed to open file은 파일 권한이나 경로 문제, 553 Could not create file은 업로드 디렉터리 쓰기 권한 문제입니다. 425와 550만 구분해도 “네트워크 문제인가, 권한 문제인가”를 첫 단계에서 가를 수 있습니다.

TYPE은 사소해 보이지만 데이터 손상의 흔한 원인입니다. ASCII 모드(TYPE A)는 전송 중에 줄바꿈 문자를 서버와 클라이언트 OS 형식에 맞게 변환하므로, zip이나 이미지 같은 바이너리를 ASCII 모드로 받으면 0x0D 0x0A 바이트가 바뀌어 파일이 깨집니다. 현대 클라이언트는 대부분 기본으로 바이너리(TYPE I)를 쓰지만, 오래된 배치 스크립트는 ASCII가 기본인 경우가 있어 “가끔 압축 파일이 손상된다”는 증상으로 나타납니다.


실전 사용

명령줄 클라이언트(lftp 예—macOS/Homebrew 등)

lftp는 기본으로 Passive 모드를 쓰며, 설정 이름은 ftp:passive-mode입니다. 현재 값은 세션 안에서 set -a | grep passive로 확인할 수 있습니다.

# 대화형 세션
lftp -u username,password ftp.example.com
# 세션 안에서: ls, cd, get file.bin, put local.bin, mirror -R 등
# Passive 모드 명시 + 재시도 횟수 제한 후 목록 조회
lftp -e 'set ftp:passive-mode on; set net:max-retries 2; ls; bye' -u username,password ftp.example.com
# FTPS(명시적 TLS) 강제
lftp -e 'set ftp:ssl-force true; set ftp:ssl-protect-data true; ls; bye' -u username ftp.example.com

명령줄에 비밀번호를 쓰면 셸 히스토리와 ps 출력에 남습니다. 배치 작업이라면 ~/.netrc(권한 600)에 자격 증명을 두는 편이 낫습니다. ftp:ssl-protect-data를 켜지 않으면 FTPS에서도 제어 채널만 암호화되고 파일 내용은 평문으로 갈 수 있다는 점도 기억해 두세요. curl로도 제한적 다운로드가 가능합니다.

curl -u user:pass 'ftp://ftp.example.com/pub/readme.txt' -o readme.txt
# FTPS: 제어·데이터 채널 모두 TLS를 요구 (실패하면 평문으로 내려가지 않음)
curl --ssl-reqd -u user:pass 'ftp://ftp.example.com/pub/readme.txt' -o readme.txt

curl은 기본적으로 EPSV를 먼저 시도하고 안 되면 PASV로 넘어갑니다. 방화벽이나 오래된 서버가 EPSV를 이해하지 못해 멈춘다면 --disable-epsv를 주면 됩니다.

vsftpd 설정 예시(개념)

/etc/vsftpd.conf (배포판별 경로 상이)

listen=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
pasv_enable=YES
pasv_min_port=50000
pasv_max_port=50100
# NAT 뒤 서버일 때 공인 IP 지정
# pasv_address=203.0.113.10
# FTPS (인증서 경로는 환경에 맞게)
ssl_enable=YES
rsa_cert_file=/etc/ssl/certs/vsftpd.pem
rsa_private_key_file=/etc/ssl/private/vsftpd.key
chroot_local_user=YES

vsftpd 설정 파일은 줄 끝 주석을 지원하지 않습니다. ssl_enable=YES # FTPS처럼 쓰면 YES # FTPS 전체가 값으로 읽혀 500 OOPS: bad bool value in config file for: ssl_enable로 데몬이 시작하지 않습니다. 원래 예제가 바로 이 형태였는데, 다른 설정 파일 형식에 익숙하면 가장 쉽게 저지르는 실수입니다. 주석은 항상 별도 줄에 둡니다. 또 = 양옆에 공백을 넣어도 같은 방식으로 실패합니다.

  • listen=YES: 독립 데몬 모드로 자체 포트에서 수신합니다(배포판·설정에 따라 listen_ipv6 등과 함께 씀).
  • anonymous_enable=NO: 익명 로그인을 끕니다. 공개 미러가 아니라면 보통 NO입니다.
  • local_enable=YES / write_enable=YES: 로컬(시스템) 사용자로 로그인·업로드를 허용합니다. 쓰기 범위는 OS 권한·chroot와 함께 최소화합니다.
  • local_umask=022: 업로드 파일의 기본 권한 마스크입니다(너무 느슨하면 안 됩니다).
  • pasv_enable=YES: Passive 모드를 켭니다. 클라이언트가 방화벽 뒤인 경우가 많아 실무에서 자주 사용합니다.
  • pasv_min_port / pasv_max_port: Passive 데이터 채널에 쓸 포트 범위입니다. 방화벽에서 이 구간을 인바운드 허용해야 합니다.
  • pasv_address: 클라이언트가 PASV 응답으로 받을 공인 IP입니다. NAT 뒤에서 내부 IP가 노출되면 연결이 실패합니다.
  • ssl_enable=YES: FTPS(TLS)를 켭니다. 인증서·프로토콜 버전은 별도 TLS 설정과 함께 관리합니다. 방화벽에서 21/tcp와 pasv_min/max 포트를 허용해야 Passive가 안정적입니다.

Chroot·권한

  • 전용 시스템 사용자·chroot jail로 업로드 루트를 제한합니다.
  • 쓰기 가능 디렉터리만 최소 권한으로 엽니다.

chroot_local_user=YES를 켜면 사용자가 자기 홈 디렉터리 밖을 볼 수 없게 되는데, 이때 처음 거의 반드시 만나는 오류가 500 OOPS: vsftpd: refusing to run with writable root inside chroot()입니다. vsftpd는 보안상 chroot 루트 디렉터리 자체가 사용자에게 쓰기 가능하면 로그인을 거부합니다(루트에 /etc 같은 디렉터리를 만들어 라이브러리 로딩을 속이는 공격을 막기 위함). allow_writeable_chroot=YES로 이 검사를 끌 수도 있지만, 권장되는 해결은 홈 디렉터리를 root 소유·쓰기 불가로 두고 그 안에 upload/ 같은 쓰기 가능한 하위 디렉터리를 만드는 것입니다.


보안 고려사항

평문 FTP의 위험

기본 FTP는 자격 증명·파일 내용이 암호화되지 않습니다. 공용 Wi‑Fi·중간 경로에서 스니핑에 노출됩니다.

FTPS(FTP over TLS)

명시적 FTPS: 제어 채널에서 AUTH TLS 후 암호화. 암시적 시작(레거시 990)은 피하는 추세입니다. 인증서 관리·TLS 버전을 최신으로 유지합니다.

FTPS로 바꾸면 평문 FTP에서 잘 되던 Passive 전송이 갑자기 멈추는 경우가 있습니다. 방화벽이나 공유기의 FTP ALG(Application Level Gateway)는 제어 채널의 227 응답을 엿보고 데이터 포트를 자동으로 열어 주거나 주소를 고쳐 쓰는데, 제어 채널이 TLS로 암호화되면 그 내용을 읽을 수 없기 때문입니다. 평문 시절에는 ALG 덕분에 우연히 동작하던 구성이 FTPS 전환과 함께 드러나는 것이므로, FTPS에서는 Passive 포트 범위를 방화벽에 명시적으로 열고 pasv_address를 정확히 설정하는 것이 필수입니다. 또 일부 서버는 데이터 채널에서 TLS 세션 재사용을 요구해서(vsftpd의 require_ssl_reuse=YES 기본값), 이를 지원하지 않는 클라이언트는 522 SSL connection failed: session reuse required 오류를 받습니다.

SFTP(SSH)

SFTP는 FTP가 아닙니다. SSH 세션 위에서 동작하며, 키 기반 인증·암호화가 일관됩니다. 새로 구축할 때 SFTP가 선택지로 자주 올라옵니다.

구분FTPSSFTP
기반FTP + TLSSSH
포트21(+데이터) / 방화벽 복잡22(기본) 단순한 편
레거시 호환기존 FTP 툴SSH 클라이언트 필요

SFTP의 가장 큰 운영상 이점은 포트 하나(22)만 열면 된다는 점입니다. 제어와 데이터가 같은 SSH 연결 안에서 다중화되므로 Active/Passive, 포트 범위, pasv_address, ALG 같은 이 글의 문제 대부분이 사라집니다. 대신 상대방이 FTP 명세로만 연동할 수 있는 장비나 기관이라면 선택지가 FTPS뿐이고, SFTP 서버를 열 때는 파일 전송 전용 계정이 셸을 얻지 못하도록 sshd_config의 Match User + ForceCommand internal-sftp + ChrootDirectory로 제한해야 합니다.


실무 활용 사례

시나리오설명
레거시 연동은행·제조 배치 파일 교환, 오래된 MFT 규격.
대용량 파일재개(resume)·병렬이 구현된 클라이언트(lftp mirror 등)로 이전.
익명 배포공개 미러(보안·악용 방지 정책 필수).
장비 펌웨어일부 장비는 FTP만 제공—격리 네트워크·FTPS로 보완.

최적화 팁

  • 병렬 전송: lftp의 mirror -P N 등으로 다중 파일 동시 처리. 큰 파일 하나는 pget -n 4로 구간을 나눠 받을 수 있지만, 서버의 사용자당 동시 접속 제한(vsftpd max_per_ip)에 걸리면 421 There are too many connections from your internet address가 납니다.
  • 재개: 전송 중단 시 REST/RETR 또는 클라이언트의 resume 지원 확인.
  • 바이너리 모드: 이미지·zip은 TYPE I(IMAGE)로 손상 방지.
  • 근접 배치: 지리적으로 가까운 중계 서버로 RTT를 줄입니다.

흔한 문제와 해결

증상점검
Passive에서 타임아웃서버 PASV 범위 방화벽 개방, NAT 뒤 IP(pasv_address) 설정.
Active만 안 됨클라이언트 데이터 포트 인바운드 차단—Passive 전환.
목록은 되고 전송만 실패데이터 채널 방화벽, TLS 데이터 채널(FTPS) 설정 불일치.
한글 파일명 깨짐클라이언트 UTF-8 옵션, 서버 OPTS UTF8 ON 지원 여부.
425 Can't open data connection데이터 채널 연결 실패: PASV 포트 범위·방화벽·pasv_address 순서로 확인.
500 OOPS: refusing to run with writable rootchroot 루트가 쓰기 가능: 루트는 쓰기 불가, 하위 디렉터리만 쓰기 허용.
압축 파일만 가끔 손상ASCII 모드 전송: 바이너리(TYPE I)로 강제.

흔한 실수와 해결

실수결과해결
제어(21)만 열고 데이터 포트는 미개방목록·전송 단계에서 타임아웃Passive 포트 범위를 방화벽에 명시
pasv_address를 내부 IP로 둠클라이언트가 잘못된 주소로 데이터 연결공인 IP 또는 NAT 외부 주소로 수정
평문 FTP로 유지스니핑·자격 증명 유출 위험FTPS 또는 SFTP로 이관 계획
FTPS와 SFTP를 동일시포트·방화벽·클라이언트 설정이 엇갈림스택이 다르다는 점을 문서에 명시

마무리

FTP는 제어·데이터 분리와 Active/Passive라는 독특한 구조 때문에 방화벽·NAT와 동행해 왔으며, 평문이라는 레거시 부채도 함께 안고 있습니다. FTPS로 전송 계층을 보강하거나, 가능하면 SFTP·객체 스토리지 API로 이관하는 것이 현대적 운영 방향입니다. 추천 시나리오: 레거시 FTP 연동 유지보수, Passive 포트·NAT 문제 해결, 보안 강화 로드맵(FTPS/SFTP) 수립이 필요할 때 이 글의 점검표를 활용하세요.


자주 묻는 질문 (FAQ)

Q. 디렉터리 목록은 보이는데 파일 전송만 실패하면 어디를 봐야 하나요?

A. FTP는 제어 채널(21번)과 데이터 채널이 분리되어 있어, 로그인과 명령은 되는데 데이터 채널이 막히는 경우가 흔합니다. Passive 모드라면 서버의 PASV 포트 범위가 방화벽에 열려 있는지, pasv_address가 내부 IP가 아닌 공인 또는 NAT 외부 주소로 설정되어 있는지 확인합니다. FTPS를 쓴다면 데이터 채널 TLS 설정이 클라이언트와 서버 사이에 일치하는지도 점검해야 합니다.

참고

  • RFC 959, RFC 4217(FTPS)
  • man lftp, OpenSSH SFTP 문서

같이 보면 좋은 글