Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 476 – 표준 라이브러리 HTTP 클라이언트의 인증서 검증 기본 활성화

Author:
Alex Gaynor <alex.gaynor at gmail.com>
Status:
Final
Type:
Standards Track
Created:
28-Aug-2014
Python-Version:
2.7.9, 3.4.3, 3.5
Resolution:
Python-Dev message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

현재 표준 라이브러리 HTTP 클라이언트(urllib, urllib2, http, httplib 모듈)가 https:// URL을 만나면 이러한 서버와 통신하는 데 필요한 대로 네트워크 HTTP 트래픽을 TLS 스트림으로 래핑합니다. 그러나 TLS 핸드셰이크 중에는 서버가 어떤 신뢰 루트에 있는 CA가 서명한 X509 인증서를 보유하는지 실제로 확인하지 않으며, 제시된 인증서의 Common Name(또는 Subject Alternate Name)이 요청된 호스트와 일치하는지도 검증하지 않습니다.

이러한 검사를 수행하지 않으면 네트워크상 특권적 위치에 있는 누구나 이 HTTP 클라이언트 중 하나를 사용하는 Python 애플리케이션을 대상으로 중간자 공격을 손쉽게 실행하고 트래픽을 마음대로 변경할 수 있습니다.

이 PEP는 호출별 옵트아웃을 허용하는 조건으로 Python HTTP 클라이언트에서 X509 인증서 서명 검증과 호스트 이름 검증을 기본적으로 활성화할 것을 제안합니다. 이 변경 사항은 Python 2.7, Python 3.4 및 Python 3.5에 적용됩니다.

근거

“HTTPS”의 “S”는 secure를 의미합니다. Python 사용자가 “HTTPS”를 입력할 때는 보안 연결을 기대하므로, Python은 이를 제공할 때 합리적인 주의 기준을 준수해야 합니다. 현재 Python은 이 기준을 충족하지 못하고 있으며, 그 결과 단순해 보이는 API가 사용자를 오도하고 있습니다.

질문을 받으면 많은 Python 사용자가 Python이 이러한 검증을 수행하지 못한다는 사실을 알지 못했다고 말하며, 이에 충격을 받습니다.

requests의 인기는 (기본적으로 이러한 검사를 활성화하는) 이러한 검사가 어떤 면에서도 지나치게 부담스럽지 않다는 것을 보여 주며, 표준 라이브러리 클라이언트보다 보안을 크게 향상하는 방법으로 널리 권장된다는 사실은 많은 사용자가 도구에서 “security by default”에 대한 더 높은 기준을 기대한다는 것을 보여 줍니다.

여러 애플리케이션이 이 문제에 대한 Python의 태만을 인지하지 못한 결과 정기적인 CVE 할당이 발생하고 있습니다 [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11].

기술 세부 사항

Python은 모든 플랫폼에서 시스템이 제공하는 인증서 데이터베이스를 사용합니다. 이러한 데이터베이스를 찾지 못하면 오류가 발생하며, 사용자는 이를 해결하기 위해 위치를 명시적으로 지정해야 합니다.

이는 ssl._create_default_https_context라는 새 함수를 추가하여 구현하며, 이 함수는 ssl.create_default_context와 동일합니다.

그러면 http.clientssl._create_stdlib_context의 사용을 ssl._create_default_https_context로 바꿀 수 있습니다.

또한 ssl._create_stdlib_contextssl._create_unverified_context로 이름이 변경됩니다(하위 호환성을 위해 별칭은 계속 유지됩니다).

신뢰 데이터베이스

이 PEP는 시스템에서 제공하는 인증서 데이터베이스를 사용할 것을 제안합니다. 이전 논의에서는 Mozilla의 인증서 데이터베이스를 번들로 포함하고 기본적으로 사용하자는 제안이 있었습니다. 다음과 같은 여러 이유로 이에 반대하기로 결정했습니다.

  • 플랫폼 신뢰 데이터베이스를 사용하면 Python 개발자의 유지 관리 부담이 줄어듭니다. 자체 신뢰 데이터베이스를 제공하면 인증서가 폐기될 때마다 릴리스를 해야 하기 때문입니다.
  • Linux 공급업체와 기타 다운스트림은 Mozilla 인증서를 번들에서 제외할 것이며, 그 결과 동작이 더욱 파편화될 것입니다.
  • 플랫폼 저장소를 사용하면 기업 내부 CA와 같은 상황을 더 쉽게 처리할 수 있습니다.

