3P by GN⁺ | ★ favorite | 댓글 1개
  • 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_PRELOADCURL_IMPERSONATE로 기존 libcurl` 사용 앱에 적용할 수 있음

curl-impersonate가 해결하는 문제

  • curl-impersonatecurl을 특별히 빌드해 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 fingerprintingHTTP/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가 있음
    • 예: chrome116 target은 curl_chrome116 wrapper script로 실행함
    • 예: ff117 target은 curl_ff117 wrapper 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
  • 시스템에 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 패키지를 사용할 수 있음
  • 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_HTTPHEADER libcurl 옵션으로 직접 헤더를 제공해야 함
  • curl_easy_impersonate()는 여러 libcurl 옵션을 설정함
    • CURLOPT_HTTP_VERSION
    • CURLOPT_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_app
    
  • CURL_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를 사용해야 함

저장소 구성과 기여

  • 저장소에는 두 개의 주요 폴더가 있음
    • chrome: Chrome 버전 curl-impersonate 빌드용 스크립트와 패치
    • firefox: Firefox 버전 curl-impersonate 빌드용 스크립트와 패치
  • Firefox 디렉터리 예시는 다음 파일을 포함함
    • Dockerfile: 모든 의존성과 함께 curl-impersonate를 빌드하는 데 사용
    • curl_ff91esr, curl_ff95, curl_ff98: 올바른 플래그로 실행하는 wrapper script
    • curl-impersonate.patch: curl이 Firefox와 같은 TLS 확장을 사용하게 만드는 주요 패치이며, libnghttp2와 libnss로 정적 컴파일되게 함
  • tests/signatures는 모방 가능한 알려진 브라우저 signature의 YAML 데이터베이스임
  • 실제 curl 패치는 upstream curl에서 fork된 별도 저장소에 유지됨

댓글과 토론

Hacker News 의견들
  • 원본보다 개선점이 꽤 많고 활발히 유지보수되는 포크가 있음: https://github.com/lexiforest/curl-impersonate
    Python 사용자를 위한 바인딩도 있음: https://github.com/lexiforest/curl_cffi
    • “curl을 브라우저처럼 보이게 하는” 프로그램이 봇 탐지 우회 서비스의 후원을 받는 건 어찌 보면 자연스러움
    • 이것을 Python requests 라이브러리와 완전히 통합하는 모듈도 있음: https://github.com/el1s7/curl-adapter
    • “인증된” 3대 웹 브라우저 중 하나처럼 보이는 단순 요청 하나를 만들려고 목 돌리는 속도보다 빨리 바뀌는 “고급” 기술을 따라가야 한다니 이상함
      역설적으로 그 요청은 인증된 브라우저보다 서버 부담이 덜할 텐데, 90년대에 경고받던 디스토피아가 이런 건가 싶음. 착한 오픈소스/해커 커뮤니티 기여자로 포지셔닝하면서도 이 상황을 만든 한 회사를 여기서 누가 이름 댈 수 있을지 궁금함
  • 앞으로 Ladybird가 힘을 얻었으면 좋겠음. 현재 네트워킹에는 정식 cURL을 쓰고 있는데, 이 방식은 난관이 있을 수 있음
    예를 들어 cURL은 아직 몇 가지 제약이 있고, h2 위에서 WebSocket을 처리하지 못하는 것으로 앎. 반면 성장하는 브라우저 엔진이 생기면 정상 트래픽도 기본 cURL과 같은 지문을 갖게 되어 이 지문 채취 경로가 사라질 수도 있음
    • Ladybird의 cURL 사용이, 예컨대 언급한 h2 위 WebSocket 같은 부분에서 cURL 자체를 개선하는 계기가 되면 좋겠음
      실제 브라우저 작업 흐름 기준으로 cURL에 어떤 기능이 빠져 있는지 확인하는 좋은 시험대이기도 함
    • “성장하는 브라우저 엔진이 있으면 이 지문 채취 경로가 사라질 수도 있다”는 부분은, CloudFlare 등에서 본 바로는 정반대에 가까움
      지난 몇 달 사이 지문 채취와 구현 정의 동작을 “악용”하는 수준이 크게 늘었고, 아마 다른 브라우저 엔진을 죽이려는 시도로 보임. 기존 강자들은 경쟁을 전혀 좋아하지 않음
      상대편은 이를 “AI 봇의 DDoS”로 포장하려 하지만, 그중 얼마나 자기들이 만든 일인지 의문임
    • 이 사람들과 이야기했을 때 [0], 서명을 만드는 여러 기묘한 구현 차이를 다뤘고, 사용자 공간 앱이 제어할 수 없는 TCP 스택 요소까지 포함됐음
      이 curl은 마음에 들지만, 어떤 구성요소가 “따라잡기” 위해 속임수 역할을 맡으면 유지보수하기 어려운 “호환성” 짐이 쌓일까 걱정됨
      이상적으로는 그냥 “안녕, 나는 curl이야, 들여보내줘”라고 말할 수 있어야 함
      물론 문제는 복장 규정에 까다로운 서버에 있고, 그 문제는 또 변장하고 들어오는 사기꾼들 때문에 생기니 닭과 달걀처럼 순환적임
      [0] https://cybershow.uk/episodes.php?id=39
    • 이 덕분에 Ladybird가 ftp URL을 지원하게 되면 좋겠음
    • Ladybird는 현재 브라우저들과 경쟁할 자원이 없음. 홍보는 잘됐지만 Chromium 대비 장점이나 존재 이유가 부족함
      또 입증 가능하게 안전하지 않은 C++ 로 다시 설계됐다는 점에서 큰 보안 위험이 있음
  • 가장하려는 플랫폼에 맞춰 TTL 값을 맞추도록 IP_TTL도 설정했을까?
    그렇지 않다면 IP 계층에서도 어느 정도 지문 채취가 가능함. IP 계층의 TTL 값이 64 미만이면, 현대 Windows에서 실행 중이 아니거나 기본 TTL을 바꾼 현대 Windows라는 게 분명함. 현대 Windows의 패킷 기본 TTL은 128에서 시작하고, 대부분의 다른 플랫폼은 64에서 시작하기 때문임
    다른 플랫폼들도 인터넷 통신에 문제가 없으므로, 현대 Windows에서 온 IP 패킷은 원격 측에서 항상 64 이상, 아마 64보다 약간 큰 TTL로 보일 것임. 다만 IP 계층 지문 채취는 어렵지만 불가능하지는 않음
    • 낮은 수준의 TCP/IP 스택 접근을 제공하지 않는 PaaS/IaaS를 쓸 때만 어려움. 직접 서버를 운영한다면 온갖 TCP/IP 속성 지문 채취는 아주 쉬움
      https://en.wikipedia.org/wiki/TCP/IP_stack_fingerprinting
    • 수신 패킷의 TTL 값은 네트워크 상태에 따라 달라지지 않나? 서버에서 클라이언트의 원래 값을 복원할 수 있음?
    • TTL은 왜 증가가 아니라 감소하도록 설계됐을까? 일반적으로는 트래픽을 라우팅하는 쪽이 라우팅 여부와 방식을 결정한다고 기대하지 않나?
  • 잠깐, TLS 핸드셰이크가 다르게 보인다면, 웹 브라우저라고 주장하지만 실제로는 Python/PHP 스크립트인 트래픽을 nginx 수준에서 필터링할 수 있지 않을까?
    예를 들어 Chrome 사용자 에이전트를 쓰지만 실제 브라우저가 아닌 경우임. 악성 봇 트래픽의 대부분이 여기에 해당할 테니 그냥 막을 수 있으면 좋겠음
    • Cloudflare는 여러 TLS 핸드셰이크 매개변수의 해시인 JA3/JA4 TLS 지문을 사용함
      작동 방식은 https://github.com/FoxIO-LLC/ja4/blob/main/technical_details...에 자세히 나와 있고, Nginx 모듈도 제공함: https://github.com/FoxIO-LLC/ja4-nginx-module
    • 기본적으로 Cloudflare 같은 보안 업체들이 하는 일이 그거고, 거기에 더해 JavaScript 인터프리터/DOM을 확인하는 JavaScript 챌린지 같은 지문 채취를 더 많이 함
    • 가능하고, 실제 사이트들이 하고 있는데 정말 별로임. 신뢰성이 낮아서 최신 Windows의 최신 Chrome을 쓰지 않는 모두를 막아버림
      지금 실제로 공격받는 중이 아니라면 TLS 지문 허용목록을 쓰지 말아야 함
    • 글의 도구가 정확히 그런 차단을 피하려는 용도로 보임
  • 멋진 도구지만 클라이언트가 브라우저인지 아닌지는 중요하지 않아야 함. 현실에서 이런 도구가 필요하다는 게 씁쓸함
    • 약 6개월 전에 Internet Explorer를 요구하는 정부 경매 사이트에 들어간 적이 있음. 정말 Internet Explorer였고, 사이트도 활성 상태라 경매 데이터가 최신이었음
      Chrome에 사용자 에이전트 확장을 추가하고 IE로 바꿔 다시 시도하니 동작했고, 사이트의 모든 기능도 멀쩡했음. 그래서 씁쓸하면서도 짜증났음. 아마 그 정부 기관이 25년 전에 웹사이트를 만들고 그 뒤로 업데이트하지 않은 듯함
    • 우리가 승인한 소프트웨어를 써야만 사이트에 들어올 수 있음. 그 외는 전부 악성으로 간주함. 서류 보여주세요!
      이런 게이트키핑이 나도 씁쓸함. 이해한 바로는 직접 만든 맞춤 브라우저나 사용자 에이전트는 충분한 영향력, 돈, 사용자 수 같은 것이 생겨 그들을 설득할 수 있기 전까지 Cloudflare류 사이트에서 절대 동작하지 않을 것임
  • 이 도구는 레드팀 작업에서 작은 bash 스크립트와 GNU parallel을 조합해 쓰면 꽤 좋음
    범위로 지정된 주소 대역 안에서, 여러 이유로 제대로 된 브라우저에만 응답하거나 SNI 구성이 맞아야 응답하는 HTTPS 엔드포인트를 매핑할 때 유용했음. 헤더 위장용 -H 같은 일반 curl 옵션도 그대로 쓸 수 있음
  • 당시 Show HN 글: https://news.ycombinator.com/item?id=30378562
    • 그때인 2022년에는 Firefox 전용이었음
  • 이런 게 여기 올라올 때마다 늘 양가감정이 듦. 한편으로는 사람들 사이에 아직 약간의 반항심과 독립성이 살아 있음을 알리는 게 좋음
    다른 한편으로는 “자유는 불안정”으로 취급되는 다른 프로젝트들처럼, 원치 않는 관심을 끌면 여기에 의존하는 사람들에게 상황이 더 나빠질 수도 있음. 브라우저 작성은 어렵고, 기존 강자들은 계속 더 어렵게 만들고 있음
    • 브라우저 개발자들이 브라우저의 지문 식별 가능성을 바라는 것처럼 들리지만, 그건 서로 다른 사람들이 서로 다르게 구현하다 보니 저절로 생기는 일에 가까움
      이걸 반항심의 문제로 보지는 않음. 소프트웨어가 지문 식별 가능해지는 것은 개인정보 보호와 소프트웨어 다양성을 갉아먹음
  • 웹사이트가 봇을 괜찮게 여기면 허용하고, 싫어하면 사용자 에이전트를 막던 단순한 시절이 조금 그리움
    • 그때는 웹사이트가 지금처럼 자원을 많이 쓰지 않았음. 봇에 대한 부정적 반발은 웹 경험에 대한 기대가 지나치게 무거워진 데서 나온 부작용에 가깝다고 봄
  • 패치 3개와 셸 래퍼뿐이라면 Daniel이 코딩에 나설 만함. 개인적으로는 이게 반드시 메인라인 curl에 들어가야 한다고 봄