전체 글

언블로커 플래그 내부: `unblocker: true`의 실제 동작 방식

단일 플래그로 실제 브라우저 헤더, 일치하는 연결 서명, gzip 및 brotli 자동 압축 해제 등 세 가지 기능을 활성화합니다. 이 기능의 동작 방식과 이유를 설명합니다.

대부분의 스크레이퍼는 단일 헤더를 읽기도 전에 실패합니다.

서버는 클라이언트가 네트워크에 전송하는 연결 수준 서명을 확인하고, 실제 브라우저인지 브라우저인 척하는 클라이언트 라이브러리인지 판단합니다. Python requests, Go의 net/http, 일반적인 curl은 모두 연결을 시도하는 순간 고유한 지문을 전달합니다. 보안을 중시하는 사이트(Datadome, Akamai, Imperva, Cloudflare의 관리형 서비스)는 User-Agent 문자열을 확인하기도 전에 연결을 끊거나 챌린지 페이지를 제공합니다.

이것이 FourA에서 unblocker: true이 해결하는 문제입니다. 지난달 당사는 이 기능이 안정적으로 작동하도록 구성 요소를 고정했습니다.

새로운 기능

unblocker: true은 모든 /api/single 호출에 사용되는 단일 플래그입니다. 이 기능을 켜면 브라우저 헤더 세트 주입, 실제 브라우저가 전송하는 것과 일치하는 전송 방식을 통한 요청 전달, 서버가 반환하는 모든 데이터(gzip, brotli, deflate) 압축 해제 등 세 가지 작업이 수행됩니다. 처음 두 가지는 베타 버전부터 제공되었습니다. 세 번째 기능(brotli 자동 압축 해제)은 3월 25일에 출시되었으며, 헤더와 전송 방식을 동기화하기 위한 버전 고정 작업은 다음 날 적용되었습니다.

작동 방식

요청의 모습은 다음과 같습니다.

curl -X POST "https://api.foura.ai/api/single" \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "url": "https://example.com/products",
    "method": "GET",
    "unblocker": true
  }'

내부적으로 세 개의 계층이 실행됩니다.

헤더 주입. User-Agent, Sec-Ch-Ua, Sec-Ch-Ua-Platform, Sec-Fetch-Site, Sec-Fetch-Mode, Sec-Fetch-Dest, Accept, Accept-Language, Accept-Encoding 등 전체 브라우저 헤더 번들을 설정합니다. 순서가 중요합니다. 실제 브라우저는 특정 순서로 이를 내보내며 감지 라이브러리는 이를 확인합니다.

연결 서명. 당사의 전송 방식은 최신 브라우저 세션의 바이트 수준 형태(동일한 확장 순서, 동일한 암호 선호도, 동일한 핸드셰이크 특성)와 일치합니다. 표준 curl, Python requests, Go의 net/http는 보호된 인프라에서 수 밀리초 내에 기계적으로 차단되는 서명을 생성합니다.

자동 압축 해제. unblocker이 켜져 있으면 Accept-Encoding을 gzip, deflate, br로 설정하고 전송 계층에서 본문을 해제합니다. 디코딩된 문자열을 반환받습니다(returnBuffer: true를 전달하는 경우 Buffer 반환). 수동으로 brotli를 처리할 필요가 없으며, 사이트가 gzip 대신 deflate를 선택할 때 헤더와 본문이 불일치하는 문제도 없습니다.

버전 고정이 중요한 이유

연결 서명은 버전에 종속됩니다. 브라우저의 이번 달 네트워크 수준 세부 정보는 지난달과 다르며, 지문 분석을 철저히 하는 사이트는 이러한 변화를 알아챕니다. 당사는 헤더, 네비게이터 객체, 연결 서명이 모두 동일한 브라우저 버전을 보고하도록 변동 요소를 함께 고정합니다.

