- curl-impersonate는 Chrome, Edge, Safari, Firefox처럼 보이는 TLS 및 HTTP 핸드셰이크를 수행하도록 수정된 curl 빌드이며, CLI 도구와
libcurl대체 라이브러리로 사용할 수 있음 - 일반 HTTP 클라이언트와 라이브러리의 Client Hello 및 HTTP/2 설정은 실제 브라우저와 크게 달라, 일부 웹 서비스가 TLS/HTTP 핸드셰이크로 클라이언트를 식별하고 다른 콘텐츠를 제공함
- 구현은 Firefox용 NSS, Chrome 계열용 BoringSSL 사용, TLS 확장·SSL 옵션·HTTP/2 설정 변경,
--ciphers,--curves, 헤더 같은 비기본 플래그 적용으로 구성됨 - 지원 대상은 Chrome 99~116, Android Chrome 99, Edge 99·101, Firefox 91 ESR~117, Safari 15.3·15.5이며 각 대상마다 wrapper script와 target name이 제공됨
- 명령줄에서는
curl_chrome116 실행하고, 라이브러리 통합에서는curl_easy_impersonate()또는 Linux의LD_PRELOAD와CURL_IMPERSONATE로 기존libcurl` 사용 앱에 적용할 수 있음
curl-impersonate가 해결하는 문제
- curl-impersonate는 curl을 특별히 빌드해 Chrome, Edge, Safari, Firefox 4대 브라우저를 모방할 수 있게 만든 프로젝트임
- TLS와 HTTP 핸드셰이크를 실제 브라우저와 동일하게 수행할 수 있음
- 사용 방식은 두 가지임
- 일반 curl과 비슷한 명령줄 도구
- 기존
libcurl대신 통합할 수 있는 라이브러리
왜 필요한가
- HTTP 클라이언트가 TLS 웹사이트에 접속하면 먼저 TLS 핸드셰이크를 수행함
- TLS 핸드셰이크의 첫 메시지는 Client Hello이며, 대부분의 HTTP 클라이언트와 라이브러리가 만드는 Client Hello는 실제 브라우저와 크게 다름
- 서버가 HTTP/2를 사용하면 TLS 핸드셰이크 외에 HTTP/2 핸드셰이크도 수행되고, 여기서 여러 설정이 교환됨
- 대부분의 HTTP 클라이언트와 라이브러리의 HTTP/2 설정도 실제 브라우저와 다름
- 일부 웹 서비스는 이런 차이를 이용해 접속 클라이언트를 식별하고, 클라이언트별로 다른 콘텐츠를 제공함
- 이 방식은 TLS fingerprinting과 HTTP/2 fingerprinting으로 불림
- README는 이런 방식의 광범위한 사용이 웹을 덜 개방적이고, 덜 사적이며, 특정 웹 클라이언트에 더 제한적으로 만들었다고 밝힘
작동 방식
curl은 브라우저처럼 보이도록 상당히 패치됨- 주요 변경 사항은 다음과 같음
- Firefox 버전은 OpenSSL 대신 Firefox가 사용하는 TLS 라이브러리인 NSS로 curl을 컴파일함
- Chrome 버전은 Google의 TLS 라이브러리인 BoringSSL로 컴파일함
- curl이 여러 TLS 확장과 SSL 옵션을 설정하는 방식을 변경함
- 새로운 TLS 확장 지원을 추가함
- HTTP/2 연결에 사용하는 설정을 변경함
--ciphers,--curves, 일부-H헤더 같은 비기본 플래그로 curl을 실행함
- 그 결과 네트워크 관점에서 curl이 실제 브라우저와 동일하게 보임
- 전체 기술 설명은 part a, part b에 있음
지원 브라우저와 대상 이름
- 지원 브라우저 목록은
browsers.json에도 제공됨 - Chrome 계열 지원 대상
- Chrome 99, 100, 101, 104, 107, 110, 116 on Windows 10
- Chrome 99 on Android 12
- Edge 99, 101 on Windows 10
- Safari 15.3 on MacOS Big Sur
- Safari 15.5 on MacOS Monterey
- Firefox 지원 대상
- Firefox 91 ESR, 95, 98, 100, 102, 109, 117 on Windows 10
- 각 지원 대상에는 target name과 wrapper script가 있음
- 예:
chrome116target은curl_chrome116wrapper script로 실행함 - 예:
ff117target은curl_ff117wrapper script로 실행함
- 예:
기본 사용법
- 각 지원 브라우저마다 필요한 헤더와 플래그를 포함해
curl-impersonate를 실행하는 wrapper script가 있음 - 예시 실행
curl_chrome116 https://www.wikipedia.org - 명령줄 플래그를 추가하면 curl로 전달됨
- 일부 플래그는 curl의 TLS signature를 바꿔 탐지될 수 있음
- wrapper script는 기본 HTTP 헤더 세트를 사용함
- 헤더를 바꾸려면 목적에 맞게 wrapper script를 수정할 수 있음
- 더 많은 옵션은
libcurl-impersonate를 라이브러리로 사용하는 고급 사용법에 포함됨
설치와 배포 방식
- 기술적 이유로
curl-impersonate는 두 가지 버전이 있음- chrome 버전: Chrome, Edge, Safari 모방에 사용
- firefox 버전: Firefox 모방에 사용
- Linux와 macOS Intel용 사전 컴파일 바이너리는 GitHub releases에서 제공됨
- 사전 컴파일 바이너리 사용 전 NSS와 CA 인증서 설치가 필요함
- Ubuntu:
sudo apt install libnss3 nss-plugin-pem ca-certificates - Red Hat/Fedora/CentOS:
yum install nss nss-pem ca-certificates - Archlinux:
pacman -S nss ca-certificates - macOS:
brew install nss ca-certificates
- Ubuntu:
- 시스템에 zlib도 필요함
- zlib은 거의 항상 있지만 일부 최소 시스템에서는 없을 수 있음
- 사전 컴파일 Linux 바이너리는 Ubuntu 시스템용으로 빌드됨
- 다른 배포판에서 인증서 검증 오류가 나면 CA 인증서 위치를 curl에 알려줘야 할 수 있음
curl_chrome116 https://www.wikipedia.org --cacert /etc/ssl/certs/ca-bundle.crt - 소스 빌드는 INSTALL.md를 참고함
Docker와 패키지
- Alpine Linux와 Debian 기반 Docker 이미지가 Docker Hub에 제공됨
- Docker 이미지에는 바이너리와 모든 wrapper script가 포함됨
- 예시
# Firefox version, Alpine Linux docker pull lwthiker/curl-impersonate:0.6-ff docker run --rm lwthiker/curl-impersonate:0.6-ff curl_ff109 https://www.wikipedia.org # Chrome version, Alpine Linux docker pull lwthiker/curl-impersonate:0.6-chrome docker run --rm lwthiker/curl-impersonate:0.6-chrome curl_chrome110 https://www.wikipedia.org - Archlinux 사용자는 AUR 패키지를 사용할 수 있음
- 사전 컴파일 패키지: curl-impersonate-bin, libcurl-impersonate-bin
- 소스 빌드 패키지: curl-impersonate-chrome, curl-impersonate-firefox
- Mac용 비공식 Homebrew formula는 Chrome 전용으로 제공됨
brew tap shakacode/brew brew install curl-impersonate
libcurl-impersonate 고급 사용
libcurl-impersonate.so는 명령줄curl-impersonate와 같은 변경 사항으로 컴파일된 libcurl임- 추가 API 함수가 있음
CURLcode curl_easy_impersonate(struct Curl_easy *data, const char * target, int default_headers); chrome116같은 target name으로 호출하면 wrapper script가 설정하던 옵션과 헤더를 내부에서 설정함default_headers가 0이면 내장 HTTP 헤더 목록을 설정하지 않음- 사용자가 일반
CURLOPT_HTTPHEADERlibcurl 옵션으로 직접 헤더를 제공해야 함
- 사용자가 일반
curl_easy_impersonate()는 여러 libcurl 옵션을 설정함CURLOPT_HTTP_VERSIONCURLOPT_SSLVERSION,CURLOPT_SSL_CIPHER_LIST,CURLOPT_SSL_EC_CURVES,CURLOPT_SSL_ENABLE_NPN,CURLOPT_SSL_ENABLE_ALPN- 프로젝트용 비표준 옵션인
CURLOPT_HTTPBASEHEADER - 프로젝트용 비표준 HTTP/2 옵션인
CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER,CURLOPT_HTTP2_NO_SERVER_PUSH - 프로젝트용 비표준 TLS 옵션인
CURLOPT_SSL_ENABLE_ALPS,CURLOPT_SSL_SIG_HASH_ALGS,CURLOPT_SSL_CERT_COMPRESSION,CURLOPT_SSL_ENABLE_TICKET - 프로젝트용 비표준 TLS 옵션인
CURLOPT_SSL_PERMUTE_EXTENSIONS
- 이후
curl_easy_setopt()으로 위 옵션 중 하나를 호출하면curl_easy_impersonate()가 설정한 값을 덮어씀
기존 libcurl 앱에 적용하기
- 애플리케이션이 이미
libcurl을 사용한다면 Linux에서LD_PRELOAD로 기존 라이브러리를 런타임에 교체할 수 있음 CURL_IMPERSONATE환경 변수를 설정해 자동 모방을 적용함LD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 my_appCURL_IMPERSONATE는 두 가지 효과가 있음curl_easy_init()으로 새 curl handle이 만들어질 때curl_easy_impersonate()가 자동 호출됨curl_easy_reset()이후에도curl_easy_impersonate()가 자동 호출됨
- HTTP 헤더를 정밀하게 제어해야 하면
CURL_IMPERSONATE_HEADERS=no로 내장 헤더 목록을 비활성화하고 직접 설정함LD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 CURL_IMPERSONATE_HEADERS=no my_app LD_PRELOAD방식은 curl 자체에는 작동하지 않음- curl 도구가 TLS 설정을 덮어쓰기 때문임
- curl에는 wrapper script를 사용해야 함
저장소 구성과 기여
- 저장소에는 두 개의 주요 폴더가 있음
- Firefox 디렉터리 예시는 다음 파일을 포함함
- Dockerfile: 모든 의존성과 함께
curl-impersonate를 빌드하는 데 사용 - curl_ff91esr, curl_ff95, curl_ff98: 올바른 플래그로 실행하는 wrapper script
- curl-impersonate.patch: curl이 Firefox와 같은 TLS 확장을 사용하게 만드는 주요 패치이며, libnghttp2와 libnss로 정적 컴파일되게 함
- Dockerfile: 모든 의존성과 함께
- tests/signatures는 모방 가능한 알려진 브라우저 signature의 YAML 데이터베이스임
- 실제 curl 패치는 upstream curl에서 fork된 별도 저장소에 유지됨
- Firefox 변경은 impersonate-firefox 브랜치에 있음
- Chrome 변경은 impersonate-chrome 브랜치에 있음