OpenSSL에는 Python이 다른 인증서 데이터베이스를 가리키도록 설정하는 데 사용할 수 있는 환경 변수 쌍 SSL_CERT_DIRSSL_CERT_FILE도 있습니다.

하위 호환성

이 변경으로 인해 일부 HTTPS 연결이 “중단”되는 것처럼 보일 수 있습니다. 이제 핸드셰이크 중에 예외가 발생하기 때문입니다.

그러나 이는 오해의 소지가 있습니다. 실제로 이러한 연결은 현재 조용히 실패하고 있으며, HTTPS URL은 기밀성과 인증에 대한 기대를 나타냅니다. Python이 사용자의 요청이 실제로 이루어졌는지 검증하지 않는다는 사실은 버그이며, 더 나아가 “오류가 조용히 전달되어서는 안 됩니다.”

그럼에도 불구하고 자체 서명되었거나 잘못된 인증서를 사용하는 서버에 액세스해야 하는 사용자는 사용자 지정 신뢰 루트를 포함하거나 검증을 비활성화하는 컨텍스트를 제공하여 액세스할 수 있습니다(가능한 경우 전자를 강력히 권장하도록 문서화해야 합니다). 사용자는 필요한 인증서를 시스템 신뢰 저장소에 추가하여 시스템 전역에서 해당 인증서를 신뢰하도록 할 수도 있습니다.

Twisted의 14.0 릴리스에서도 동일한 변경이 이루어졌으며, 거의 아무런 반대에도 부딪히지 않았습니다.

선택적 제외

단일 연결에서 인증서 검증을 제외하려는 사용자는 urllib.urlopencontext 인자를 제공하면 됩니다.:

import ssl

# This restores the same behavior as before.
context = ssl._create_unverified_context()
urllib.urlopen("https://no-valid-cert", context=context)

Python의 이 PEP 구현 버전에서는 매우 권장하지 않지만, ssl 모듈을 몽키 패치하여 검증을 전역적으로 비활성화할 수도 있습니다.:

import ssl

try:
    _create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
    # Legacy Python that doesn't verify HTTPS certificates by default
    pass
else:
    # Handle target environment that doesn't support HTTPS verification
    ssl._create_default_https_context = _create_unverified_https_context

이 지침은 HTTPS 연결에서 아직 인증서 검증을 지원하지 않는 레거시 환경에 이 PEP를 구현한 최신 버전의 Python을 도입하려는 시스템 관리자들을 주된 대상으로 합니다. 예를 들어 관리자는 Python의 표준 운영 환경에 있는 sitecustomize.py에 위의 몽키 패치를 추가하여 선택적으로 제외할 수 있습니다. 애플리케이션과 라이브러리는 프로세스 전체에 걸쳐 이 변경을 적용해서는 안 됩니다(SYSTEM 관리자가 제어하는 구성 설정에 대한 응답인 경우는 예외일 수 있습니다).

특히 보안에 민감한 애플리케이션은 기본 Python 구현의 동작에 의존하지 말고 항상 애플리케이션에서 명시적으로 정의한 SSL 컨텍스트를 제공해야 합니다.

기타 프로토콜

이 PEP는 SMTP와 같은 다른 프로토콜이 아니라 HTTP 클라이언트에 대해서만 이 수준의 검증을 요구할 것을 제안합니다.

브라우저에서 검증을 수행한 결과 높은 비율의 HTTPS 서버가 올바른 인증서를 보유하고 있는 반면, 다른 프로토콜에서는 자체 서명되었거나 그 밖의 이유로 잘못된 인증서가 훨씬 더 흔하기 때문입니다. 적어도 SMTP의 경우 이러한 상황이 변하고 있는 것으로 보이므로, 향후 유사한 PEP의 가능성을 검토해야 합니다.

Python 버전

이 PEP는 3.4.x, 3.5 및 2.7.X 브랜치 모두에서 발생할 변경 사항을 설명합니다. 2.7.X에서는 이미 PEP 466에서 백포팅된 기능에 더해 context (SSLContext) 인자를 httplib에 백포팅해야 합니다.

구현

  • LANDED: Issue 22366urlib.request.urlopencontext 인자를 추가합니다.
  • Issue 22417은 이 PEP의 본질을 구현합니다.