Python 요청에서 SSL 보안 인증서 확인 무시
보안 SSL 인증서 없이 URL에 액세스하면 HTTP 요청이 전송될 때 예외 경고가 발생합니다. 많은 경우 이러한 URL의 SSL 인증서가 만료되어 모든 종류의 보안 문제가 발생합니다.
정보가 민감하지 않은 경우 프로그램이 Python에서 requests
를 사용할 때 이러한 경고가 가라앉을 수 있습니다. 이 문서에서는 요청
을 사용하여 보안 인증서 검사를 비활성화하는 여러 가지 방법을 제공합니다.
SSL 보안 검사의 이유와 실패 이유 이해
프로그램이 Python 요청
을 사용하여 SSL 인증서가 만료된 URL에서 요청을 받는 경우 두 가지 예외가 발생합니다. 아래 프로그램은 이러한 예외가 무엇인지 표시합니다.
시나리오 1
이 프로그램은 테스트 목적으로 보안 인증서가 만료된 SSL 커뮤니티에서 제공한 URL을 사용합니다. SSL
보안 예외는 SSL
인증서가 만료된 URL에서만 발생한다는 점에 유의해야 합니다.
Python 요청
은 유효한 SSL
인증서 또는 취소된 인증서가 있는 URL로 예외를 발생시키지 않습니다. 따라서 이 문서에서는 보안 인증서가 만료된 URL에 주로 초점을 맞춥니다.
아래 예는 첫 번째 줄에서 requests
를 가져오는 간단한 프로그램을 보여줍니다. 프로그램의 두 번째 라인은 post
요청을 URL로 전송하여 'bar'
를 'baz'
로 수정합니다.
URL에 포스트
요청을 보내기 위해 수행되며 프로그램 내에서 다른 의미는 없습니다.
import requests
requests.post(url="https://expired-rsa-dv.ssl.com/", data={"bar": "baz"})
이 Python 스크립트를 실행하면 SSLError
예외가 발생합니다.
....
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='expired-rsa-dv.ssl.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:841)'),))
이것은 요청
을 사용하여 보안 인증서 확인을 비활성화하는 방법을 학습할 때 고려해야 할 첫 번째 예외입니다.
시나리오 2
이 프로그램은 requests
를 사용하여 보안 인증서 확인을 비활성화하기 위해 verify=False
를 사용하여 SSL
인증서 확인을 끕니다.
import requests
requests.post(url="https://expired-rsa-dv.ssl.com/", data={"bar": "baz"}, verify=False)
requests
라이브러리는 SSL
인증서에 대한 확인을 끌 수 있는 방식으로 구축되었지만 프로그램은 SSL
인증서가 만료된 링크에 대해 또 다른 예외를 발생시킵니다.
InsecureRequestWarning: Unverified HTTPS request is being made to host 'expired-rsa-dv.ssl.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
InsecureRequestWarning,
requests
를 사용하여 보안 인증서 확인을 비활성화하려면 이 두 가지 예외를 처리해야 합니다.
Python에서 SSL 보안 검사 무시
이 섹션에서는 요청
을 사용하여 보안 인증서 확인을 비활성화하거나 문제를 해결하는 다양한 방법을 설명합니다. 모든 방법에는 목적이 있습니다.
requests
라이브러리에 대한 원숭이 패치 만들기
타사 라이브러리에서 보안 검사를 비활성화해야 하는 경우 requests
라이브러리를 monkey patched할 수 있습니다. 컨텍스트 관리자는 requests
를 사용하여 보안 인증서 검사를 비활성화하는 패치에 사용됩니다.
requests
가 패치된 후 verify
필드에 기본적으로 False
값이 지정되어 경고가 표시되지 않습니다. 이전 섹션의 시나리오 2에서 설명한 대로 verify=false
를 사용하면 또 다른 경고가 발생합니다.
그런 다음 패치는 requests
를 사용하여 보안 인증서 확인을 비활성화하고 경고를 억제하는 예외 처리 블록을 추가합니다. 다음 예제를 통해 이해하기 쉬울 것입니다.
아래 프로그램은 requests
라이브러리를 패치합니다. 이 코드가 무엇을 하는지 이해해 봅시다.
수입:
경고
: 이 라이브러리 패키지는예외
라이브러리의 하위 패키지입니다.contextlib
: 이 Python 라이브러리는requests
라이브러리 패치에 사용됩니다.requests
: Python의 요청 라이브러리 패키지.urllib3
: Python에서 HTTP 요청 및 URL을 처리하는 모듈입니다. 이 라이브러리는 만료된SSL
인증서에 대한 예외를 발생시키는InsecureRequestWarning
하위 모듈을 가져오는 데 사용됩니다.
반점:
처음에 프로그램은 requests
라이브러리의 기본 환경 설정을 old_merge_environment_settings
변수에 저장합니다. 이 변수는 열린 어댑터가 닫힌 후 요청
을 기본 상태로 되돌리는 데 사용됩니다.
old_merge_environment_settings = requests.Session.merge_environment_settings
no_ssl_verification
메서드가 생성되고 @contextlib.contextmanager
로 장식됩니다. 새 변수 opened_adapters
가 생성되고 여기에 set()
이 할당됩니다.
세트는 항목을 정렬되지 않은 형식으로 저장하는 데이터 유형입니다.
@contextlib.contextmanager
def no_ssl_verification():
opened_adapters = set()
no_ssl_verification
메서드 내에서 또 다른 중첩 메서드 merge_environment_settings
가 생성됩니다. 이 메서드에는 requests
모듈의 merge_environment_settings
와 유사한 6개의 매개변수가 있으며 원래 모듈의 패치 역할을 합니다.
def merge_environment_settings(self, url, proxies, stream, verify, cert):
메서드 내에서 opened_adapters
변수는 url
매개 변수에서 일치하는 어댑터 쌍으로 업데이트됩니다. 모든 연결은 이 단계에서 반환되는 일부 일치하는 어댑터 쌍으로 이루어집니다.
확인은 연결당 한 번만 발생하므로 확인이 완료되면 열려 있는 모든 어댑터를 종료해야 합니다. 그렇지 않은 경우 verify=False
의 효과는 이 컨텍스트 관리자가 종료된 후에 지속됩니다.
def merge_environment_settings(self, url, proxies, stream, verify, cert):
opened_adapters.add(self.get_adapter(url))
코드의 다음 줄을 이해하려면 기사의 첫 번째 섹션을 기억하는 것이 중요합니다. 만료된 SSL
인증서가 있는 URL에서 requests.post
기능을 사용하면 두 가지 예외가 발생했습니다.
첫 번째 예외는 True
값으로 설정된 verify
에 의해 발생했습니다. verify
필드는 전환 가능하지만 False
값을 지정할 수 있습니다.
두 번째 예외는 프로그램의 연결을 중지시키는 변경 불가능한 엔터티였습니다.
아래 코드는 이 문제를 해결하기 위해 기본적으로 Valse
값을 갖도록 verify
필드를 수정합니다. 이 단계를 실행하기 위해 새 변수 settings
가 생성되고 old_merge_environment_settings
변수의 데이터가 할당됩니다.
데이터가 settings
변수에 저장되면 verify
필드가 False
로 바뀝니다.
이는 verify
의 기본값을 변경합니다. 마지막으로 이 변수가 반환됩니다.
settings = old_merge_environment_settings(self, url, proxies, stream, verify, cert)
settings["verify"] = False
return settings
업데이트된 merge_environment_settings
메서드의 데이터는 requests.Session.merge_environment_settings
기능에 할당됩니다. 그러면 verify
필드에 기본적으로 False
값이 지정됩니다.
requests.Session.merge_environment_settings = merge_environment_settings
첫 번째 예외를 처리했습니다. 변경할 수 없는 두 번째 예외는 예외 처리 블록을 사용하여 해결됩니다.
코드가 여기서 무엇을 하는지 이해합시다.
처음에는 try
블록 내부에 제기된 모든 경고를 포착하는 with
블록이 생성됩니다. 이 with
블록 내에서 warnings.simplefilter
기능은 urllib3
하위 모듈 InsecureRequestWarning
에 ignore
값을 제공하는 데 사용됩니다.
가져오기 섹션에서 배운 것처럼 InsecureRequestWarning
하위 모듈은 SSL
인증서 만료에 대한 예외를 발생시킵니다. ignore
값을 할당하면 발생한 두 번째 예외를 우회합니다.
try:
with warnings.catch_warnings():
warnings.simplefilter('ignore', InsecureRequestWarning)
yield
finally
블록 내에서 old_merge_environment_settings
에 저장된 원래 설정은 requests.Session.merge_environment_settings
기능에 다시 할당됩니다.
프로그램이 종료되기 전에 열려 있는 모든 어댑터를 닫아야 하므로 열린 어댑터가 프로그램 내에 있는 횟수만큼 반복되는 for
루프가 생성됩니다.
모든 어댑터는 try-except
블록을 사용하는 adapter.close()
함수를 사용하여 닫힙니다. except
블록 내부에서 반복이 전달됩니다.
for adapter in opened_adapters:
try:
adapter.close()
except:
pass
전체 코드는 다음과 같습니다.
import warnings
import contextlib
import requests
from urllib3.exceptions import InsecureRequestWarning
old_merge_environment_settings = requests.Session.merge_environment_settings
@contextlib.contextmanager
def no_ssl_verification():
opened_adapters = set()
def merge_environment_settings(self, url, proxies, stream, verify, cert):
opened_adapters.add(self.get_adapter(url))
settings = old_merge_environment_settings(
self, url, proxies, stream, verify, cert
)
settings["verify"] = False
return settings
requests.Session.merge_environment_settings = merge_environment_settings
try:
with warnings.catch_warnings():
warnings.simplefilter("ignore", InsecureRequestWarning)
yield
finally:
requests.Session.merge_environment_settings = old_merge_environment_settings
for adapter in opened_adapters:
try:
adapter.close()
except:
pass
Python에서 원숭이 패치 사용
원숭이 패치는 no_ssl_verification()
메서드를 사용하여 호출할 수 있습니다. 이 방법으로 전송된 모든 요청은 monkey patch의 지시문을 따르고 requests
를 사용하여 보안 인증서 검사를 비활성화합니다.
예를 들어 보겠습니다.
no_ssl_verification
메서드는 with
블록 아래에서 호출됩니다. 이렇게 하면 블록 내부에서 메서드가 실행되고 컴파일러가 블록 밖으로 나올 때 자체적으로 닫힙니다.
블록 내에서 requests.get
호출이 만료된 SSL
인증서가 있는 URL로 전송됩니다. 기본 설정을 사용하면 예외 발생이 발생하지만 이번에는 requests.get
호출이 성공적으로 전송됩니다.
또 다른 요청은 requests
와 함께 전송되며 verify
필드는 True
로 설정됩니다. 기본 시나리오와 달리 이번에는 오류 예외가 발생하지 않습니다.
with no_ssl_verification():
requests.get("https://expired-rsa-dv.ssl.com/")
print("Modifications working Properly")
requests.get("https://expired-rsa-dv.ssl.com/", verify=True)
print("`Verify=true` has its meaning changed")
출력:
Modifications working Properly
`Verify=true` has its meaning changed
Verify=False
에는 기본 설정으로 패치를 재설정하라는 지시문이 있는 것으로 나타났습니다. 이 필드가 requests.get()
내에서 설정되면 원숭이 패치가 기본 설정으로 재설정되어 오류 예외를 발생시킬 수 있습니다.
requests.get("https://expired-rsa-dv.ssl.com/", verify=False)
print("It resets back")
출력:
connectionpool.py:1052: InsecureRequestWarning: Unverified HTTPS request is being made to host 'expired-rsa-dv.ssl.com'.
InsecureRequestWarning,
It resets back
요청
하위 모듈 세션
은 원숭이 패치와 함께 사용할 수도 있습니다.
session
이라는 변수에서 requests.Session()
함수가 로드됩니다. session.verify
값은 True
로 설정됩니다.
with
블록 내에서 session.get()
함수는 get
요청을 URL로 보내는 데 사용되며 verify
필드는 True
로 설정됩니다. print
문은 연결이 이루어진 경우 메시지를 인쇄합니다.
session = requests.Session()
session.verify = True
with no_ssl_verification():
session.get("https://expired-rsa-dv.ssl.com/", verify=True)
print("Works in Session")
출력:
Works in Session
컨텍스트 관리자가 종료되면 이 코드는 패치된 요청을 처리하는 모든 열린 어댑터를 닫습니다. requests
는 각 세션에 대한 연결 풀을 유지하고 인증서 유효성 검사는 연결당 한 번만 발생하므로 다음과 같은 예기치 않은 이벤트가 발생합니다.
try:
requests.get("https://expired-rsa-dv.ssl.com/", verify=False)
except requests.exceptions.SSLError:
print("It breaks")
try:
session.get("https://expired-rsa-dv.ssl.com/")
except requests.exceptions.SSLError:
print("It breaks here again")
출력:
C:\Users\Win 10\venv\lib\site-packages\urllib3\connectionpool.py:1052: InsecureRequestWarning: Unverified HTTPS request is being made to host 'expired-rsa-dv.ssl.com'.
InsecureRequestWarning,
It breaks here again
urllib3
을 사용하여 Python에서 경고 비활성화
프로그램에서 requests
또는 Monkey Patching을 사용하지 않고 보안 인증서 확인을 비활성화해야 하는 경우 urllib3
을 사용하면 간단한 솔루션을 제공합니다.
requests
및 urllib3
을 가져오고 urllib3
에서 InsecureRequestWarning
하위 모듈을 가져옵니다. urllib3.disable_warnings
기능을 사용하여 경고를 비활성화합니다.
requests.post()
문은 try
블록 아래에 배치되고 verify
필드는 False
로 설정됩니다. 이렇게 하면 만료된 보안 인증서에 대한 보안 검사가 비활성화됩니다.
except
블록 내에서 SSLError
가 오류 메시지와 함께 발생합니다.
import requests
from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
try:
requests.post(
url="https://expired-rsa-dv.ssl.com/", data={"bar": "baz"}, verify=False
)
print("Connection made successfully")
except requests.exceptions.SSLError:
print("Expired Certificate")
출력:
Connection made successfully
결론
이 문서에서는 Python에서 requests
를 사용하여 보안 인증서 확인을 비활성화하는 다양한 방법을 설명합니다. 독자는 기사를 통해 보안 검사를 쉽게 비활성화할 수 있습니다.