요약

  • RFC 1509는gss_cred_id_t를 호출자에게 불투명한 로컬 핸들로 정의했다. 실제 자격 증명은 GSS-API 내부에 남아 있었고, 동일한 핸들 값이 호출자에 따라 서로 다른 자격 증명을 가리킬 수 있었다.
  • 핸들 범위는 하나의 프로세스, 프로세스와 그 자식, 또는 UID 같은 로컬 신원을 공유하는 프로세스들일 수 있었다. 핸들을 생략하는 것은 로컬에서 선택된 기본 자격 증명을 요청하는 일이었으며, 어떤 주체도 표명되지 않는다는 뜻은 아니었다.
  • 핸들, 자격 증명, 인증된 주체, 보안 컨텍스트, 보호된 메시지, 승인된 애플리케이션 행동은 각각 별개의 확인 기록이었다. 이식성은 작은 값 하나에 보편적 의미를 부여해서가 아니라 이러한 경계를 보존함으로써 나왔다.

두 개의 동일한 값이 서로 다른 문을 열었다

평범한 C 프로그램에서 동등성은 결정적인 것처럼 느껴진다. 두 정수가 같다고 비교된다. 두 포인터가 같은 표현을 가진다. 테이블 조회가 같은 슬롯을 반환한다. 그 값들이 같은 대상을 지칭한다고 결론 내리고 싶어진다.

RFC 1509는 자격 증명 핸들에 대해서는 그런 추론을 허용하지 않았다.gss_cred_id_t가 산술형이나 포인터형으로 구현되는 것은 허용했지만, 이를 호출자에게 불투명한 원자적 데이터로 규정했다. 더 이례적으로, 서로 다른 호출자가 그 값을 GSS-API에 제시하면 동일한 값이 서로 다른 자격 증명을 가리킬 수 있다고 밝혔다.

그 차이는 핸들 안에 인코딩되어 있지 않았다. 그것은 호출을 둘러싼 환경에 존재했다. 구현은 핸들의 범위를 이를 획득한 프로세스, 그 프로세스와 자식들, 또는 UID 같은 로컬 식별을 공유하는 프로세스로 한정할 수 있었다. 따라서 한 프로세스에서 관찰한 값을 감사 기록에 복사해 나중에 보편적인 자격 증명 식별자로 해석할 수는 없었다. 그 기록에는 해석자가 빠져 있었던 것이다.

이는 엉성함이 아니었다. 그것이 추상화였다. 핸들은 호출자가 보안 상태의 표현을 노출하지 않고 그 상태를 참조할 수 있게 했다. 그 유용성은 의도적으로 로컬한 결합에 의존했다:

caller + implementation state + handle value + mechanism → credential

호출자나 구현 에포크를 제거하면 그 값은 안정적인 의미를 잃는다. 숫자는 살아남아도 지시 대상은 바뀔 수 있다. 두 호출자 사이에서 값이 같게 유지되면서 지시 대상은 달라질 수도 있다.

자격 증명은 인터페이스 뒤에 머물렀다

RFC 1509는 자격 증명과 그 핸들을 유난히 명확하게 구분했다. 자격 증명은 주체를 설명하고 그 보유자에게 그 주체로서 행동할 능력을 부여했다. 그러나 애플리케이션은 실제 자격 증명을 받지 않았다. GSS-API 또는 그 기반 메커니즘 내부에 보관된 자격 증명에 대한 로컬 참조를 받았다.

그 분리는 보안과 이식성에 동시에 기여했다. 애플리케이션은 비밀 키, 티켓, 메커니즘별 구조를 파싱할 필요가 없었다. GSS-API 구현은 각 호출자에게 내부 형식을 가르치지 않고도 여러 메커니즘을 지원할 수 있었다. 자격 증명 핸들은 그 뒤의 자료가 근본적으로 달라져도 동일한 C 타입으로 남을 수 있었다.

