FTP 수동모드(Passive mode) Unleashed: FTP서버 제작과 교훈

[목차(도우미)]
FTP와 능동모드(Active, ACTV) 수동모드(Passive, PASV)에 관하여

클라이언트 프로그램을 개발하는 입장에서 시험하기에 가장 까다로운 기능중의 하나는 파일전송(FTP, File Transfer Protocol)이 아닌가 싶다. 보통 개발 프로젝트의 사양은 서버 프로젝트로써 정해지지만 파일전송에 관하여는 서버에서도 대개는 공개된 서비스를 하기 때문에 클라이언트가 대향시험을 하기에는 만만하지 않다.

아무튼 도입 설명은 이정도로 해두고 일전에 설명하기로 한 파일전송(FTP)에 관하여 좀더 구체적으로 살펴보자.(쓰다보니 길어져서 조금 지루할 것이다. FTP 서버를 개발하고자 하는 사람이 적으므로 어떻게 구현하는지 궁금한 사람이 없는것 같다. 검색 결과를 보고 여기까지 방문하셨다면 아마도 네이버가 아닌 Google을 사용하셨겠죠?)


수동모드(PASV 모드)의 특징에 대하여

모드
특징
시퀀스
능동(Active, ACTV)
먼저 접속한 유저(C) 쪽으로 서버(S)가 접속을 시도한다.
C --> Server
C<-- 접속방향
수동(Passive, PASV)
유저(C)가 서버(S)쪽으로 접속을 시도한다.
C --> Server
접속방향--> Server

FTP는 RFC959(Requests for Comment)의 설명에서도 나오듯이 접속의 형태가 복잡한 축에 속한다. 단순한 텔넷(Telnet)과 달리 이중 접속(Connection)을 필요로 하는데 먼저 연결한 접속(Connection)을 흔히 제어선(Control connection)이라고 한다. 파일이나 dir/ls 등의 결과는 두번째 접속인 데이터선(Data connection)으로 보내준다. 그리고 데이터선은 전송이 끝나면 접속이 끊어지고 제어선만을 남겨 둔다.
URL Link : RFC959
(일부 발췌)
File Transfer Protocol

User-PI - Server A User-PI - Server B
------------------ ------------------

C->A : Connect C->B : Connect
C->A : PASV
A->C : 227 Entering Passive Mode. A1,A2,A3,A4,a1,a2
C->B : PORT A1,A2,A3,A4,a1,a2
B->C : 200 Okay
C->A : STOR C->B : RETR
B->A : Connect to HOST-A, PORT-a
Figure 3

    RFC를 읽어 보면 수동모드(PASV, passive Mode)라고 부르는 접속 방식은 원래부터 방화벽이나 보안을 위해서 만들어졌다기 보다는 다른 용도에도 사용될 수 있도록 고안된 것이다. 반드시 유저(C)에 의해서 접속을 하는 것만이 아니라 제3자에게도 파일을 전송할 수 있는 것이다.

227 Entering Passive Mode. A1,A2,A3,A4,a1,a2 부분은 가장 해석의 중요한 부분이다. RFC의 응답은 3자리 숫자로 하게 되어 있고 문자열은 단지 커멘트에 불과하다. 그러므로 "227"만으로 충분한 응답을 할 수 있어야 하는데 IP주소와 Port번호를 넘겨 주기 위하여 A1,A2,A3,A4,a1,a2를 필요로 한다.
IP Address=A1.A2.A3.A4의 문자열로 충분하고
Port Number = a1 shl 8 + a2(델파이 계산)
또는 a1 << 8 +a2(C 계산)
또는 a1*256+a2(보통 계산) 로 계산하면 된다. A1...a2는 모두 2바이트 수를10진수로 되돌린 값이다.


접속자
대상(A)
제3자(B)
제어선 접속
OK

제3자에게 전송 요구
OK


파일 전송-->
OK

아름다운 꽃 한송이를 능동모드로 아내에게 전해주고 싶다

