요약

  • RFC 2389는FEAT를 추가해 FTP 클라이언트가 모든 명령을 하나하나 시도하지 않고도 문서화된 확장을 발견할 수 있게 했고,OPTS를 추가해 이후 명령에 대해 지원되는 동작을 선택할 수 있게 했다.
  • 규격에 맞는 긍정적 기능 목록은 문서화된 확장에 대해서는 완전했지만, 인식되지 않은FEAT응답은 부재를 증명하지 못했다. 오래된 서버는 목록을 만드는 방법을 모르면서도 FEAT 이전의 확장을 지원할 수 있었다.
  • 기능 광고, 옵션 수용, 인증, 권한 부여, 명령 완료, 관찰된 전송 결과는 여전히 별개의 영수증이었다.

무해한 질문을 위험하게 묻는 방법

1990년대의 FTP 클라이언트는 기본 프로토콜을 알고 있어도 앞에 놓인 서버에 대해서는 알아야 할 것이 부족했다. RFC 959 이후 새로운 명령과 메커니즘이 등장했고, 구현체들은 그것들을 같은 시기에 채택하지 않았다. 따라서 클라이언트는 확장을 사용하기 전에 실용적인 질문에 직면했다: 이 서버가 그것을 이해하는가?

가장 자명한 시험은 명령을 내려 보는 것이었다. 그러나 그것은 잘못된 추상화이기도 했다. 명령은 무언가를 하려는 시도이지, 가능성에 대한 중립적 문의가 아니었다. RFC 2389는 확장을 하나씩 시도하면 교환을 낭비하고 클라이언트가 원하지 않는 효과를 일으킬 수 있다고 지적했다. 실행을 통한 발견은 지식과 행동을 혼동했다.

FEAT는 행동 전에 읽는 경계를 만들었다. 클라이언트는 인자 없이 한 단어를 보냈다. 명령을 인식하는 서버는 확장 기능의 구조화된 목록을 반환할 수 있었다. 클라이언트는 그중 하나를 호출하기 전에 자신이 이해하는 것과 그 목록을 비교할 수 있었다. 이것은 얇은 조정이었다. 즉, 기능 라벨을 위한 공통 봉투를 제공하면서 각 확장은 자신의 의미론을 그대로 유지했다.

이 구분은 작아 보인다. 와이어 교환이 작기 때문이다. 그러나 그것은 불확실성의 위치를 바꾸었다. 클라이언트는 더 이상 모든 질문을 시험적 작업으로 바꿀 필요가 없었다. 먼저 범위가 한정된 진술을 획득하고, 그것을 어떻게 처리할지는 로컬에서 판단할 수 있었다.

하나의 선행 공백이 프로토콜 구조를 담았다

RFC 2389는 성공한 목록을 기계 판독 가능하게 만들었다. 비어 있지 않은 응답은 여러 줄211-응답으로 시작했다. 각 기능은 자신의 줄을 차지했고 정확히 하나의 공백으로 시작했다. 마지막211 End가 목록을 닫았다. 선행 공백은 장식적 들여쓰기가 아니었다. 기능 줄이 종료 응답과 혼동되지 않도록 보장하는 것이었다.

첫 줄은 자유 형식 텍스트로 남았다. 기능 줄은 그렇지 않았다. 기능 라벨은 명령 이름을 가질 수 있지만 반드시 그래야 하는 것은 아니었다. 라벨 뒤의 매개변수는 그 확장을 정의하는 후속 명세에 속했다. 순서는 아무 의미가 없었고, 서버는 같은 순서를 두 번 반환할 의무가 없었다.

이 문법이 진화를 보호했다. 클라이언트는 모르는 기능 라벨을 허용하도록 요구되었다. 알 수 없는 라벨은 잘못된 형식의 응답이 아니었다. 그것은 서버가 클라이언트가 작성된 이후에 정의된 무언가를 구현했다는 증거였다. 클라이언트는 해석할 수 없는 것은 무시하면서 공통 하위 집합은 여전히 사용할 수 있었다. 프로토콜의 성장은 오래된 소프트웨어가 새 정보를 오류처럼 취급하는 것을 요구하지 않았다.

FEAT자체는 목록에 없었다.500이나502가 아닌 응답은 발견 명령에 대한 지원을 입증했다.OPTS도 생략되었는데, RFC 2389가 모든 FEAT 구현체에 그것을 요구했기 때문이었다. 이 목록은 확장을 기술했으며, 클라이언트가 교환에서 이미 추론할 수 있는 모든 사실을 기술한 것이 아니었다.