따라서 이 문서는 핸들 자체에 보안 관련 정보가 없으며 애플리케이션이 특별히 보호할 필요도 없다고 말했다. 그 문장은 지나치게 확대 해석하기 쉽다. 자격 증명이 무해하다고 말한 것이 아니다. 어떤 프로세스든 핸들을 호출할 수 있다고 말한 것도 아니다. 비트를 복사하면 권한이 이전된다고 말한 것도 아니다. 보호 경계는 핸들 아래에 있었다. 구현과 운영체제가 어떤 호출자가 자격 증명을 해석하고 사용할 수 있는지 결정해야 했다.

언어 독립적인 동반 문서인 RFC 1508은 그 결과를 더 직접적으로 밝혔다. 시스템별 메커니즘과 OS 기능이 자격 증명 획득과 사용을 적절한 프로세스로 제한할 책임이 있었다. 주체의 자격 증명을 사용할 수 있는 능력을 그 주체의 신원을 표명할 수 있는 능력으로 다루었다. 다른 프로세스로의 이전 가능성은 보편적 GSS-API 규칙이 아니라 로컬 사안이었다.

겉보기 모순은 객체를 분리하면 사라진다. 핸들 비트가 비밀일 필요는 없다. 해석자에 대한 접근은 여전히 통제되어야 한다. 도서관 색인은 공개되어 있어도 대출 데스크가 누가 봉인된 책을 꺼낼 수 있는지 확인할 수 있다.

범위는 신원의 일부였다

핸들의 범위는 구현 각주가 아니었다. 핸들의 모든 사용을 이해하는 데 필요한 근거의 일부였다.

프로세스 A가 자격 증명을 획득하고 핸들 값7을 받았다고 하자. 한 구현에서는 자식 프로세스가 접근 권한을 상속할 수 있다. 다른 UID 아래의 인접 서비스는7을 제시하고 다른 테이블에 도달할 수 있다. 재시작 후에는 새 자격 증명이 이전 슬롯을 차지할 수 있다. 다른 호스트에서는7이 의미가 없을 수 있다. 어느 경우도 추상화를 위반하지 않는다.

유일하게 건전한 주장은 로컬적이고 시점이 한정된다: 이 호출자가, 이 구현 상태를 사용해, 이 핸들을, 이 작업을 위해, 이 자격 증명으로 해석했다는 것이다. 그조차 애플리케이션 결과를 입증하지는 않는다. 더 긴 사슬의 한 지점을 식별할 뿐이다.

RFC 1509는 또한 자격 증명에 도달할 수 있는 핸들이 하나도 남지 않으면 그 자격 증명이 사라지는 것을 허용했다. 따라서 수명은 값 자체에 속하지 않았다. 핸들, 자격 증명 상태, 메커니즘 자체의 만료 규칙에 의존했다. 작은 값만 보존한 로그는 그 값을 해석하는 데 필요한 모든 객체보다 오래 살아남을 수 있었다.

이것은 시스템 소프트웨어에서 반복되는 설계 선택이다. 간접 참조를 통해 구현은 내부를 교체하고 상태를 철회하며 호출자를 격리할 수 있다. 그 대가는 참조가 의미를 부여하는 네임스페이스와 에포크 없이는 내구성 있는 경계 간 식별자 역할을 할 수 없다는 점이다. API는 그 값이 어디서나 같은 것을 뜻한다고 약속하지 않음으로써 정확히 자유를 얻었다.

자격 증명 없음은 ‘하나를 고르라’는 뜻이었다

인터페이스에서 가장 위험한 이름은GSS_C_NO_CREDENTIAL이었을 수 있다. 컨텍스트 설정 호출에서 이를 제공한다고 해서 반드시 자격 증명이 사용되지 않는다는 뜻은 아니었다. 기본 자격 증명 동작을 요청한 것이다.

RFC 1509는 기본 자격 증명이 수립되는 메커니즘과 그 기본값의 범위를 구현에 맡겼다. RFC 2078은 나중에 해석 절차를 더 명시적으로 만들었다. 플랫폼은 애플리케이션이 사용하도록 승인된 유일한 주체, 기본 네트워크 신원, 기본 로컬 신원의 네트워크 이미지, 사용자가 구성한 신원 중 하나를 선택할 수 있었다. 사용 가능한 기본값을 해석하지 못하는 경우에는 별도의 오류 경로가 있었다.