수동모드(PASV 모드)의 장점 위의 RFC959의 "Figure 3"을 보면 알수 있듯이 유저가 서버A에 접속하여 파일을 올려 놓고(STOR, store) 서버B에 가서는 서버A에 가져다 놓은 파일을 떨어뜨릴(RETR, retrieve) 수 있다는 것이다. 그러나 이런 방식을 서버가 구현해 놓고 있어도 클라이언트 프로그램, 예를 들면 ftp.exe 등의 프로그램은 제3자 서버에 파일을 전송하는 명령어를 구현해놓고 있지 않아서 파일 전송의 길이 막혀있는 것이다.

  여기서 우리는 수동모드(PASV mode)는 방화벽, 보안 대책에 유용한 우회대책으로 사용될 수 있다는 점을 주목해볼 필요가 있다. 왜 주목해 보고자 하는가하면, FTP의 방식을 이해하면 TCP/IP의 접속을 사용한 독자적 접속방식을 구현할 수 있는 기술이 습득되기 때문이다.

  능동모드(ACTV, active mode)에서는 유저(C)가 자신의 포트를 열어 놓고 접속요구(PORT ip_address, port)를 보내야 한다. 이때 Windows XP등의 클라이언트 단말기에서는 외부에 대하여 포트를 열어 놓게 되기 때문에 유저에게 포트를 열어도 되는지에 대한 메시지박스를 표시해준다. 그리고 허락을 클릭하면 방화벽의 예외에 등록되고 파일전송을 개시한다. 파일 전송이 끝나면 접속이 닫히므로 큰 문제가 되지 않으리라고 보이지만 요새는 하도 악질의 프로그램과 바이러스가 많아서 단말측의 포트를 여는 것은 매우 위험 조작에 속한다고 할 수 있다.

그러나 수동모드에서는 유저(C)가 다시 서버에 접속을 요구하므로(PASV) 단말기에서는 불필요한 포트를 열어두지 않아도 된다. 심지어는 특정 포트외에는 모두 막아두어도 될 것이다.

브라우저와 에이전트의 동작에 관하여

인터넷 웹브라우저에서도 FTP에 대하여 지원하고 있는데 FTP 용 서버를 개발하여 직접 테스트해본 결과 몇가지 재미있는 점을 발견하였다. 일부 블로그등을 통하여 자료를 참조하다보면 브라우저에서는 '수동모드만을 지원하는 것처럼' 추측이 게시되어 있는데 사실은 그렇지 않다.

