프록시를 통한 쿠키 문제 해결 방법
쿠키는 프록시 작업 시 가장 흔한 오류 원인 중 하나입니다. 세션이 끊어지고, 인증이 실패하며, 데이터가 손실됩니다. 이 글에서는 이것이 발생하는 이유와 안정적인 작업을 위해 쿠키 처리를 올바르게 설정하는 방법을 알아봅시다.
프록시 사용 시 쿠키가 손실되는 이유
프록시를 통해 요청을 보내면 클라이언트와 대상 서버 사이에 중간 노드가 개입합니다. 이는 여러 문제를 야기합니다:
- 한 세션에 대한 다양한 IP 주소. 서버는 요청이 다른 주소에서 오는 것을 감지하고 쿠키를 의심스러운 것으로 거부할 수 있습니다.
- Set-Cookie 헤더 손실. 프록시 설정이 잘못되면 Set-Cookie 헤더를 클라이언트에 전달하지 않을 수 있습니다.
- 도메인 및 경로 불일치. 프록시가 Host 헤더를 다시 작성하면 도메인 불일치로 인해 쿠키가 저장되지 않을 수 있습니다.
- 상태 저장 부재. 쿠키를 저장하지 않고 각 요청을 별도로 보내면 세션이 손실됩니다.
쿠키 jar란 무엇이고 어떻게 사용하나요
쿠키 jar는 쿠키를 자동으로 관리하는 저장소입니다. 각 요청에 쿠키 헤더를 수동으로 추가하는 대신 라이브러리가 자동으로 처리하도록 합니다.
대부분의 HTTP 클라이언트는 쿠키 jar를 기본 지원합니다:
import requests
from requests.cookies import RequestsCookieJar
# 쿠키 저장용 jar 생성
jar = RequestsCookieJar()
# 첫 번째 요청 — 서버가 Set-Cookie 전송
response1 = requests.get(
'https://example.com/login',
cookies=jar,
proxies={'https': 'http://proxy.example.com:8080'}
)
# 쿠키가 자동으로 jar에 저장됨
print(jar)
# 두 번째 요청 — 쿠키가 자동으로 전송됨
response2 = requests.get(
'https://example.com/dashboard',
cookies=jar,
proxies={'https': 'http://proxy.example.com:8080'}
)
jar 없이는 Set-Cookie를 수동으로 파싱하고 다음 요청에 추가해야 하므로 신뢰할 수 없고 번거롭습니다.
요청 간 쿠키 저장
스크립트가 오래 실행되거나 재시작 후 세션을 복구해야 하는 경우 쿠키를 파일에 저장하세요:
import requests
from http.cookiejar import LWPCookieJar
# 파일에 저장하는 jar 생성
jar = LWPCookieJar('cookies.txt')
# 기존 쿠키가 있으면 로드
try:
jar.load(ignore_discard=True, ignore_expires=True)
except FileNotFoundError:
pass
# 요청에 jar 사용
response = requests.get(
'https://example.com/login',
cookies=jar,
proxies={'https': 'http://proxy.example.com:8080'}
)
# 업데이트된 쿠키 저장
jar.save(ignore_discard=True, ignore_expires=True)
ignore_discard=True 및 ignore_expires=True 플래그를 사용하면 임시 쿠키도 저장할 수 있습니다.
쿠키 도메인 바인딩 문제
쿠키는 어떤 도메인에 대해 전송될지를 결정하는 Domain 속성을 가집니다. 다음과 같은 경우 문제가 발생합니다:
- 프록시가 Host를 다시 작성합니다. 프록시가 Host 헤더를 변경하면 쿠키 jar가 다른 도메인에 속한 것으로 쿠키를 거부할 수 있습니다.
- 서브도메인이 일치하지 않습니다.
example.com의 쿠키는api.example.com에 전송되지 않을 수 있습니다. - 경로가 일치하지 않습니다.
/api의 쿠키는/admin에 전송되지 않습니다.
다음과 같이 쿠키 속성을 확인하세요:
import requests
response = requests.get(
'https://example.com',
proxies={'https': 'http://proxy.example.com:8080'}
)
# 모든 쿠키 출력
for cookie in response.cookies:
print(f"Name: {cookie.name}")
print(f"Value: {cookie.value}")
print(f"Domain: {cookie.domain}")
print(f"Path: {cookie.path}")
print(f"Secure: {cookie.secure}")
print(f"HttpOnly: {cookie.has_nonstandard_attr('HttpOnly')}")
print("---")
Domain이 너무 좁으면 자동 관리 대신 쿠키를 명시적으로 지정해 보세요:
headers = {
'Cookie': 'session_id=abc123; user_token=xyz789'
}
response = requests.get(
'https://example.com/api',
headers=headers,
proxies={'https': 'http://proxy.example.com:8080'}
)
Secure 및 HttpOnly 플래그
Secure 플래그는 쿠키가 HTTPS를 통해서만 전송됨을 의미합니다. HTTP 프록시를 사용하여 HTTPS 리소스에 액세스하는 경우 프록시 연결이 보호되거나 프록시가 HTTPS를 올바르게 터널링하는지 확인하세요.
HttpOnly 플래그는 JavaScript에서 쿠키에 액세스하는 것을 금지합니다. 이는 요청에서 쿠키 전송에는 영향을 주지 않지만 브라우저에서 이러한 쿠키를 읽을 수 없다는 점을 기억하세요.
레지던셜 프록시를 사용할 때 다음을 확인하세요:
- 프록시가 HTTPS를 지원합니다 (CONNECT 메서드)
- 인증서가 유효합니다 (프로덕션에서
verify=False사용 금지) - 헤더가 프록시에 의해 다시 작성되지 않습니다
코드를 포함한 실용적인 예제
예제 1: 세션 저장을 통한 로그인
import requests
from requests.cookies import RequestsCookieJar
jar = RequestsCookieJar()
proxy = 'http://proxy.example.com:8080'
# 로그인
login_response = requests.post(
'https://example.com/login',
data={'username': 'user', 'password': 'pass'},
cookies=jar,
proxies={'https': proxy}
)
if login_response.status_code == 200:
print("로그인 성공")
# 저장된 세션 사용
dashboard = requests.get(
'https://example.com/dashboard',
cookies=jar,
proxies={'https': proxy}
)
print(dashboard.text)
예제 2: 여러 요청 처리
import requests
from http.cookiejar import LWPCookieJar
import time
jar = LWPCookieJar('session.txt')
try:
jar.load(ignore_discard=True)
except:
pass
proxy = 'http://proxy.example.com:8080'
urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3'
]
for url in urls:
response = requests.get(
url,
cookies=jar,
proxies={'https': proxy},
timeout=10
)
print(f"{url}: {response.status_code}")
jar.save(ignore_discard=True)
time.sleep(1) # 서버 과부하 방지
예제 3: 문제 발생 시 명시적 쿠키 전달
import requests
proxy = 'http://proxy.example.com:8080'
# 자동 관리가 작동하지 않는 경우
cookies_dict = {
'session_id': 'abc123def456',
'user_pref': 'dark_mode'
}
headers = {
'User-Agent': 'Mozilla/5.0...',
'Cookie': '; '.join([f"{k}={v}" for k, v in cookies_dict.items()])
}
response = requests.get(
'https://example.com/api/data',
headers=headers,
proxies={'https': proxy}
)
print(response.json())
쿠키 문제 디버깅
쿠키가 작동하지 않으면 다음 도구를 사용하세요:
| 도구 | 용도 |
|---|---|
requests.Session |
세션의 모든 요청에 대해 쿠키를 자동으로 관리합니다 |
logging |
모든 헤더를 보기 위해 requests에 DEBUG를 활성화합니다 |
Fiddler / Charles |
트래픽을 가로채고 Set-Cookie 및 Cookie 헤더를 확인합니다 |
curl -v |
명령줄에서 프록시를 통해 동일한 작업을 테스트합니다 |
디버깅을 위해 로깅을 활성화하세요:
import logging
import requests
logging.basicConfig(level=logging.DEBUG)
# 이제 모든 요청이 헤더와 쿠키를 출력합니다
response = requests.get(
'https://example.com',
proxies={'https': 'http://proxy.example.com:8080'}
)
프록시가 쿠키를 차단하지 않는지 확인하세요:
curl -v -x http://proxy.example.com:8080 https://example.com
# 헤더를 확인하세요:
# Set-Cookie: ... (있어야 함)
# Cookie: ... (다음 요청에서 전송되어야 함)
팁: 레지던셜 프록시를 사용하는 경우 요청 간에 IP가 로테이션될 수 있습니다. 쿠키 처리 로직이 이를 고려하는지 확인하세요. 일부 서버는 한 세션에서 다른 IP의 요청을 거부합니다.
요약
프록시를 통한 쿠키 문제는 올바른 설정으로 해결됩니다:
- 쿠키 jar 사용으로 자동 쿠키 관리
- 요청 간 쿠키를 파일에 저장
- 도메인 바인딩 및 경로 속성 확인
- 프록시의 HTTPS 지원 확인
- 디버깅 도구를 사용하여 문제 파악
프록시를 통한 자동화 및 파싱 작업에서 안정적인 세션 작업이 필요한 경우 HTTPS 지원 및 쿠키 관리가 가능한 레지던셜 프록시가 적합합니다. 간단한 쿠키 jar부터 시작하여 필요한 경우에만 더 복잡한 방식으로 전환하세요.