이 답은 한 방향으로만 완전했다

RFC 2389에서 가장 흥미로운 부분은 그 비대칭성이었다.

서버가FEAT를 구현했다면, RFC 959와 RFC 2389를 넘어서 지원하는 문서화된 모든 FTP 확장을 나열해야 했다. 규격에 맞는 목록을 받은 클라이언트는 그 목록에 나열된 집합을 서버의 완전한 확장 목록으로 취급할 수 있었다. 그 성공적인 응답에서 누락된 확장은 서버가 지원하지 않는다는 뜻이었다.

그러나FEAT를 인식하지 못하는 서버는 평범한500이나502알 수 없는 명령 응답을 반환했다. 그 오류가 서버에 확장이 전혀 없다는 뜻은 아니었다. 일부 확장은 목록 메커니즘이 존재하기 전에 배치되었다. 오래된 서버는 그중 하나를 이해하면서FEAT에 답할 방법이 없을 수 있었다. 클라이언트는 여전히 그 오래된 확장을 개별적으로 시험해야 할 수 있었다.

“기능 없음”조차 이 호환성 그림자를 안고 있었다.FEAT는 이해하면서 확장을 구현하지 않는 서버는 한 줄211응답을 반환해야 했다. 그럼에도 RFC 2389는500이나502를 허용했다. 클라이언트 입장에서 두 상황은 실질적으로 구별되지 않기 때문이었다.

결과는 일방적인 닫힘 규칙이었다. 유효한 긍정적 목록은 불확실성을 강하게 좁혔다. 그 목록을 얻지 못하면 역사는 미해결로 남았다. 그것은 침묵에 신뢰할 수 없는 정보를 담게 만드는 것보다 정직한 설계였다.

OPTS는 동작을 선택했을 뿐, 일을 실행하지 않았다

발견만으로 확장 내부의 변주를 해결하지는 못했다.OPTS는 클라이언트가 이후 대상 명령에 대해 원하는 동작을 요청할 수 있게 했다. 대상 명령의 정의 명세가 옵션 구문과 효과를 공급했다. RFC 2389는 공통 봉투와 응답 클래스만 공급했다.

200응답은 서버가 대상 명령을 인식하고 제공된 옵션이 적절하다고 판단했다는 뜻이었다.501응답은 영구적 문제를 기술했다. 상태를 바꾸지 않고 나중에 같은 요청을 반복해도 실패한다는 뜻이었다.451응답은 사라질 수 있는 서버의 일시적 상태를 기술했다.

타이밍이 중요했다.OPTS는 이후 명령이 무엇을 해야 하는지 설정했다. 이후 명령이 발행되었거나, 권한이 부여되었거나, 완료되었다고 말하지 않았다. 옵션 영수증은 유효하면서도, 이후 작업은 경로가 존재하지 않거나, 자격 증명에 권한이 없거나, 자원을 사용할 수 없거나, 데이터 연결이 끊겨 실패할 수 있었다.

RFC 3659는 이 분리를 구체적으로 만들었다. 그 명세의MLST기능 줄은 서버가 지원하는 파일 사실을 광고하고 기본 사실에 별표를 표시할 수 있었다.OPTS MLST는 이후MLST와MLSD응답이 어떤 사실을 반환할지 선택했다. RFC는 일부 비기본 사실은 서버가 생성하기 비용이 클 수 있다고 경고했다. 가용성, 기본 선택, 클라이언트의 선택, 실제 목록은 서로 다른 상태로 남았다.

TLS 라벨은 보호된 세션이 아니었다

RFC 4217은 나중에 같은 메커니즘을 FTP over TLS에 사용했다. 보안 확장을 지원하는 FEAT 가능 서버는AUTH TLS,PBSZ,PROT를 광고했다. 이 줄들은 클라이언트에게 어떤 협상 경로가 이용 가능한지 알려주었다. 그 자체로 그 경로를 만들지는 않았다.

클라이언트는 여전히AUTH TLS를 보내야 했고, 서버는234로 수용해야 했고, TLS 교환이 완료되어야 했고, 보호 매개변수가 설정되어야 했고, 인증서 신원과 정책이 평가되어야 했으며, 요구되는 FTP 사용자 인증은 여전히 성공해야 했다. 기능 라벨은 그 사슬의 첫 번째 영수증이었지, 사슬의 압축 대체물이 아니었다.