이것은 이식 가능한 외투를 입은 환경 부여 권한이다. 애플리케이션은 여러 메커니즘과 플랫폼에서 실행되기 위해 명시적 참조를 생략한다. 로컬 환경이 주체를 공급한다. 호출자가 덜 알수록 이식성은 좋아진다. 같은 이유로 감사 가능성은 나빠질 수 있다.

따라서 ‘자격 증명 없음’이라고 적힌 추적 기록은 실제로 일어난 일과 정반대를 말할 수 있다. 의미 있는 기록은 기본 해석이 요청되었는지, 로컬 시스템이 어느 주체를 선택했는지, 어느 메커니즘 아래에서, 개시자 또는 수락자 용도로, 어떤 수명으로, 누구의 승인 정책 아래에서였는지를 밝혀야 한다.

명세는 기본값을 부당하게 만들지 않았다. 기본값을 로컬하게 만들었다. 그 구분이 중요하다. 공통 API는 묻는 방법을 정의할 수 있지만, 모든 머신이 같은 주체를 선택하거나 같은 위험을 수용하게 만들 수는 없다.

컨텍스트 핸들은 연결이 아니었다

RFC 1509는gss_ctx_id_t에도 비슷한 규칙을 적용했다. 컨텍스트 핸들 역시 호출자에게 불투명한 원자적 값이었고, 동일한 값이 호출자에 따라 서로 다른 컨텍스트를 식별할 수 있었다. 그 뒤의 컨텍스트는 피어 관계의 한쪽 끝에 대한 암호화 상태를 포함한 공유 상태를 담고 있었다.

컨텍스트는 전송 연결과도 독립적이었다. 통신 세션은 여러 연결을 가로지를 수 있었고, 여러 보안 컨텍스트가 하나의 통신 연관 안에서 연속적으로 또는 동시에 존재할 수 있었다. 소켓 신원과 보안 컨텍스트 신원은 호환되지 않았다.

이 설계는 흔한 혼동을 막았다. 열린 TCP 연결이 특정 GSS 컨텍스트의 존재를 입증하지 않았다. 컨텍스트 핸들이 어느 소켓이 그 토큰을 운반했는지 입증하지 않았다. 컨텍스트 완료가 이후의 모든 애플리케이션 메시지가 무결성이나 기밀성을 사용했음을 입증하지 않았다. 한쪽 끝에서 컨텍스트를 삭제한 것이 원격 애플리케이션이 자체 작업을 완료했음을 그 자체로 입증하지도 않았다.

각 단계에서 호출자에게는 할 일이 있었다. 토큰을 운반하고, 올바른 컨텍스트와 연결하고, 주요 상태와 메커니즘별 상태를 검사하고, 서비스를 요청하고, 반환된 플래그를 확인하고, 인증된 피어가 무엇을 할 수 있는지 결정해야 했다.

토큰은 경계를 건넜고, 핸들은 건너지 않았다

RFC 1509의 인증 토큰은 표면적으로 또 다른 불투명한 값처럼 보였지만, 그 역할은 달랐다. 토큰은 한쪽 끝의 메커니즘이 다른 쪽 끝의 피어 메커니즘이 처리하도록 생성한 비트 문자열이었다. 애플리케이션은 이를 전송할 책임이 있었다. 토큰은 암호학적으로 보호될 수 있었고, 로컬 자격 증명 핸들은 전송되는 자격 증명이 아니었다.

이 분리는 유용한 시험을 제공했다. 객체가 경계를 건너도록 의도되었다면, 프로토콜은 수신자가 그것을 어떻게 해석하고 검증할 수 있는지 정의해야 한다. 그것이 로컬 핸들이라면, 그 표현을 복사하는 것은 전송 절차가 아니다.

