---
title: 사기 방지 — 광고 사기
---
# 광고 사기
이것은 **시나리오 가이드**입니다. 핵심 연동은 변하지 않습니다 — [Web SDK](../web-sdk)를
로드하고, [판정](../verdict-reference)을 읽고, [Data API](../data-api)에서 데이터를
가져오는 것이 그 페이지들에서 설명한 것과 정확히 같습니다. 이 페이지는 광고 사기
시나리오에만 필요한 몇 가지 특이 사항을 추가합니다.
광고 사기 방지는 봇과 무효 트래픽을 유료 캠페인에서 걸러내어, 광고주와 광고 플랫폼이
가짜 볼륨이 아니라 진짜 클릭과 노출에 대해 비용을 지불하도록 합니다. 다른 사용 사례와
다른 점은 **제3자가 방문자를 전달한다**는 것이며, 양측이 독립적인 클릭별 사람 / 봇
결론으로 합의해야 한다는 것입니다.
## 소스에서의 실시간 차단
Fraud Prevention은 진입점에서 inline으로 실행되며 판정을 실시간으로 반환합니다 — 사후 보고서가 아닙니다. 방문자가 도착하는 페이지를 소유한 쪽은 그 판정을 즉시 활용할 수 있습니다:
- **광고주**는 랜딩 페이지에서 SDK를 실행합니다: 무효 트래픽은 도착하는 순간 감지되며 — 선택적으로 Challenge가 제시되고 — 퍼널에 진입하거나 리포팅을 왜곡하기 전에 차단됩니다.
- **트래픽 플랫폼**은 자체 pre-lander나 리다이렉트 페이지에서 SDK를 실행합니다: 클릭은 전달되기 *전에* 검증되므로, 봇은 입구에서 차단되어 전달된 트래픽이 되지 않습니다.
유일한 전제 조건은 SDK를 실행할 수 있는 페이지입니다: 판정에는 브라우저 신호가 필요하므로 순수 서버 사이드 302에는 신호를 수집할 곳이 없습니다 — 가벼운 중간 페이지를 추가하면 동일한 실시간 차단이 적용됩니다.
서명된 click token과 Data API는 그 위에 얹혀 양쪽의 대조를 수행합니다; 실시간 inline 차단이 주요 메커니즘이며, 사후 API 스크러빙은 중간 페이지가 없는 트래픽을 위한 폴백입니다.
## 두 역할
- **광고주(Advertiser)** — 방문자가 도착하는 랜딩 페이지를 소유합니다. 그 페이지에서
Web SDK를 실행해 도착하는 각 방문에 대한 판정을 받고, Data API에서 판정을 가져와
자신이 청구받은 내용을 대조합니다.
- **공급자(Provider, 트래픽 / 광고 소스)** — 클릭을 전달합니다. **자신이 보낸 것의
품질을 증명**해야 하므로, 클릭마다 클릭 토큰을 발급하고 이후 자신의 전달 보고서를
독립적인 판정과 조인합니다.
양측은 각자의 애플리케이션 자격 증명으로 [Data API](../data-api)에 인증하고 동일한
클릭별 판정을 읽으며, 바로 이 점이 서로의 원시 로그를 신뢰하지 않고도 대조할 수 있게
합니다.
## 엔드투엔드 흐름
아래 시퀀스는 한 번의 클릭이 공급자의 전달에서 출발해, 방문자와 광고주의 랜딩 페이지를
거쳐, 단일 판정에 이르고 — **두 계정이 이후 각자 자기 쪽에서 그것을 읽는** 과정을
보여줍니다.
```mermaid
sequenceDiagram
autonumber
participant P as 공급자(Provider / 계정 B)
participant V as 방문자(Visitor)
participant A as 광고주 랜딩 페이지 + SDK
participant F as 사기 방지 백엔드(Fraud Prevention API)
P->>P: click_token 서명(bot_kid + secret, pkid 포함)
P->>V: ?_ctk=click_token 이 붙은 링크 전달
V->>A: 클릭 → 광고주 페이지에 착지(URL이 _ctk 운반)
A->>A: SDK가 URL에서 _ctk 읽음
A->>F: 암호화된 POST /bot/verify
(app_key = 광고주, click_token = 공급자 서명)
F->>F: app_key → advertiser_app_id,
pkid 검증 → provider_app_id, 판정 도출,
양쪽 app_id를 담은 한 행 기록
F-->>A: verdict → onVerdict
A->>A: verdict.action 에 따라 처리(기록 / 차단 / 챌린지)
Note over P,F: 대조(계정 간)
A->>F: 광고주 App-Key 로 GET /v1/bot
F-->>A: 같은 행(advertiser_app_id 로 매칭)
P->>F: 공급자 App-Key 로 GET /v1/bot
F-->>P: 같은 행(provider_app_id 로 매칭)
```
결정적인 특성: **두 app_id가 같은 행에 있다는 것.** 광고주는 `advertiser_app_id`로
매칭하여 그것을 읽고, 공급자는 `provider_app_id`로 매칭하여 그것을 읽습니다 — 두 개의 서로
다른 계정이 각자 자신의 App-Key로 질의하며, 각자 같은 독립 판정에 착지합니다. 어느 쪽도
주어진 클릭에 무슨 일이 있었는지 합의하기 위해 상대의 원시 로그를 노출하거나 신뢰할 필요가
없습니다.
## 클릭 토큰
**클릭 토큰(click token)**은 공급자가 전달한 클릭을 그 방문이 궁극적으로 받는 판정과
묶어줍니다. 양 당사자가 정산의 기준으로 삼는 클릭별 식별자입니다. 다른 사기 방지 사용
사례에는 클릭 토큰이 필요 없습니다 — 그것 없이도 판정을 대조합니다.
흐름:
1. **발급** — 공급자는 방문자를 광고주의 랜딩 페이지로 라우팅할 때 서명된 클릭 토큰을
(클릭당 하나) 얻습니다.
2. **대상 URL에 실어 운반** — 발급된 토큰을 쿼리 파라미터로 랜딩 URL에 덧붙입니다.
```
https://advertiser.example/lp?click_token=ct_xxxxxxxx
```
3. **페이지에서 읽기** — [Web SDK](../web-sdk)가 URL에서 토큰을 자동으로 읽습니다. 다른
파라미터 이름을 사용한다면 `tokenParam`을 설정하세요.
```js
BotSignal.init({ appKey: 'YOUR_APP_KEY', tokenParam: 'click_token' });
```
4. **대조** — 이후 Data API로 그 클릭을 조회하여 공급자의 전달 보고서와 다시 조인합니다.
::: info
토큰은 당신에게 발급될 때 이미 서명되어 있습니다 — 당신은 그것을 **랜딩 URL까지 그대로
운반**하기만 하면 됩니다. 당신 쪽에서 서명하거나 계산할 것은 전혀 없습니다.
:::
### `tokenParam` 옵션
이 시나리오에서 Web SDK는 옵션 하나가 더 생깁니다.
| 옵션 | 타입 | 기본값 | 설명 |
| --- | --- | --- | --- |
| `tokenParam` | string | 자동 감지 | 공급자 클릭 토큰을 운반하는 URL 쿼리 파라미터의 이름(예: `click_token`). SDK가 페이지 URL에서 그것을 읽어 판정을 그 클릭에 묶습니다. 클릭 토큰을 사용하지 않으면 설정하지 마세요. |
## 대조 및 정산
방문이 클릭 토큰을 운반하기 시작하면, 양측은 동일한 독립 판정으로 정산합니다.
- **단일 클릭의 판정 가져오기** — 예를 들어 한 클릭에 대해 이의를 제기하거나 확인하기 위해:
```bash
GET /v1/bot/verdict?click_token=ct_xxx
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET
```
- **클릭별 행 내보내기** — 시간 범위를 가져와 각 행의 `click_token`을 자신의 클릭 로그와
다시 조인:
```bash
GET /v1/bot/export?from=2026-06-01&to=2026-06-30&format=csv
X-App-Key: YOUR_APP_KEY
X-App-Secret: YOUR_APP_SECRET
```
각 행은 방문의 `click_token`(존재하는 경우), 타임스탬프, 그리고 판정 필드(`is_bot`,
`score`, `level`, `action`)를 담습니다.
광고주는 표시된 클릭과 봇 클릭을 지불 대상에서 제외하고, 공급자는 같은 결론과 자신의 전달
보고서를 대조합니다. 양측이 **같은 독립 판정**을 읽기 때문에, 정산은 어느 쪽의 원시 로그에도
의존하지 않습니다.
## 보안 모델
`cid`와 클릭 토큰은 URL에 실려 이동하므로, 자격 증명이 아니라 식별자로 다루세요:
- **판정 읽기에는 App-Key + App-Secret이 필요합니다.** Data API는 모든 호출을 여러분의 app secret으로 인증하고(상수 시간 비교), 여러분의 앱이 advertiser 또는 provider인 행만 반환합니다. `cid`만으로는 아무것도 가져오지 못합니다 — URL에서 그것을 복사한 제3자는 secret이 없고 그 행의 당사자도 아닙니다.
- **provider는 서명으로 묶입니다.** 클릭 토큰은 provider의 secret으로 HMAC 서명되고 provider의 `pkid`를 담습니다. 위조할 수 없으며, 동일한 `cid`는 한 번만 청구할 수 있습니다(재전송 방지).
- **선택적으로 advertiser도 묶을 수 있습니다.** 대상 advertiser의 `app_key`를 `aud`로 토큰에 서명하세요. 그러면 검증 시 페이지의 app_key가 `aud`와 일치해야 하므로, advertiser A에게 발급된 토큰이 advertiser B의 판정을 귀속시킬 수 없습니다. `aud`를 생략하면 어떤 advertiser 페이지든 그 토큰을 받아들일 수 있습니다.
최종 효과: 어떤 `cid`의 판정은 그것에 묶인 두 앱 — SDK를 실행한 페이지의 advertiser와 토큰을 제시한 provider — 만 읽을 수 있으며, 각자 자신의 App-Secret으로 인증합니다.
## 다음 단계
- [Web SDK](../web-sdk) — 랜딩 페이지에서 판정 수집하기
- [Verdict Reference](../verdict-reference) — 모든 필드와 대응 방법
- [Data API](../data-api) — 서버 측에서 판정을 가져오고 대조하기