이 구분은 캡처된 목록에서 추론할 수 있는 것도 제한한다.AUTH TLS는 서버가 그 맥락에서 그 메커니즘에 대한 지원을 주장한다는 뜻이다. 특정 인증서가 유효하다거나, 사용자가 권한을 부여받았다거나, 데이터 보호가 선택될 것이라거나, 파일이 실제로 이동할 것이라는 증명이 아니다.

RFC 7151은 또 다른 예를 공급했다. 가상 호스트HOST명령을 지원하는 서버는HOST를 광고해야 했다. 그러나 가상 호스트 선택은 인증 환경을 바꿀 수 있었고, 인증서 이름 매칭은 여전히 별개였다. 기능 이름은 문을 드러냈다. 누가 그 문을 통과할 수 있는지는 결정하지 않았다.

레지스트리는 이름들을 분리했을 뿐, 승자와 패자를 나누지 않았다

FTP 확장이 늘어나면서, RFC 5797은 IANA FTP 명령 및 확장 레지스트리를 만들었다. 그 목적은 명령과 기능 이름의 충돌, 그리고 그 충돌이 만들 애매함을 줄이는 것이었다. 레지스트리는 명령, FEAT 코드, 설명, 명령 유형, 규격 적합 기대치, 참조를 기록했다.

이 문서는 더 넓은 권위를 명시적으로 부인했다. 등록이 확장이 “승인”되었다는 증명은 아니었다. 확장은 영구 공개 명세를 통해서, 또는 일반적으로 이용 가능한 클라이언트와 서버 시스템에 구현됨으로써 자격을 얻을 수 있었다. 역사적 항목은 재사용을 막기 위해 계속 보였다. 자리표시 코드는 유용한 이름을 예약했지만 FEAT 응답에 나타나도록 만들어진 것은 아니었다.

그것은 다른 층에서의 같은 규율이다. 레지스트리는 어떤 라벨이 어떤 문서화된 메커니즘을 가리키는지 말한다. 서버는 어떤 메커니즘을 지원하는지 말한다. 옵션 교환은 어떤 동작을 수용했는지 말한다. 인증과 정책은 이 사용자가 무엇을 할 수 있는지 말한다. 명령 응답은 제어층에서 무엇이 일어났는지 말한다. 데이터 연결과 그 결과의 파일 상태는 의도된 전송이 실제로 발생했는지 말한다.

어떤 단일 기록도 다른 모든 기록으로 승격되어서는 안 된다.

공개가 유용했던 것은 범위가 한정되어 있었기 때문이다

RFC 2389는 기능 목록이 서버의 능력을 드러내고 추가 추론을 허용할 수 있음을 인정했다.FEAT가 없으면 결연한 관찰자는 명령을 개별적으로 탐칠 수 있었지만, 그 패턴은 로그에서 더 눈에 띄었을 수 있다. 저자들은 그 차이가 메커니즘을 제거할 만큼 심각하다고 보지 않았다.

그것은 하나의 거래였다. 호환되는 클라이언트가 예측 가능하게 동작할 수 있도록 범위가 한정된 기능 표면을 공개하되, 각 확장은 자신의 보안에 책임을 진다. 대안은 비밀주의가 아니었다. 행동에 의한 더 시끄러운 발견이었다.

운영적 교훈은 모든 서비스가 모든 것을 공개해야 한다는 것이 아니다. 발견에는 명시적 범위가 필요하다는 것이다. 기능 목록은 안전한 선택에 충분한 것을 드러내고, 안정된 이름을 사용하고, 낯선 추가를 허용하고, 아는 것보다 더 많이 주장하지 않아야 한다. 로컬 시스템은 행동이 발생할 때 신원, 정책, 자원 사용, 효과를 여전히 스스로 판단해야 한다.

RFC 2389는 FTP 확장을 스스로 실행되게 만들지 않았다. “당신은 무엇을 말할 수 있는가?”라는 질문을 “나를 위해 이것을 해줄 것인가?”보다 먼저 답할 수 있게 만들었다. 인터넷은 정확히 이런 분리들을 통해 성장했다. 즉, 최소 공통 구문, 로컬 해석, 자발적 사용, 그리고 실행되는 교환이 공급하는 증명이었다.

출처