버전 2 C 바인딩은 보안 컨텍스트에 대해 그 교훈을 명시적으로 만들었다. RFC 2744는gss_export_sec_context와gss_import_sec_context를 추가했다. 내보내기는 소스 프로세스에서 컨텍스트를 비활성화하고 다른 프로세스를 위한 프로세스 간 토큰을 생성했다. 한 번에 하나의 컨텍스트 인스턴스만 활성화될 수 있었다. 대상이 가져올 수 있는 능력은 공유 계정이나 프로세스 그룹 같은 로컬 정책으로 제한될 수 있었다.

이전의 로컬 핸들 값과 달리, 프로세스 간 토큰은 키나 다른 민감한 데이터를 포함할 수 있었다. 애플리케이션은 전송 중 이를 보호하고 신뢰할 수 있는 프로세스에만 주어야 했다. 실제로 이동한 객체는 그것이 대체한 참조와 다른 보호 의무를 지녔다.

후대 설계가 모든 구현이 컨텍스트를 내보냈거나 전송이 항상 성공했음을 입증한 것은 아니다. 그것은 계약을 명확히 했다. 이동에는 명시적 상태 전이와 목적에 맞게 만들어진 산출물이 필요했다. 핸들을 복사하는 것은 이동이 아니었다.

표시되는 이름도 또 하나의 투영이었다

이름은 같은 경계를 사람이 읽을 수 있는 형태로 드러냈다. RFC 1509는 사람을 위한 인쇄 가능한 이름과 API에 제시되는 내부 이름을 구분했다. 인쇄 가능한 구문은 로컬 구성이나 개인 선호에 의존할 수 있었다. 객체 식별자가 네임스페이스를 식별하기 위해 인쇄 가능한 값과 함께 제공되었다. 내부 이름은 구현이 한 네임스페이스를 다른 네임스페이스와 혼동하지 않도록 충분한 타입 정보를 유지했다.

애플리케이션은 이름을 가져오고, 표시하고, 비교하기 위해 GSS-API 연산을 사용하라는 지침을 받았다. 문자열 동등성은 신원 규칙이 아니었다. RFC 2744는 나중에 가져온 내부 이름을 표시할 때 제공된 것과 같은 문자열을 반환할 필요가 없으며, 네임스페이스 식별자조차 내부 여정에서 변하지 않고 살아남지 못할 수 있다고 분명히 밝혔다. DNS 이름은 X.500 형식으로 매핑된 뒤 그렇게 표시될 수 있었다.

이것이 표시 텍스트를 쓸모없게 만들지는 않았다. 그 역할을 정밀하게 만들었다. 표시되는 이름은 구현이 선택한 표현이었다. 인증된 내부 이름은 메커니즘에 묶인 상태였다. 로컬 계정 매핑은 또 다른 결정이었다. 애플리케이션 승인 규칙은 또 다른 결정이었다.

화면은 맞아 보이는 사람을 보여 주면서도 승인 검사는 잘못된 주체를 사용할 수 있다. 같은 내부 엔터티에 대해 다르게 보이는 문자열을 보여 줄 수도 있다. 근거 사슬에는 운영자가 본 문자만이 아니라 네임스페이스, 메커니즘, 비교 결과가 필요하다.

채널 바인딩이 사실 하나를 더 결합했다

채널 바인딩은 GSS-API가 통신 연관으로부터 독립적이라는 원칙에 대한 의도적 예외였다. RFC 1509는 애플리케이션이 개시자와 수락자의 주소 타입 및 주소, 그리고 애플리케이션 데이터를 제공할 수 있게 했다. 개념적으로 메커니즘은 이 값들을 연결하고 서명한 뒤 그 결과를 컨텍스트 설정 토큰에 바인딩했다. 수락자가 다른 바인딩을 제공하면 컨텍스트 설정이GSS_S_BAD_BINDINGS로 실패할 수 있었다.

