[Withdrawn] PEP 543 - A Unified TLS API for Python
원문 링크: PEP 543 - A Unified TLS API for Python
상태: Withdrawn 유형: Standards Track 작성일: 17-Oct-2016
PEP 543 – Python을 위한 통합 TLS API
- 작성자: Cory Benfield, Christian Heimes
- 상태: 철회됨 (Withdrawn)
- 유형: Standards Track
- 생성일: 2016년 10월 17일
- Python 버전: 3.7
- 대체: 748
개요 (Abstract)
이 PEP는 추상 베이스 클래스(Abstract Base Class, ABC)들의 집합 형태로 표준 TLS(Transport Layer Security) 인터페이스를 정의하고자 했습니다. 이 인터페이스는 Python 구현체와 서드파티 라이브러리가 OpenSSL 외의 다른 TLS 라이브러리에 대한 바인딩을 제공할 수 있도록 하여, Python 표준 라이브러리가 제공하는 인터페이스를 기대하는 도구들이 이를 사용할 수 있도록 하는 것이 목표였습니다. 궁극적으로 Python 생태계의 OpenSSL 의존도를 줄이는 것을 목표로 했습니다.
결정 (Resolution)
2020년 6월 25일, PEP의 두 저자와의 합의를 통해 이 PEP는 기본 운영 체제의 API 변경으로 인해 철회되었습니다.
배경 (Rationale)
21세기에 접어들면서, 강력하고 사용자 친화적인 TLS 지원은 모든 인기 프로그래밍 언어 생태계에서 매우 중요한 부분이 되었습니다. Python 생태계에서는 대부분 ssl
모듈이 이 역할을 수행해왔으며, 이 모듈은 OpenSSL 라이브러리에 대한 Python API를 제공합니다.
ssl
모듈은 Python 표준 라이브러리에 포함되어 있기 때문에, Python에서 TLS를 처리하는 압도적으로 가장 인기 있는 방법이 되었습니다. 표준 라이브러리와 Python Package Index(PyPI)에 있는 대다수의 Python 라이브러리는 TLS 연결을 위해 ssl
모듈에 의존합니다.
불행하게도, ssl
모듈의 이러한 지배력은 Python 생태계 전체를 OpenSSL에 단단히 묶는 여러 예상치 못한 부작용을 낳았습니다. 이로 인해 Python 사용자는 대체 TLS 구현이 더 나은 사용자 경험을 제공할 수 있는 상황에서도 OpenSSL을 사용해야 했으며, 이는 인지적 부담을 주고 “플랫폼-네이티브(platform-native)” 경험을 제공하기 어렵게 만들었습니다.
문제점 (Problems)
ssl
모듈이 표준 라이브러리에 내장되어 있다는 사실은 모든 표준 라이브러리 Python 네트워킹 라이브러리가 Python 구현체가 링크된 OpenSSL에 전적으로 의존한다는 것을 의미합니다. 이는 다음과 같은 문제들을 야기합니다.
- 새로운 OpenSSL 버전에 대한 접근성 부족: 새로운, 더 높은 보안 TLS 기능을 활용하기 위해서는 Python을 재컴파일하여 새로운 OpenSSL 버전을 얻어야 합니다.
- Windows 배포의 부담: Windows용 Python 배포판은 OpenSSL 사본을 함께 제공해야 합니다. 이로 인해 CPython 개발팀은 OpenSSL 재배포자 역할을 하게 되며, OpenSSL 취약점이 공개될 때마다 Windows Python 배포판에 보안 업데이트를 제공해야 할 수 있습니다.
- macOS 시스템 라이브러리 의존성: macOS용 Python 배포판은 OpenSSL 사본을 함께 제공하거나 시스템 OpenSSL 라이브러리에 링크해야 합니다. Apple은 시스템 OpenSSL 라이브러리 링크를 공식적으로 사용 중단(deprecated)했으며, 이 라이브러리 버전은 작성 시점 기준으로 거의 1년간 업스트림에서 지원되지 않았습니다.
- 시스템 인증서 저장소 접근 제한: Windows 및 macOS를 포함한 많은 시스템은 OpenSSL에 시스템 인증서 저장소를 제공하지 않습니다. 이는 사용자가 신뢰할 수 있는 루트 인증서를 다른 곳에서(예:
certifi
) 얻거나, 시스템 신뢰 저장소를 어떤 형태로든 내보내야 함을 의미합니다.certifi
에 의존하는 것은 이상적이지 않습니다. 대부분의 시스템 관리자는 PyPI에서 보안상 중요한 소프트웨어 업데이트를 받을 것으로 기대하지 않으며,certifi
번들에 사용자 정의 루트를 추가하거나 중앙에서 신뢰를 관리하는 것이 쉽지 않습니다.
- OpenSSL의 기능 부족 및 크기 문제: 사용자는 OpenSSL에 없는 기능(예: TLS 1.3 지원)이나 OpenSSL이 플랫폼에 비해 너무 크고 다루기 어려운 경우(예: 임베디드 Python) 등 여러 이유로 다른 TLS 라이브러리와 통합하기를 원할 수 있습니다.
- 대체 TLS 백엔드 지원의 어려움: 현재 구현된
ssl
모듈은 CPython 자체적으로 대체 TLS 백엔드를 추가하거나 OpenSSL 지원을 완전히 제거하는 능력을 제한합니다.ssl
모듈은 OpenSSL에 특화된 너무 많은 함수 호출과 기능을 노출하여 대체 TLS 백엔드에 쉽게 매핑하기 어렵습니다.
제안 (Proposal)
이 PEP는 OpenSSL에 강하게 묶이지 않은 TLS 기능을 제공하기 위해 Python 3.7에 몇 가지 새로운 추상 베이스 클래스(ABC)를 도입할 것을 제안했습니다. 또한 표준 라이브러리 모듈을 가능한 한 이러한 ABC가 노출하는 인터페이스만 사용하도록 업데이트할 것을 제안했습니다. 세 가지 주요 목표는 다음과 같습니다.
- 공통 API 표면 제공: 코어 개발자와 서드파티 개발자 모두가 TLS 구현을 대상으로 할 수 있는 공통 API 표면을 제공합니다.
- OpenSSL 특정 개념 누출 방지: OpenSSL 특정 개념이 API를 통해 누출되는 것을 최소화하는 API를 제공합니다.
- OpenSSL을 여러 TLS 백엔드 중 하나로 전환: 코어 개발팀이 OpenSSL을 Python의 TLS 지원을 위해 반드시 필요한 구성 요소가 아닌, 여러 가능한 TLS 백엔드 중 하나로 만들 수 있는 경로를 제공합니다.
제안된 인터페이스는 아래에 설명되어 있습니다.
인터페이스 (Interfaces)
표준화가 필요한 몇 가지 인터페이스가 있습니다.
- TLS 구성 (
ssl
모듈의SSLContext
클래스에 의해 현재 구현됨). - 실제 I/O 없이 인메모리 암호화 또는 암호 해독을 위한 인메모리 버퍼 제공 (비동기 I/O 모델에 필요하며,
ssl
모듈의SSLObject
클래스에 의해 현재 구현됨). - 소켓 객체 래핑 (
ssl
모듈의SSLSocket
클래스에 의해 현재 구현됨). - TLS 구성 적용. (
ssl
모듈의SSLContext
클래스에 의해 현재 구현됨). - TLS Cipher Suites 지정.
- TLS 핸드셰이크 중에 협상될 수 있는 애플리케이션 계층 프로토콜 지정.
- TLS 버전 지정.
- 호출자에게 오류 보고 (
ssl
모듈의SSLError
클래스에 의해 현재 구현됨). - 클라이언트 또는 서버 인증서로 로드할 인증서 지정.
- 원격 피어가 제시한 인증서를 검증하는 데 사용될 신뢰 데이터베이스 지정.
- 런타임 시 이러한 인터페이스를 얻는 방법.
이 PEP는 버퍼(TLSWrappedBuffer
)와 소켓(TLSWrappedSocket
)에 대해 통합된 접근 방식을 제안했습니다.
TLSConfiguration
TLSConfiguration
구체(concrete) 클래스는 TLS 구성을 보유하고 관리할 수 있는 객체를 정의합니다. 이 클래스의 목표는 다음과 같습니다.
- 입력 오류 위험을 피하면서 TLS 구성을 지정하는 방법을 제공합니다.
- SNI(Server Name Indication) 콜백에 사용하기 위해 다른 구성 객체와 안전하게 비교하여 TLS 구성 변경 사항을 감지할 수 있는 객체를 제공합니다.
이 클래스는 구현체별 동작을 가질 것으로 예상되지 않으므로 ABC가 아닙니다. TLSConfiguration
객체를 주어진 TLS 구현체에 유용한 구성 세트로 변환하는 책임은 아래에서 논의할 Context
객체에 속합니다.
TLSConfiguration
은 불변(immutable) 객체로 설계되어, 딕셔너리 키로 사용될 수 있으며 구현체가 구성 객체의 변경에 대해 걱정할 필요가 없도록 합니다. 향후 Python 릴리스에서 유용한 구성 필드를 추가할 수 있도록 확장 가능합니다.
Context
두 개의 Context
추상 베이스 클래스가 정의됩니다: ClientContext
와 ServerContext
. 이 ABC들은 TLS 구성을 특정 연결에 적용할 수 있도록 하는 객체를 정의하며, TLSWrappedSocket
및 TLSWrappedBuffer
객체를 위한 팩토리(factory)로 간주될 수 있습니다.
현재 ssl
모듈과 달리 두 개의 컨텍스트 클래스를 제공함으로써 API를 단순화하고, 구현체가 TLS 연결의 어느 쪽을 담당할지 최대한 빨리 알 수 있도록 합니다.
주요 변경사항 중 하나는 wrap_socket
에서 auto_handshake
플래그가 제거되었다는 것입니다. 이는 사용자 및 구현자 모두에게 복잡성을 증가시키는 디자인 문제로 간주되었으며, 모든 사용자가 연결 후 명시적으로 do_handshake
를 호출해야 합니다.
버퍼 (Buffer)
버퍼 래퍼 ABC는 TLSWrappedBuffer
ABC에 의해 정의됩니다. 이 ABC는 다음과 같은 메서드를 포함합니다.
read(amt: int) -> bytes
: 입력 버퍼에서 데이터를 읽습니다.readinto(buffer: Any, amt: int) -> int
: 입력 버퍼에서buffer
로 데이터를 읽습니다.write(buf: Any) -> int
: 암호화된buf
를 출력 버퍼에 씁니다.do_handshake() -> None
: TLS 핸드셰이크를 수행합니다.cipher() -> Optional[Union[CipherSuite, int]]
: 협상된 CipherSuite를 반환합니다.negotiated_protocol() -> Optional[Union[NextProtocol, bytes]]
: TLS 핸드셰이크 중에 선택된 프로토콜을 반환합니다.context(self) -> Context
: 이 버퍼가 연결된Context
객체를 반환합니다.negotiated_tls_version(self) -> Optional[TLSVersion]
: 이 연결에서 협상된 TLS 버전을 반환합니다.shutdown() -> None
: 깔끔한 TLS 종료를 수행합니다.receive_from_network(data)
: 네트워크에서 TLS 데이터를 수신하여 내부 버퍼에 저장합니다.peek_outgoing(amt)
: 출력 데이터 버퍼에서 네트워크로 기록되어야 할 다음amt
바이트를 반환합니다.consume_outgoing(amt)
: 출력 데이터 버퍼에서 다음amt
바이트를 버립니다.
소켓 (Socket)
소켓 래퍼 클래스 TLSWrappedSocket
는 일반 소켓 객체와 TLSWrappedBuffer
객체를 생성자에서 받아들이는 구체(concrete) 클래스가 됩니다. 이 클래스는 SOCK_STREAM
타입 소켓에만 작동하는 메서드들을 제외하고는 모든 소켓 API를 구현할 것입니다.
TLSWrappedSocket
은 일반 소켓 메서드 외에 다음 메서드를 포함합니다.
do_handshake() -> None
: TLS 핸드셰이크를 수행합니다.cipher() -> Optional[Union[CipherSuite, int]]
: 협상된 CipherSuite를 반환합니다.negotiated_protocol() -> Optional[Union[NextProtocol, bytes]]
: 협상된 프로토콜을 반환합니다.context(self) -> Context
: 이 소켓이 연결된Context
객체를 반환합니다.negotiated_tls_version(self) -> Optional[TLSVersion]
: 이 연결에서 협상된 TLS 버전을 반환합니다.unwrap() -> socket.socket
: TLS 연결을 깔끔하게 종료하고 래핑되었던 소켓을 반환합니다.
Cipher Suites
CipherSuite
는 TLS 핸드셰이크에 사용되는 두 옥텟 암호 식별자를 16비트 정수로 저장하는 열거형(enum)으로 제안되었습니다. OpenSSL, SecureTransport, SChannel, NSS 등 다양한 TLS 구현체들이 암호 스위트(cipher suite)를 지정하는 방식이 매우 다르기 때문에, 각 개별 암호 스위트를 제공하는 가장 낮은 공통 분모(lowest-common denominator) 접근 방식을 채택했습니다.
CipherSuite
열거형은 현재 110개 정도의 스위트를 포함하며, 레거시 애플리케이션을 위해 3DES, RC4, SEED, IDEA가 포함되고 TLS 1.3 드래프트의 5개 암호 스위트도 포함됩니다. 향후 OpenSSL의 집계 기능을 재도입할 수 있는 헬퍼(helper)를 제공하는 확장이 가능합니다.
프로토콜 협상 (Protocol Negotiation)
NPN(Next Protocol Negotiation)과 ALPN(Application-Layer Protocol Negotiation)은 HTTP/2 핸드셰이크의 일부로 프로토콜 협상을 허용합니다. 이 모듈은 프로토콜 협상 구현체가 전달할 수 있는 유형을 정의하며, 잘 알려진 프로토콜에 대한 별칭(alias)을 허용하도록 바이트 스트링을 래핑합니다.
제안된 NextProtocol
Enum은 다음과 같습니다.
H2 = b'h2'
H2C = b'h2c'
HTTP1 = b'http/1.1'
WEBRTC = b'webrtc'
C_WEBRTC = b'c-webrtc'
FTP = b'ftp'
STUN = b'stun.nat-discovery'
TURN = b'stun.turn'
TLS 버전 (TLS Versions)
지원할 TLS 버전을 제한하는 것은 보안상 유용합니다. 제안된 TLSVersion
열거형은 TLS 버전을 제어하는 데 사용될 수 있습니다.
MINIMUM_SUPPORTED
SSLv2
SSLv3
TLSv1
TLSv1_1
TLSv1_2
TLSv1_3
MAXIMUM_SUPPORTED
오류 (Errors)
이 모듈은 오류 처리에 사용되는 네 가지 기본 클래스를 정의했습니다. 이 클래스들은 추상적이지 않으며, 특정 공통 동작을 알리는 역할을 합니다. 백엔드 구현체는 이 예외들을 서브클래싱하여 사용해야 합니다.
TLSError
: 모든 TLS 관련 오류의 기본 예외입니다.WantWriteError
: 논블로킹(non-blocking) 또는 버퍼 전용 I/O에서 사용되며, 쓰기 작업이 완료될 수 없음을 알립니다.WantReadError
: 논블로킹 또는 버퍼 전용 I/O에서 사용되며, 읽기 작업이 완료될 수 없음을 알립니다.RaggedEOF
: TLS 연결이 비정상적으로 종료되었을 때 사용됩니다 (예:CloseNotify
없이 TCP 소켓이 EOF에 도달).
인증서 (Certificates)
이 모듈은 추상 X509
인증서 클래스를 정의했습니다. 이 클래스는 from_buffer
및 from_file
이라는 두 가지 추상 클래스 메서드를 통해 인증서를 생성할 수 있도록 하여, 구체적인 구현체에 인증서의 소스를 알리는 기능을 제공했습니다.
개인 키 (Private Keys)
인증서 클래스와 유사하게, 이 모듈은 추상 PrivateKey
클래스를 정의했습니다. 이 클래스는 from_buffer
및 from_file
클래스 메서드를 통해 개인 키를 로드할 수 있도록 했으며, 필요한 경우 암호를 사용하여 암호화된 키를 해독하는 기능을 포함합니다.
신뢰 저장소 (Trust Store)
TrustStore
는 시스템 신뢰 데이터베이스를 나타내는 객체를 반환하는 system
클래스 메서드와 PEM 파일에서 신뢰 저장소를 초기화하는 from_pem_file
클래스 메서드를 포함하는 추상 클래스입니다. 다양한 TLS 구현체가 신뢰 저장소를 선택하는 방식이 다르기 때문에, 신뢰 저장소의 형태에 대해 최소한의 가정을 하는 모델을 제공하고자 했습니다.
런타임 접근 (Runtime Access)
라이브러리 사용자가 TLS 구성을 제어하면서도 사용 중인 백엔드를 선택할 수 있도록, 이 PEP는 Backend
객체를 통해 런타임 시 구체적인 구현체에 접근할 수 있는 API를 제안했습니다. Backend
객체는 client_context
, server_context
, certificate
, private_key
, trust_store
속성을 가집니다.
표준 라이브러리 변경 사항 (Changes to the Standard Library)
TLS와 상호작용하는 표준 라이브러리 부분들은 이러한 ABC를 사용하도록 수정되어야 했습니다. 여기에는 asyncio
, ftplib
, http
, imaplib
, nntplib
, poplib
, smtplib
, urllib
모듈이 포함됩니다.
ssl
모듈의 마이그레이션 (Migration of the ssl module)
ssl
모듈 자체도 이러한 ABC에 맞게 확장되어야 했습니다. 기존 ssl
모듈을 활용하는 애플리케이션은 계속 작동하도록 하면서, 새로운 API를 사용하려는 애플리케이션 및 라이브러리를 위해 새로운 클래스 형태로 확장될 예정이었습니다.
ssl
모듈에서 새로운 ABC로 마이그레이션하는 것은 일대일 매핑이 아닐 것으로 예상되었습니다. 그러나 ssl
모듈에서 예외를 누출하는 라이브러리나 애플리케이션의 경우 문제가 발생할 수 있었으므로, ssl
모듈의 예외는 위에 정의된 예외들을 별칭(alias)으로 사용하도록 변경해야 했습니다.
예:
assert ssl.SSLError is tls.TLSError
assert ssl.SSLWantReadError is tls.WantReadError
assert ssl.SSLWantWriteError is tls.WantWriteError
향후 (Future)
주요 미래 TLS 기능은 이러한 ABC의 개정이 필요할 수 있었지만, 신중하게 이루어져야 했습니다. ABC는 IETF가 지정한 기능의 상위 수준 설명으로 제한되어야 합니다. 그러나 이 API에 대한 정당한 확장도 이루어져야 하며, 이 API의 초점은 Python 커뮤니티를 위한 통합된 “최소 공통 분모(lowest-common-denominator)” 구성 옵션을 제공하는 것입니다.
⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.
Comments