다음은 자작 FTP 시뮬레이션 서버(MySon)을 써서 알게된 정보를 기억하는 대로 적어본 것이다.
  • 웹브라우저에서는 모두 ACTV, PASV모드가 동작된다. 단 먼저 PASV를 시도하여 실패하면 ACTV로 동작한다.
    테스트한 브라우저: Internet Explorer(IE) 6, IE7, Opera 7.5, Opera 9, Opera 10, Safari 3, Safari 4.
    구글이나 파이어폭스는 개인적으로 사용하지 않아서 테스트에 포함시키지 않았다. 대개의 블로그에서는 대부분의 웹브라우저가 수동모드(PASV Mode)만을 지원하는 것처럼 소개하고 있지만 실제로 테스트한 웹브라우저는 모두 PASV --> ACTV의 순으로 동작한다. 이는 내가 MySon을 개발하면서 먼저 간단한 능동모드(ACTV Mode)를 먼저 구현하고 나중에 복잡한 수동모드(PASV Mode)를 구현했기 때문에 발견한 사실이다.

  • 웹브라우저마다 접속한 후에 이루어지는 초기 시퀀스는 동일하지 않고 각각 다르다.
    어찌 보면 같아야 할 이유도 없다. IE 계열에서는 접속 직후에 "CD /" 명령을 보내서 루트 디렉토리로 이동하도록 하는 반면, Opera 계열에서는 굳이 "CD /"를 보내지 않는다.

  • 웹브라우저 마다 익명(anonymous)의 로그인을 수행하는 과정에서 보내지는 패스워드는 각각 다르다. 
    브라우저의 이름이 패스워드로 사용되고 있는데 오페라 웹브라우저 같으면 "PASS Opera@"가 보내지고 IE6, IE7은 이상하게도 "PASS IE3user"라는 식으로 IE3가 남아있다.

  • IE 계열에서는 RFC 문헌에도 안나오는 명령어를 접속후 시퀀스에서 보낸다.
    이에 대하여 실패(502 Command not implemented. 등) 응답을 보내면 다른 브라우저등과 같이 보통의 표준 명령어를 보낸다.
    추측하건대 아마 마이크로소프트사가 비표준으로 유명한 하나의 작은 증거일지도 모른다.

  • 윈도의 부속 FTP.EXE는 수동모드가 장착되어 있지 않다.
    이점에 있어서는 INET.OCX와 FTP.EXE 로 할수 있는 동작 사양은 같다고 말할 수 있다.
    윈도의 버전은 Windows NT4, XP, Vista에서 테스트하였다.

  • IE 계열은 Temporary Internet Files 라는 폴더에 파일을 캐시해놓는다.
    그래서 두번째 이상의 파일을 다운로드할 때에는 서버에 연결하지 않는다.

    이점은 클라이언트 프로그램을 개발할 때 매우 중요한 점이다. 파 일 다운로드가 끝나면 캐시를 지우는 처리를 반드시 추가해야 오동작을 미연에 방지할 수 있다.

  • 브라우저에서 http로 접속하지 않고 ftp://host_address/ 등으로 접속해보면 웹페이지가 고스란히 보인다.
    웹페이지에는 이미지 파일등이 정의되어 있어도 이런 파일은 하나의 데이터선을 통해 다운로드되는 것이 아니라 여러개의 데이터선을 사용한다는 것을 알수 있다.

    브라우저 기능을 어떻게 구현하기에 나름이겠지만 같은 서버의 파일이면 동일한 데이터선을 통해 다운로드하고 다른 주소의 서버라면 다른 제어선과 데이터선으로 동시에 다운로드하면 브라우저의 속도가 높아질 것이다.

  • 대개의 블로그 또는 홈페이지에서 전송포트를 랜덤하게 열어 놓는다고 하여 어떤 홈페이지에서는 FTP를 구현함에 있어서 난수를 발생시켜서 바인드에러(Bind Error)가 생기지 않을 때까지 재시도하는 소스를 본적이 있다.
    그러나 미사용 포트(공포트)를 구하는 것은 네트워크 프로그램용 OS의 API가 있어서 이를 사용하면 간편히 된다. 구체적으는 포트를 지정하지 않고 0인 채로 바인드하도록 Open을 시도하면 OS가 미사용 포트를 찾아서 열어준다. 열린 포트번호는 참조가능하다.(그래야 상대편에 번호를 보낼 수 있으니까)

  • 윈도에서는 바로 미사용 포트의 계산에서 NT, XP의 차이가 생겨난다.
    번호의 대역이 차이가 난다.(NT는 1000번대에서 시작. XP는 2만번대에서 시작하는 것으로 기억한다.) 참고로 포트번호는 랜덤하게 변화되는 것이 아니라 대개는 2씩 증가하는 변화를 보인다. 그리고 최대값에 이르면 최소값으로 돌아와 순환하는 거동을 한다. 물론 여기서의 포트는 능동모드(ACTV mode)가 열어두는 포트를 가리킨다.

  • 윈도XP에서 IE 또는 윈도탐색기(Windows Explorer)로도 FTP에 접속할 수 있는데 수동모드가 실패하면 능동모드로 접속하게 된다. 능동모드로 바뀌게 되면 OS가 방화벽 예외처리를 물어온다. 물론 예외 등록을 해두면 나중에 제어판에서 부터 시작하여 찾아가면 흔적이 보인다.

  • FTP 다운로드의 경우 전송이 끝났는지 확인하거나 진행율을 보기 위하여 파일크기를 비교하곤 하는데 대부분의 브라우저가 SIZE명령어를 발행하고 있다. 물론 SIZE 명령어에 대해 에러를 되돌려도 다운로드에는 지장이 없다. 그냥 파일크기를 모르고 마냥 진행되는 것이 답답하게 보일 뿐이다.

  • 오페라 브라우저에는 (HTTP이긴 하지만) 파일 내려받기(Download)중에 중단하고 나중에 이어받기 기능이 있다. 이것은 오페라가 획기적인 기능을 개발한 것이라기 보다는 RFC 사양에 충실하다고 말할 수 있다. 중단한 경우 단말기 측에는 파일 스트림의 마지막 부분에 서버측 파일스트림의 위치를 기록해 놓고 재개 명령어를 보내면 서버가 재개하는 사양이 이미 RFC에 공개되어 있다. 다만 IE계열이나 오페라가 아닌 브라우저에는 재개(RESTART, REST)가 무시되어 있는 것으로 보인다.

