실무에서 봇 방어를 붙여본 사람이라면 다들 겪는 순간이 있다. 로그인·가입·결제 지점에 CAPTCHA나 Turnstile을 걸어놨는데, 어느 날 로그가 이상해진다. Challenge는 다 통과했는데 계정이 수천 개 만들어지고, Twilio SMS 비용이 폭발하고, 크리덴셜 스터핑이 조용히 성공한다. "분명히 검증 지점은 다 막았는데 왜?" 이 질문에 대한 Cloudflare의 답이 Precursor다.
1. 도입: 왜 지금 화제이고 어떤 문제를 푸는가
기존 봇 방어의 근본 전제는 "특정 지점에서 한 번 검증하면 통과"였다. 로그인 폼에 CAPTCHA, 가입 폼에 Turnstile, 결제 직전에 3DS. 이걸 나는 실무에서 "체크포인트 방식"이라고 부른다. 문제는 요즘 자동화가 이 체크포인트를 아주 잘 넘는다는 거다.
왜 넘는가? 현대 봇은 더 이상 curl로 요청 날리는 스크립트가 아니다. Playwright나 Puppeteer로 진짜 Chromium을 띄우고, 실제 JavaScript를 실행하고, 실제 브라우저 지문을 가진다. brightdata, zenrows 같은 상용 서비스는 수억 개 가정용 IP에 사람 같은 브라우저 지문까지 얹어 판다. 그래서 "짧은 순간"의 스냅샷만 보면 봇과 사람을 구분할 수 없다.
Cloudflare의 관점 전환이 여기서 나온다. 원문 요약을 그대로 옮기면 이렇다:
현대 자동화는 JavaScript를 실행하고 실제 브라우저 환경을 사용하며 개별 CAPTCHA도 통과할 수 있어, 짧은 구간에서는 정상 사용자처럼 보일 수 있음. 세션 전체에서 일관된 인간 행동을 재현하기는 더 어려우며, Precursor는 이러한 행동의 연속성을 사기와 악용 탐지 신호로 활용함.
핵심은 "짧은 순간은 흉내 낼 수 있어도, 세션 전체를 일관되게 흉내 내긴 어렵다"는 통찰이다. Cloudflare는 하루 1조 건 이상 요청을 처리하고 웹의 20% 이상을 커버한다고 하니, 이 규모에서 세션 단위 행동 데이터를 모으는 건 확실히 그들만 할 수 있는 접근이긴 하다.
2. 핵심: 동작 원리를 예시로
2-1. 전체 세션을 연속 스트림으로 본다
Precursor의 아키텍처를 단계별로 뜯어보면 이렇다. (아래는 원문 발췌 기반으로 정리한 흐름이다.)
- 클라이언트 주입: Precursor를 켜면 Cloudflare 네트워크를 통과하는 사이트의 HTML 응답에 경량 스크립트가 자동으로 삽입된다. 별도 설정, 별도 네트워크 연결, 제3자 임베딩 불필요. 번들은 작고 난독화돼 있으며 응답마다 동적으로 조립된다.
- 신호 수집: 삽입된 스크립트가 이벤트 리스너로 포인터 이동, 키보드 활동, 포커스 변화, 페이지 표시 상태(visibilitychange)를 잡는다. 이걸 압축 형식으로 직렬화해 메모리에 버퍼링한다.
- 전송: 버퍼가 정기적으로 엣지 평가 계층으로 올라간다.
- 엣지 평가: 엣지 서버가 페이로드를 역직렬화하고, 디스패처가 여러 평가기(evaluator)를 돌린다. 각 평가기는 필요한 스트림을 읽고 공유 탐지 레지스트리에 신호를 등록한다.
- 세션 누적: 데이터가 세션 범위로 누적되므로, 봇이 페이지를 새로고침하거나 Challenge부터 다시 시작해도 행동 서명을 초기화할 수 없다.
5번이 개인적으로 가장 영리하다고 본다. 체크포인트 방식의 약점은 "봇이 검증 지점만 통과하면 그 뒤론 자유"라는 건데, 세션 범위로 누적하면 리셋 자체가 안 된다.
2-2. 왜 마우스 움직임이 신호가 되는가
여기가 재밌는 부분이다. 봇 개발자들은 마우스 경로에 가우시안 노이즈나 균일한 랜덤 지연을 넣어 "자연스럽게" 만든다. 하지만 진짜 사람의 움직임에는 단순 노이즈로는 재현 안 되는 물리·인지적 제약이 있다:
- 손목 회전축: 손목 가동 범위와 팔뚝 회전 때문에 마우스가 흔히 호(arc) 형태로 움직인다. 직선이 아니다.
- 인지 부하: 체크박스를 본 뒤 클릭하기까지 측정 가능한 지연이 있다.
- 생리적 손떨림: 안정된 손에서도 특정 주파수의 미세 진동이 나타난다.
반면 자동화는 직선 보간이나 수학적으로 이상적인 Bézier 곡선을 쓰고, 사람이 못 낼 정밀도로 클릭한다. 예로 든 자동화 라이브러리는 "마우스를 완전한 직선으로 움직이고, 항상 원점으로 돌아가며, 같은 속도로 반응"한다. 개별 클릭은 그럴듯해도 세션 전체에서는 이 패턴 차이가 드러난다.
실무자 입장에서 이 발상을 코드로 직관화해보자. 아래는 Precursor 내부 구현이 아니라, "직선 vs 사람 곡선"의 차이를 감 잡기 위한 개념 데모다. 실제 판별 로직과는 무관하다.
# mouse_signal_demo.py — 직선 이동과 사람 유사 경로의 곡률/속도 분산 비교 (개념 데모)
import numpy as np
def path_features(points):
p = np.array(points, dtype=float)
deltas = np.diff(p, axis=0)
speeds = np.linalg.norm(deltas, axis=1)
# 방향 변화(곡률 대용): 연속 벡터의 각도 차
angles = np.arctan2(deltas[:,1], deltas[:,0])
turn = np.abs(np.diff(angles))
return {
"speed_std": round(float(np.std(speeds)), 3),
"turn_mean": round(float(np.mean(turn)), 4),
}
# 봇: 직선, 등속
bot = [(x, x) for x in range(0, 100, 5)]
# 사람: 호를 그리며 속도가 들쭉날쭉 + 목표 초과 후 보정
rng = np.random.default_rng(42)
t = np.linspace(0, 1, 20)
human = [(100*t[i] + rng.normal(0, 1.5),
40*np.sin(t[i]*np.pi) + rng.normal(0, 1.2)) for i in range(len(t))]
print("bot :", path_features(bot))
print("human :", path_features(human))
실행 결과:
$ python3 mouse_signal_demo.py
bot : {'speed_std': 0.0, 'turn_mean': 0.0}
human : {'speed_std': 1.834, 'turn_mean': 0.2216}
봇은 속도 분산도 0, 방향 변화도 0으로 딱 떨어진다. 이 speed_std와 turn_mean이 정확히 0에 수렴한다는 것 자체가 강력한 이상 신호다. Precursor는 이런 걸 세션 전체에 걸쳐, 여러 상호작용에 걸쳐 본다는 게 요점이다. (다만 원문에 "마우스 움직임은 여러 신호 중 하나일 뿐"이라고 세 번 강조돼 있다는 점은 꼭 기억하자. 마우스 하나로 판정하지 않는다.)
2-3. 교차 검증 — 단일 이벤트에 의존하지 않는다
Precursor가 단순 곡선 분석과 다른 지점은 서로 다른 신호를 교차 검증한다는 거다. 원문에서 든 예:
- 포인터 활동이 페이지가 실제 표시된 시간과 일치하는가? (탭이 백그라운드인데 마우스가 움직이면 이상)
- 텍스트 필드에 포커스가 있을 때만 키보드 이벤트가 발생하는가? (포커스 없는데 타이핑 이벤트가 오면 이상)
이 상관관계 검증은 봇 개발자를 정말 괴롭힌다. 마우스만 흉내 내면 되던 게, 이제 "포커스-키보드-포인터-visibility"를 전부 일관되게 조율해야 하기 때문이다.
3. 실무 관점: 도입 시 고려사항, 트레이드오프, 흔한 함정
3-1. 어떻게 켜는가
공식 발표 기준으로 Precursor는 Enterprise Bot Management 기능에 포함되며, 대시보드에서 영역(zone)별로 켤 수 있고 올해 말 GA 전까지 무료다. 활성화 방식은 두 가지:
- 마찰 낮은 모드: 백그라운드에서 행동만 관찰. 애플리케이션 변경 불필요.
- 강제 검증 모드: 검증된 세션이 없으면 Challenge를 강제.
대부분의 팀은 무조건 관찰 모드부터 시작하는 게 맞다. 바로 Challenge 강제로 켜면 오탐(false positive)이 정상 사용자를 때리는 순간 CS 티켓이 쏟아진다. Cloudflare는 Precursor 데이터를 기존 봇 점수·Challenge 판단·보안 규칙으로 직접 전달한다고 하니, 며칠 관찰 모드로 돌리며 Security Analytics의 세션 기반 뷰에서 "우리 사이트 정상 세션이 어떻게 생겼는지" 먼저 파악한 뒤 임계값을 조이는 걸 권한다.
3-2. 관찰 모드로 시작하는 이유 — 실제 검증 명령
Precursor 자체는 대시보드 기반이지만, 활성화 후 봇 점수가 실제 요청에 반영되는지, HTML에 스크립트가 주입되는지 정도는 커맨드라인으로 빠르게 확인할 수 있다. 아래는 Cloudflare 봇 점수를 응답 헤더로 노출하도록 Transform Rule을 걸어둔 뒤 확인하는 예다.
# Cloudflare 봇 점수를 응답 헤더로 노출하는 Transform Rule을 걸었다고 가정
# (대시보드 > Rules > Transform Rules > Modify Response Header:
# x-bot-score = cf.bot_management.score)
$ curl -sI https://example.com/ | grep -i -E "cf-ray|x-bot-score|server"
server: cloudflare
cf-ray: 8a3f1c2d9e7b4a21-ICN
x-bot-score: 1
x-bot-score가 1에 가까우면 봇 확률이 높다는 뜻이다(Cloudflare 봇 점수는 1=봇, 99=사람에 가까움). 정상 브라우저로 접근했는데 점수가 낮게 나온다면 오탐 소지가 있는 것이고, 스크립트로 접근했는데 높게 나온다면 탐지가 작동 중이라는 신호다.
HTML에 경량 스크립트가 주입되는지도 눈으로 확인할 수 있다:
$ curl -s https://example.com/ | grep -o 'cloudflare[^"]*\.js' | head
cdn-cgi/challenge-platform/scripts/jsd/main.js
(주입되는 스크립트 경로/이름은 계정·설정·시점에 따라 달라진다. 정확한 식별자는 공식 문서 확인 필요. 위 경로는 예시다.)
3-3. 흔한 함정 — 접근성 사용자와 오탐
이게 가장 조심해야 할 지점이다. HN 토론에서 반복해서 나온 우려인데, 행동 기반 탐지는 비전형적 입력 사용자를 봇으로 오인할 수 있다:
- ThinkPad 트랙포인트, 터치스크린 등 비전통적 입력 장치
- 시선 추적(eye-tracking) 등 보조 기술을 쓰는 장애인
- 키보드 전용 사용자 (마우스 신호가 아예 없음)
- 한 손만 쓰는 사용자
실제로 Cloudflare 오탐 때문에 페이지가 무한 로딩에 걸리는 상황을 겪은 사람들의 증언이 토론에 있다. Starlink 같은 위성 회선에서 더 자주 차단당한다는 얘기도 나온다. 이럴 때 사용자가 마주치는 전형적인 화면은 이런 식이다:
example.com에 연결 중...
Checking your browser before accessing example.com.
This process is automatic. Your browser will redirect to your
requested content shortly. Please allow up to 5 seconds...
Ray ID: 8a3f1c2d9e7b4a21
Performance & security by Cloudflare
이 화면에서 계속 redirect만 되고 콘텐츠로 안 넘어가는 상태가 오탐의 대표 증상이다. 로그를 파보면 브라우저 콘솔에서 이런 에러를 보게 되는 경우도 있다:
Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'self'".
Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a
nonce ('nonce-...') is required to enable inline execution.
이건 Precursor가 주입하는 인라인 스크립트가 사이트의 CSP(Content Security Policy)에 걸려 실행 자체가 막히는 상황이다. 이렇게 되면 신호 수집이 안 돼서 오히려 "행동 데이터 없음 → 봇 의심"으로 이어지는 최악의 루프가 생길 수 있다. Cloudflare 관리형 스크립트 주입을 쓴다면 CSP에 nonce나 해당 도메인 허용을 반영했는지 반드시 확인해야 한다. (구체적 CSP 설정 방법은 공식 문서 확인 필요.)
3-4. 트레이드오프 정리
| 구분 | 장점 | 비용/리스크 |
|---|---|---|
| 탐지 정밀도 | 세션 전체를 보므로 체크포인트 회피 봇을 잡음 | 접근성 사용자 오탐 위험 |
| 운영 부담 | 앱 코드 변경 없이 엣지에서 처리 | CSP·확장프로그램·비전통 입력과의 충돌 |
| 공격자 비용 | 봇이 세션 전체를 흉내 내야 해 비용 급증 | 정교한 흔들림 합성으로 우회 시도 가능 |
| 프라이버시 | 실제 키값 대신 타이밍·리듬만, 계정과 미연결 | 손목 상태·주손·모국어 등 추정 가능성 논란 |
프라이버시 설계는 원문 기준으로 나쁘지 않다. 실제 누른 키가 아니라 타이밍·리듬만 수집하고, 사용자 계정·로그인 신원·영구 프로필과 연결하지 않으며, 고객 대시보드에 직접 노출도 안 한다고 명시돼 있다. 다만 HN 토론에서는 "그 신호로 이론상 주손·대략 나이·모국어·부상 여부까지 추정 가능하지 않냐"는 우려가 나왔다. 이건 Cloudflare가 실제로 그렇게 쓴다는 게 아니라 가능성에 대한 지적이니 구분해서 받아들이자.
3-5. 대안
같은 시장(행동 기반 봇/에이전트 탐지)에는 이미 여러 제품이 있다. 토론에서 언급된 것들: DataDome, Kasada, HUMAN, Castle, Fingerprint, Foil, Google Cloud Fraud Defense(사실상 reCAPTCHA 후속), Darwinium 등. hCaptcha는 6년 전에 유사 기능(같은 사람의 다중 계정·다중 카드 시도 탐지)을 구현했다는 증언도 있고, reCAPTCHA v3도 "백그라운드에서 조용히 감시"가 핵심 셀링 포인트였다. 즉 Precursor의 발상 자체가 완전히 새로운 건 아니다. 다만 Cloudflare의 네트워크 규모(웹 20%+, 일 1조 요청)와 앱 변경 없는 엣지 통합이 차별점이다.
4. 정리
한 줄 요약: Precursor는 "검증 지점 한 번 통과"라는 체크포인트 패러다임을 버리고, 세션 전체의 행동 연속성을 엣지에서 스트림으로 분석해 봇 흉내의 비용을 폭발적으로 올리는 시스템이다.
누가 언제 써야 하나:
- 이미 Cloudflare Bot Management / Turnstile을 쓰는데, 검증 지점은 다 통과하는데 크리덴셜 스터핑·가짜 계정·무료체험 악용·스크래핑이 새는 팀 → 켜볼 가치 충분하다. GA 전까지 무료다.
- 단, 접근성이 중요한 서비스(공공·금융·의료)는 반드시 관찰 모드로 오래 돌리고, 세션 분석으로 정상 사용자 분포를 확인한 뒤에 Challenge 강제를 켜라. 키보드 전용·보조기술 사용자를 봇으로 때리면 그건 탐지 실패가 아니라 서비스 사고다.
- CSP를 빡세게 걸어둔 사이트는 인라인 스크립트 정책부터 점검하고 도입하라.
봇 방어는 결국 공격자 적응과 방어자 대응이 끝없이 반복되는 적대적 경쟁이다. 흔들림 합성으로 우회하려는 시도가 나올 거고, Cloudflare는 더 많은 실제 데이터로 역흔들림 대응을 할 거다. 우리 실무자 입장에서 중요한 건, 이 무기를 켜는 순간 정상 사용자에게 새로운 마찰을 얹을 수 있다는 걸 잊지 않는 것이다. 관찰부터, 그다음 강제. 순서 지키자.
참고 자료
'Tech_News' 카테고리의 다른 글
| lobste.rs는 왜 MariaDB를 버리고 SQLite로 갔나 — DB를 줄여서 얻은 것들 (0) | 2026.07.15 |
|---|---|
| Claude Code가 프롬프트도 읽기 전에 33k 토큰을 태우는 이유 — OpenCode와 실측 비교 (0) | 2026.07.14 |
| PgBouncer를 4배 빠르게: so_reuseport 기반 멀티프로세스 풀러 아키텍처 뜯어보기 (1) | 2026.07.13 |
| ingress-nginx 은퇴, 이제 뭘 써야 하나: Gateway API·Contour·Traefik 실전 마이그레이션 (0) | 2026.07.11 |
| TypeScript 7이 Go로 재작성된 진짜 이유와, 우리 빌드 파이프라인에 미칠 영향 (0) | 2026.07.10 |
