본문 바로가기
Python tips

urllib3 request 사용해서 requests.exceptions.SSLError 해결

by 스티브 십잡스 2024. 5. 15.

requests 라이브러리를 통해 외부 API 개발을 하는 도중,

로컬 및 개발 환경에서는 큰 문제가 없었지만 운영 환경에서 SSLError가 발생했다.

verify=False 인자를 추가하거나 파이썬 3.9.x 버전을 사용하는 것은 좋은 방법이 아닌 것 같아 다른 해결 방법을 찾던 중 좋은 자료를 찾았다. (하단 참고)

 

urllib3가 requests보다 더 low level의 코드라고 한다. 자세한 건 chatgpt에게 물어보는 것도 좋다.

예제를 통해 두 라이브러리를 비교하면서 알아보자.

 

[GET] request

import hashlib

import requests

params ={
    "key_a": "val_a",
    "key_b": "val_b",
    "key_c": "val_c",
}
sig = ""
for key, value in params.items():
    sig += f"{key}={value}&"
sig += f"hashKey={HASH_KEY}"
signature = hashlib.sha256(sig.encode()).hexdigest()

params['signature'] = signature

response = requests.get(
    url="요청 URL",
    params=params,
)

status = response.status_code
result = response.json()

 

[GET] urllib3 

import hashlib

import ssl
from urllib3 import PoolManager
from urllib3.util import create_urllib3_context

from config import settings  # 개인 환경 설정

# params 설정을 동일하다.
params ={
    "key_a": "val_a",
    "key_b": "val_b",
    "key_c": "val_c",
}
sig = ""
for key, value in params.items():
    sig += f"{key}={value}&"
sig += f"hashKey={HASH_KEY}"
signature = hashlib.sha256(sig.encode()).hexdigest()

params['signature'] = signature

# SSLError가 발생하지 않는 로컬 및 개발 환경에서는 ctx 인자가 필요없다.
if settings.ENV != "PROD":
    pool_manager = PoolManager()
else:
    ctx = create_urllib3_context()
    ctx.load_default_certs()
    ctx.set_ciphers('AES256-GCM-SHA384')
    pool_manager = PoolManager(ssl_context=ctx)

with pool_manager as pool:
    response = pool.request(
        method='GET',
        url='요청 URL',
        fields=params,
    )

status = response.status
result = response.json()

 

[POST] request

import hashlib
import json

import requests

from config import settings  # 개인 환경 설정

# body 설정을 동일하다.
body ={
    "key_a": "val_a",
    "key_b": "val_b",
    "key_c": "val_c",
}
sig = ""
for key, value in params.items():
    sig += f"{key}={value}&"
sig += f"hashKey={HASH_KEY}"
signature = hashlib.sha256(sig.encode()).hexdigest()

body['signature'] = signature

response = requests.post(
    url='요청 URL',
    headers={
        "Content-Type": "application/json;charset=utf-8"
    },
    data=body,
)

status = response.status_code
result = response.json()

 

[POST] urllib3 - application/json

import hashlib
import json

import ssl
from urllib3 import PoolManager
from urllib3.util import create_urllib3_context

from config import settings  # 개인 환경 설정

# body 설정을 동일하다.
body ={
    "key_a": "val_a",
    "key_b": "val_b",
    "key_c": "val_c",
}
sig = ""
for key, value in params.items():
    sig += f"{key}={value}&"
sig += f"hashKey={HASH_KEY}"
signature = hashlib.sha256(sig.encode()).hexdigest()

body['signature'] = signature

# SSLError가 발생하지 않는 로컬 및 개발 환경에서는 ctx 인자가 필요없다.
if settings.ENV != "PROD":
    pool_manager = PoolManager()
else:
    ctx = create_urllib3_context()
    ctx.load_default_certs()
    ctx.set_ciphers('AES256-GCM-SHA384')
    pool_manager = PoolManager(ssl_context=ctx)

with pool_manager as pool:
    response = pool.request(
        method='POST',
        url='요청 URL',
        headers={
            "Content-Type": "application/json;charset=utf-8"
        },
        json=body,  # json.dumps(body).encode('utf-8'),
        timeout=3.0,
    )

status = response.status
result = response.json()

 

[POST] urllib3 - application/x-www-form-urlencoded

import hashlib

import ssl
from urllib3 import PoolManager
from urllib3.util import create_urllib3_context
from urllib.parse import urlencode

from config import settings  # 개인 환경 설정

# body 설정을 동일하다.
body ={
    "key_a": "val_a",
    "key_b": "val_b",
    "key_c": "val_c",
}
sig = ""
for key, value in params.items():
    sig += f"{key}={value}&"
sig += f"hashKey={HASH_KEY}"
signature = hashlib.sha256(sig.encode()).hexdigest()

body['signature'] = signature

# SSLError가 발생하지 않는 로컬 및 개발 환경에서는 ctx 인자가 필요없다.
if settings.ENV != "PROD":
    pool_manager = PoolManager()
else:
    ctx = create_urllib3_context()
    ctx.load_default_certs()
    ctx.set_ciphers('AES256-GCM-SHA384')
    pool_manager = PoolManager(ssl_context=ctx)

with pool_manager as pool:
    response = pool.request(
        method='POST',
        url='요청 URL',
        headers={
            "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
        },
        body=urlencode(body),
        timeout=3.0,
    )

status = response.status
result = response.json()
  • Content-Type에 따라 파라미터가 다르다.
  • json: json = json.dumps().encode('utf-8')
  • x-www-form-urlencoded: body = urlencode()

 

관련 자료

 

sslv3 alert handshake failure · Issue #6616 · psf/requests

Trying to connect https://www.irctc.co.in using requests module but it raises the ssl error when using requests==2.31.0 but not on when using requests==2.29.0 but have not tried it on v2.30 Expecte...

github.com

 

Fixing SSLV3_ALERT_HANDSHAKE_FAILURE with urllib3 2.0

Over the years, the urllib3 HTTP client maintained its SSL cipher suites based on Hynek's evergreen list. However, with the release of urllib3 version 2.0, we decided to require OpenSSL 1.1.1 or above and rely on the default cipher suites, which are secure

quentin.pradet.me

 

urllib3 docs

 

User Guide

Installing: urllib3 can be installed with pip Making Requests: First things first, import the urllib3 module: You’ll need a PoolManager instance to make requests. This object handles all of the det...

urllib3.readthedocs.io