그것은 소켓 레이블을 신뢰하는 것보다 강력했다. 그래도 그 의미는 제한적이었다. 애플리케이션이 바인딩 입력을 선택했다. 주소 패밀리는 여러 형태를 포함할 수 있었다. 메커니즘은 검사를 추가할 수 있었지만 채널의 모든 물리적·행정적 속성을 입증할 필요는 없었다. 일부 메커니즘은 실제 바인딩 데이터를 토큰에 넣을 수 있었기 때문에 RFC 1509는 기밀 자료를 바인딩 구성 요소로 사용하지 말라고 경고했다.

성공적인 바인딩 비교는 주장된 통신 컨텍스트를 보안 컨텍스트 교환에 결합했다. 그것은 법적 신원, 애플리케이션 자격, 메시지 전달, 관찰된 효과를 입증하지 않았다. 하나의 간극을 닫았을 뿐 이후 결정들은 가시적으로 남겼다.

공통 인터페이스는 로컬 머신에서 멈췄다

RFC 1511은 Common Authentication Technology 워킹그룹의 전략을 분업으로 설명했다. 보안 전문가는 재사용 가능한 메커니즘과 서비스를 구현할 수 있었고, 프로토콜 설계자는 각 암호 시스템의 전문가가 되지 않고도 일반 인터페이스를 통합할 수 있었다. RFC 1508은 개념적 API를 정의했다. RFC 1509는 C 프로그램에 구체적인 타입과 호출을 제공했다.

그 아키텍처는 중요한 의미에서 얇았다. 참조, 연산, 결과 어휘를 표준화하는 한편 자격 증명 보관, 프로세스 승인, 기본 신원, 로컬 이름 변환, 그리고 많은 배치 선택을 구현과 운영체제에 남겼다.

RFC 2078은 구현 경험 후 추상 API를 개정했다. RFC 2743과 2744는 나중에 초기 문서를 버전 2 업데이트 1과 그 C 바인딩으로 대체했다. 후속 작업은 연산과 정밀성을 더했지만 더 깊은 경계는 유지했다. GSS-API를 사용하는 것만으로는 특정 보증이 제공되지 않았다. 보안은 기반 메커니즘과 호출자가 서비스를 올바르게 요청하고 반환된 결과를 읽는지에 달려 있었다.

이것은 ‘표준이 싱글 사인온을 만들었다’는 이야기보다 나은 역사다. 표준은 로컬 보안 기계에 컨텍스트를 수립하고 사용하도록 요청하는 공통 방법을 만들었다. 모든 자격 증명을 글로벌 네임스페이스로 국유화하지 않았다. 호출자 신원을 지우지 않았다. 핸들을 복사만 하면 이식 가능한 능력으로 바꾸지 않았다.

출처가 입증하는 것

이 기록은 명세 언어와 계보를 입증한다. RFC 1509가 호출자 상대적 핸들 의미를 의도적으로 허용했고, 핸들을 자격 증명 및 토큰과 분리했으며, 본질적 통제를 로컬 구현에 두었음을 입증한다. 후대 GSS-API 문서가 기본 해석, 이름 동작, 명시적 컨텍스트 전송을 더 상세히 만들었음을 입증한다.

그러나 특정 라이브러리가 핸들을 어떻게 표현했는지, 배포된 운영체제가 어떤 범위를 선택했는지, 애플리케이션이 올바른 사실을 기록했는지, 어떤 취약점·익스플로이트·인시던트가 발생했는지는 입증하지 않는다. 광범위한 채택이나 보편적 적합성을 입증하지도 않는다. 표준 트랙 레이블은 출판 상태이지 배포 확인서가 아니다.

역사적 가치는 부정적 공간에 있다. 1993년에 이미 보안 API는 작은 참조의 동등성이 권한의 동등성을 수립하기에 충분하지 않다는 것을 알고 있었다. 공통 계층이 작동한 이유는 결정적 매핑이 로컬하고 이견 가능한 상태로 남았기 때문이다. 내구성 있는 주장에는 호출자, 해석자, 메커니즘, 주체, 컨텍스트, 효과가 필요했으며, 메모리에서 복사한 외로운 숫자 하나로는 충분하지 않았다.

출처