이것이 번거롭게 들린다면 실제로 그렇습니다. 지난 3월 모노레포 마이그레이션 중에 한 요소가 자동 업데이트되고 나머지가 동기화되지 않아 불일치 문제가 발생했습니다. 해결책은 두 가지 커밋이었습니다. 변동 요소를 고정하고 패키지 관리자가 모든 것을 맞춰줄 것이라고 절대 믿지 않는 것입니다.

영향

지문 분석이 엄격한 타겟(금융, 여행, 보호된 전자상거래)을 대상으로 한 내부 테스트에서 unblocker: falseunblocker: true의 차이는 챌린지 페이지와 200 응답의 차이입니다. 관리형 Cloudflare에 일반 curl을 사용하면 첫 번째 시도에서 403 오류가 발생합니다. unblocker: true를 사용하는 동일한 URL은 연결이 네트워크 수준에서 브라우저 세션처럼 보이기 때문에 통과합니다.

하지만 지문을 분석하지 않는 사이트(대부분의 공개 API, 오래된 CMS 템플릿, IP 속도 제한으로만 제어되는 모든 항목)의 경우 unblocker을 꺼두어도 무방하며 협상 시간을 몇 밀리초 절약할 수 있습니다. 필요한 곳에만 사용하십시오.

고급 사용자를 위한 정보

알아둘 만한 몇 가지 패턴이 있습니다.

타겟이 IP 평판도 확인하는 경우 unblocker을 주거용 프록시와 함께 사용하십시오. 데이터센터 IP와 완벽한 연결 서명을 함께 사용하더라도 사이트가 블랙리스트에 올린 ASN에서는 차단됩니다. 당사의 프록시 엔드포인트(/api/proxy)는 타겟 도메인에 따라 회전하므로, 일반적으로 요청에 "proxy": "residential"를 추가하는 것으로 충분합니다.

브라우저 여부를 확인하지 않는 JSON API를 호출할 때는 unblocker을 건너뛰십시오. 추가 헤더는 프로그램 기반 클라이언트를 기대하는 API(예: 자체 마이크로서비스를 호출하는 백엔드)에 오히려 의심스럽게 보일 수 있습니다.

사이트가 JavaScript 안티봇(Turnstile 대화형 챌린지, 가장 엄격한 수준의 PerimeterX, 휴리스틱이 켜진 Akamai Bot Manager)을 실행하는 경우 unblocker만으로는 충분하지 않습니다. 전체 브라우저에서 챌린지를 실행하는 브라우저 엔드포인트가 필요합니다. 이는 크레딧 가격이 다른 별도의 제품이며 Browser Tasks: How to Scrape JavaScript-Heavy Sites에서 자세히 설명했습니다.

또한 unblockervalidate 블록과 결합하여 기술적으로는 200을 반환하지만 챌린지 페이지가 포함된 응답을 거부할 수 있습니다.

{
  "url": "https://example.com/products",
  "method": "GET",
  "unblocker": true,
  "validate": {
    "data": { "fail": ["captcha", "Access Denied"] }
  }
}

이를 통해 무음 실패가 분류된 실패로 전환되며, 이는 dashboard의 성공률 추적에 중요합니다.

다음 단계

브라우저는 4주마다 새로운 안정화 버전을 출시합니다. 당사도 이에 맞춰 스택을 업데이트합니다. 사용자 측에서는 아무것도 변경할 필요가 없습니다. unblocker: true는 당사가 종단간 검증을 완료한 브라우저 버전을 계속 가리킵니다.

더 어려운 과제가 남아있습니다. HTTP/3 지문 분석은 이미 관리형 안티봇에 나타나고 있으며, QUIC 전송은 이전 전송 방식보다 일치시키기 더 까다롭습니다. 또한 정적 헤더 번들에서 벗어나 진정한 동적 에뮬레이션으로의 전환이 시작되고 있습니다. 보호된 사이트는 HTTP/2 프레임 순서를 확인하는 방향으로 이동했으며, "브라우저처럼 보이는 라이브러리"와 "실제 브라우저" 사이의 격차는 양쪽 모두에서 줄어들 것입니다. 기능이 출시되면 이와 관련하여 다시 설명하겠습니다.