사람인 척하는 AI를 알아보는 건에 대하여: 삐빅, 로봇이 아닙니다(?)
3줄 요약
- 이름표(User-Agent)만 바꿔도 AI 봇 과금을 무료로 통과하는 요청이 있었습니다.
- 지문 검사, 브라우저 시험, 자동 조종 확인까지 검문소를 세우자 숨을수록 더 안쪽에서 잡혔습니다(기사 페이지 기준).
- 직접 적은 목록 없이 AWS 판별만 켜도 대부분 잡혔고, 빈틈은 연결 규칙 한 줄로 메웠습니다. 판별은 AWS가, 어디에 걸지는 판매자가 정합니다.
작성 기준: 2026년 10월, 테스트넷 데모 환경. 방어 설정만 다루며, 우회 도구의 이름과 사용법은 적지 않았습니다.
들어가며: 1편에서 남은 빈틈
1편의 규칙 순서에는 빈틈이 있습니다.
| 우선순위 | 규칙 | 동작 |
|---|---|---|
| 0 | Bot Control | 아는 봇에 라벨만 붙임 |
| 1 | human-allow | 봇 라벨이 없으면 통과 |
| 10~ | Monetize-* | 봇이면 402 |
봇 라벨만 피하면 공짜입니다. 돈을 내기 싫은 로봇은 사람인 척할 겁니다. 그래서 직접 해 봤습니다.
1. 이름표를 바꿔 봤다

| User-Agent | 결과 |
|---|---|
| curl, python-httpx, GPTBot, ClaudeBot | 402 |
aiohttp, My-Agent, Strands-Agent (처음 보는 이름) | 200 무료 |
| curl·python-httpx + 크롬 User-Agent | 200 무료 |
| 진짜 브라우저 | 200 |
User-Agent는 보내는 쪽이 마음대로 적는 값이라, 여기에만 기대면 막을 수 없습니다.
2. 지문 검사: 접속할 때의 악수
핵심: 이름표는 바꿔도, TLS로 접속하는 방식(악수)은 도구마다 그대로 남습니다.
| 요청 | JA4 지문 | WAF 판정 |
|---|---|---|
| 진짜 브라우저 | t13d1517h2_8daaf6152771_… | 통과 |
| curl + 크롬 UA | t13d4907h2_0d8feac7bc37_7395dae3b2f3 | 통과 (사람으로 오인) |
| curl 기본 | t13d4907h2_0d8feac7bc37_7395dae3b2f3 | 402 |
| python-httpx + 크롬 UA | t13d1712h1_ab0a1bf427ad_8e6e362c5eac | 통과 (사람으로 오인) |
- 크롬으로 위장한 curl의 JA4는 일반 curl과 똑같습니다.
- python-httpx는
h1(HTTP/1.1)로, 진짜 크롬은h2로 접속합니다.
JA4: TLS 연결을 시작할 때 보내는 정보로 만든 지문입니다(AWS 한국어 문서는 “JA4 지문”). AWS WAF는 2025년 3월부터 규칙 조건으로 쓸 수 있습니다.
“유료 경로 + curl·httpx의 JA4면 402” 규칙을 human-allow보다 앞에 넣었습니다. 동작은 차단이 아니라 Monetize라서, 위장한 봇도 돈을 내면 들어올 수 있습니다.
{
"Name": "Monetize-spoofed-browser-ja4",
"Statement": { "AndStatement": { "Statements": [
{ "ByteMatchStatement": { "FieldToMatch": { "UriPath": {} },
"PositionalConstraint": "STARTS_WITH", "SearchString": "/api/premium-article", … } },
{ "OrStatement": { "Statements": [
{ "ByteMatchStatement": {
"FieldToMatch": { "JA4Fingerprint": { "FallbackBehavior": "NO_MATCH" } },
"PositionalConstraint": "EXACTLY",
"SearchString": "t13d4907h2_0d8feac7bc37_7395dae3b2f3", … } },
{ "ByteMatchStatement": { … "t13d1712h1_ab0a1bf427ad_8e6e362c5eac" … } }
] } }
] } },
"Action": { "Monetize": { "PriceMultiplier": "1" } }
}

