본문 바로가기
MegazoneCloud
← 전체 글
미분류

숨는 AI 봇과 검문소 네 개: AWS WAF로 어디까지 알아볼 수 있나 (2편)

사람인 척하는 AI를 알아보는 건에 대하여: 삐빅, 로봇이 아닙니다(?)

3줄 요약

  1. 이름표(User-Agent)만 바꿔도 AI 봇 과금을 무료로 통과하는 요청이 있었습니다.
  2. 지문 검사, 브라우저 시험, 자동 조종 확인까지 검문소를 세우자 숨을수록 더 안쪽에서 잡혔습니다(기사 페이지 기준).
  3. 직접 적은 목록 없이 AWS 판별만 켜도 대부분 잡혔고, 빈틈은 연결 규칙 한 줄로 메웠습니다. 판별은 AWS가, 어디에 걸지는 판매자가 정합니다.

작성 기준: 2026년 10월, 테스트넷 데모 환경. 방어 설정만 다루며, 우회 도구의 이름과 사용법은 적지 않았습니다.


들어가며: 1편에서 남은 빈틈

1편의 규칙 순서에는 빈틈이 있습니다.

우선순위규칙동작
0Bot Control아는 봇에 라벨만 붙임
1human-allow봇 라벨이 없으면 통과
10~Monetize-*봇이면 402

봇 라벨만 피하면 공짜입니다. 돈을 내기 싫은 로봇은 사람인 척할 겁니다. 그래서 직접 해 봤습니다.


1. 이름표를 바꿔 봤다

아는 봇 이름은 402, 처음 보는 이름은 무료로 통과했습니다.
User-Agent결과
curl, python-httpx, GPTBot, ClaudeBot402
aiohttp, My-Agent, Strands-Agent (처음 보는 이름)200 무료
curl·python-httpx + 크롬 User-Agent200 무료
진짜 브라우저200

User-Agent는 보내는 쪽이 마음대로 적는 값이라, 여기에만 기대면 막을 수 없습니다.


2. 지문 검사: 접속할 때의 악수

핵심: 이름표는 바꿔도, TLS로 접속하는 방식(악수)은 도구마다 그대로 남습니다.

요청JA4 지문WAF 판정
진짜 브라우저t13d1517h2_8daaf6152771_…통과
curl + 크롬 UAt13d4907h2_0d8feac7bc37_7395dae3b2f3통과 (사람으로 오인)
curl 기본t13d4907h2_0d8feac7bc37_7395dae3b2f3402
python-httpx + 크롬 UAt13d1712h1_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" } }
}
위장한 curl·httpx는 402. 하지만 마지막 줄처럼 악수까지 크롬처럼 흉내 낸 요청은 다시 통과했습니다.

한계도 분명했습니다.

  • 악수까지 브라우저처럼 흉내 내는 공개 라이브러리가 있습니다.
  • 도구 버전이 바뀌면 지문도 바뀝니다. 지문 목록을 직접 적는 규칙은 데모용입니다.

3. 숨을수록, 더 안쪽 검문소에서 잡힌다

Bot Control을 Targeted 등급으로 올리고 검문소를 늘렸습니다.

로봇마다 선이 어디서 멈추는지 보면 됩니다. 모두 실제 요청 결과입니다. ※ 로봇 그림은 AI(Gemini)로 생성한 이해를 돕기 위한 이미지입니다.
검문소무엇을 보나걸어 둔 곳걸린 요청
① 이름표 검사스스로 밝힌 이름기사·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-reader60초에 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로 바꿀 수 있습니다).

대부분 그대로 잡혔고, 빨간 글씨가 바뀐 부분입니다. ※ 로봇 그림은 AI(Gemini)로 생성한 이해를 돕기 위한 이미지입니다.

설정 기본값에서 빈틈이 둘 나왔고, 둘 다 보완해 다시 확인했습니다.

빈틈 ①: 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

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다