이글을 읽고 궁금한점이 있으면 질문이나 방명록에 남겨 두세요.

후기 2011/07/07

FTP.ex로는 파일 전송이 되지만 자작 프로그램으로는 안되는 경우에 대해서

자세한 상황을 알수 없지만, 지금까지의 질문으로 보아 윈도용 FTP클라이언트 프로그램 부분을 만드셨다고 여겨집니다. 일부 기존 프로그램에서는 되는데 제작 프로그램에서는 파일 전송이 실패하는 경우가 생기는 것으로 고민하고 계신 것 같습니다.

FTP.exe로서는 파일 전송이 되었다는 점으로 보아 능동 모드는 문제가 없는 것으로 보입니다. 그렇다면 문제가되는 서버는 능동 모드가 원인이 아니고 수동 모드가 원인이라고 보이는데 자작 프로그램도 수동 모드로 접속했을 것으로 보입니다.

개발 언어에 따라 구체적인 이야기가 달라지는데 C/C++로 window용 API를 사용하였다면 wininet.dll에 들어 있는 ftp 용 API를 사용한 것이라고 생각되는데 여기에 있는 함수는 수동 모드와 능동 모드를 선택할 수 있습니다. 한편, 윈도 탐색기나 인터넷 탐색기등은 먼저 수동 모드로 파일 전송을 시도하고 수동 모드로의 전환이 실패하는 경우 (PASV 명령이 수동모드로가는 명령인데, API는 이런 명령을 사용하지 않고 파라미터로 설정하게 됩니다.) 두번째로 능동 모드로 파일 전송을 시도하게 됩니다. 보안 상의 이유등으로 수동 모드를 실행하고 실패할 경우에 능동 모드로도 시도하게 되는 것이죠.

능동 모드로 들어갈 때 서버가 윈도 PC로 접속을 열고 들어오게 되는데 윈도는 파이어월(방화벽)에서 윈도 탐색기 또는 인터넷 탐색기를 예외로 추가할 것인지를 물어보게 됩니다. 만약 예외로 추가하지 않고 차단하도록 하였다면 방화벽 설정의 예외를 열어 두어야 합니다. 이런 것은 방화벽 로그에도 남길 수 있게 되므로 파일에 남겨두는 방식을 써보면 도움이 됩니다.

Visual Basic으로 파일 전송을 하고자 했다면 msinet.ocx을 이용했을 가능성이 있는데, 이에 대해서는 별도의 관련글[소스코드리뷰(X) inet.ocx를 쓰는 것은 신중을 요한다]를 읽어 주세요.

델파이로 개발하는 경우 델파이는 유명한 Indy 계열을 사용하지 않았다면, 윈도의 wininet.dll을 그대로 사용하였을 가능성이 높습니다. C/C++의 경우와 다를 바가 없겠습니다.
by 금메달.아빠 on 2010. 6. 27. 12:53