한계도 분명했습니다.
- 악수까지 브라우저처럼 흉내 내는 공개 라이브러리가 있습니다.
- 도구 버전이 바뀌면 지문도 바뀝니다. 지문 목록을 직접 적는 규칙은 데모용입니다.
3. 숨을수록, 더 안쪽 검문소에서 잡힌다
Bot Control을 Targeted 등급으로 올리고 검문소를 늘렸습니다.

| 검문소 | 무엇을 보나 | 걸어 둔 곳 | 걸린 요청 |
|---|---|---|---|
| ① 이름표 검사 | 스스로 밝힌 이름 | 기사·API | 봇 이름표 그대로 → 402 |
| ② 지문 검사 | 접속할 때의 악수(JA4) | API | 이름표만 크롬으로 → 402 |
| ③ 브라우저 시험 | 브라우저만 푸는 JavaScript 문제 → 통과 도장 | 기사 페이지 | 악수까지 흉내 → 멈춤 |
| ④ 자동 조종 확인 | 도장 받을 때 본 브라우저 흔적 | 기사 페이지 | 자동 조종 브라우저 → 402 |
같은 악수 흉내가 API 주소로 가면 이 설정에서는 아직 무료로 통과합니다(2절 마지막 결과). 이 빈틈은 5절의 연결 규칙 한 줄로 메웠습니다.
브라우저 시험(Challenge)은 리캡차와 다르다
- 사람은 아무것도 하지 않습니다. 브라우저가 과제를 풀고 통과 도장(
aws-waf-token)을 받습니다. 시크릿 창 첫 방문도 1초 안에 지나갔습니다. - 못 푸는 클라이언트는 시험 페이지(202)를 받고 멈춥니다. 다시 요청해도 같은 시험이라 가격표도 보지 못합니다.
- AWS는 Challenge를 HTML을 받는 GET 요청에 쓰라고 권장합니다. API 클라이언트는 시험을 받아도 풀 수 없기 때문입니다. 기사 페이지에는 WAF 스크립트(
challenge.js)를 넣었습니다.
자동 조종 확인
자동 조종 브라우저는 시험을 풉니다. 하지만 그 과정의 흔적으로 Bot Control 규칙 TGT_SignalAutomatedBrowser가 …:targeted:signal:automated_browser 라벨을 붙이고, 이 라벨이면 402를 주게 연결했습니다. 이름표를 크롬으로 바꿔도 잡혔습니다.
실수: IP로 판단하면 사람도 걸린다
처음에는 “같은 IP에서 도장 없는 요청이 많다” 라벨이면 바로 402를 줬습니다. 그러자 같은 IP의 사람이 첫 방문에서 402를 받았습니다. 그 라벨을 402 조건에서 빼고, 사람에게는 시험이 먼저 걸리게 고쳤습니다. IP를 여럿이 함께 쓰는 회사라면 IP 기준 판단은 조심하세요.
행동으로도 잡을 수 있습니다. “같은 IP가 1분에 10번 넘게 읽으면 402” 규칙도 시험했습니다(19번째부터 402). 빈도 규칙에는 Monetize를 못 써서 Count로 라벨 → 그 라벨이면 402 두 줄로 나눴습니다.
4. 실제 설정

| 검문소 | 규칙 | 조건 → 동작 |
|---|---|---|
| ① | AWSBotControl (Targeted, Count) | 판별하고 라벨만 붙임 |
| ② | Monetize-spoofed-browser-ja4 | 유료 API + curl·httpx JA4 → 402 |
| 화면 밖 | Count-fast-reader-rate → Monetize-fast-reader | 60초에 10번 초과 → 402 |
| ③ | Challenge-news-human | 기사 페이지 + 봇 라벨 없음 → 시험 |
| ④ | Monetize-automated-browser | 자동 조종 라벨 → 402 |
| ① | human-allow | 봇 라벨 없음 → 통과 |
| ① | Monetize-* | 경로만 봄 → 경로별 가격으로 402. human-allow 뒤에 있어 봇에게만 걸림(순서를 바꾸면 사람도 402) |
5. 지문 목록 없이, AWS 판별만 켜 두면?
직접 적은 규칙 다섯 개를 빼고, Bot Control Targeted의 규칙이 직접 동작하게 했습니다. 자동 조종 규칙의 동작은 기본 동작(CAPTCHA) 대신 Monetize로 바꿨습니다(AWS 규칙도 402로 바꿀 수 있습니다).

설정 기본값에서 빈틈이 둘 나왔고, 둘 다 보완해 다시 확인했습니다.
빈틈 ①: API 주소에는 시험이 걸리지 않는다
- 크롬 이름표를 단 요청이 API 주소에서는 무료로 통과했습니다. 이 설정에는 API에 거는 시험 규칙이 없었고, 있다 해도 API 클라이언트는 시험을 풀 수 없습니다.
- 보완: “API 주소 + 도장 없음 라벨이면 402” 한 줄. 사람의 브라우저는 기사 페이지에서 도장을 미리 받기 때문입니다.
- 결과: 크롬 이름표 curl → 402, 도장 있는 브라우저 → 200, 에이전트 결제 → 정상
{
"Name": "Monetize-api-token-absent",
"Statement": { "AndStatement": { "Statements": [
{ "ByteMatchStatement": { "FieldToMatch": { "UriPath": {} },
"PositionalConstraint": "STARTS_WITH", "SearchString": "/api/premium-article", … } },
{ "LabelMatchStatement": { "Scope": "LABEL", "Key": "awswaf:managed:token:absent" } }
] } },
"Action": { "Monetize": { "PriceMultiplier": "1" } }
}
human-allow보다 앞 순서에 둡니다.
빈틈 ②: 기본값은 돈 낼 봇까지 시험에 막는다
TGT_VolumetricIpTokenAbsent(기본 동작 Challenge)는 같은 IP에서 도장 없는 요청이 쌓이면 그 IP의 모든 요청에 시험을 겁니다. 돈을 내려던 요청도 첫 요청부터 202를 받아 가격표를 보지 못했습니다(약 5분 뒤 풀림).- 보완: 이 규칙을 Count로 바꾸고, “이 라벨 + API 아님 + 봇 이름표 없음 → 시험” 한 줄.
- 결과: 같은 IP에 라벨이 붙은 상태에서도 결제 402 → 200 정상. 악수 흉내는 기사에서 시험에 멈추고, API에서는 402.
판별은 AWS가, 어디에 걸지는 판매자가
| 커스텀 규칙 | 예 | 유지보수 |
|---|---|---|
| 목록형: 판별 정보를 직접 적음 | JA4 지문 목록 | 도구가 바뀌면 낡음 → AWS 판별로 대체 |
| 연결형: AWS 라벨을 주소·동작에 이어 줌 | API 도장 검사, 경로별 가격 | 거의 그대로 → 판매자만 정할 수 있음 |
이름을 밝힌 봇이라면 콘솔 ‘AI 트래픽 분석’에서 봇과 경로를 고르면 Monetize 규칙을 만들어 줍니다. 숨는 봇은 위처럼 규칙으로 다뤘습니다.
6. 사람처럼 화면을 조작하는 에이전트는?
진짜 브라우저로 사람처럼 조작하는 에이전트는 기술로 가르기 점점 어렵습니다. 대부분 사용자 한 명의 부탁을 대신하니, 막기보다 어떻게 대할지가 문제입니다.
- 신원을 밝히면 이득이 되게: 암호 서명으로 신원을 증명한 봇(Web Bot Auth)은 ‘검증된 봇’으로 분류되고, WAF는 검증 등급마다 동작과 가격을 따로 정할 수 있습니다. 서명은 User-Agent와 달리 위조하기 어렵습니다.
- 숨는 것보다 내는 게 싼 웹은 아직 목표입니다. 지금은 숨는 비용이 거의 들지 않고, 결제에는 수수료와 몇 초가 듭니다.
마치며
이름표를 바꾼 봇은 지문으로, 악수까지 흉내 낸 봇은 브라우저 시험으로, 자동 조종하는 봇은 흔적으로 잡고, 그래도 못 잡는 봇은 신원을 밝히고 오게 만든다.
판별은 AWS가 대부분 해 줬습니다. 판매자의 일은 그 판별을 어디에 어떤 동작으로 걸지 정하고, 켜기 전에 실제 요청으로 확인하는 것이었습니다. 기본값이 돈 낼 봇까지 막는다는 것도 직접 보내 보고서야 알았습니다.

One Comment