Korzen
Bot Protection

bot 検知 API

顧客は通し、bot は止める

ログイン、サインアップ、チェックアウトなど、bot に狙われるページの前で、サーバーが訪問者について Bot Protection に問い合わせ、クラス、score、理由を受け取ります。サイトの前にプロキシは不要、顧客にパズルを解かせることもありません。

14 日間の無料トライアル · プランは月額 $29 から

# Ask before the protected action
curl -X POST https://korzen.dev/v1/decide \
  -H "Authorization: Bearer $KORZEN_API_KEY" \
  -d '{
    "ip": "203.0.113.10",
    "user_agent": "Mozilla/5.0 …",
    "token": "<_kz cookie>",
    "path": "/checkout"
  }'
// Ask before the protected action; let through on any error
const d = await fetch(API + "/v1/decide", {
  method: "POST",
  headers: { Authorization: `Bearer ${KEY}` },
  body: JSON.stringify({ ip, user_agent, token, path }),
  signal: AbortSignal.timeout(200),
}).then((r) => r.json()).catch(() => null);

if (d?.action === "block") return res.status(403).end();
# Ask before the protected action; let through on any error
try:
    d = requests.post(API + "/v1/decide",
        headers={"Authorization": f"Bearer {KEY}"},
        json={"ip": ip, "user_agent": ua, "token": token},
        timeout=0.2).json()
except requests.RequestException:
    d = {}
if d.get("action") == "block":
    abort(403)
// Ask before the protected action; let through on any error
c := &http.Client{Timeout: 200 * time.Millisecond}
r, _ := http.NewRequest("POST", api+"/v1/decide", body)
r.Header.Set("Authorization", "Bearer "+key)
var d struct{ Action string }
if res, err := c.Do(r); err == nil {
    json.NewDecoder(res.Body).Decode(&d)
    res.Body.Close()
}
if d.Action == "block" {
    http.Error(w, "try again later", 403)
    return
}
// Ask before the protected action; let through on any error
var req = HttpRequest
    .newBuilder(URI.create(API + "/v1/decide"))
    .header("Authorization", "Bearer " + KEY)
    .timeout(Duration.ofMillis(200))
    .POST(BodyPublishers.ofString(json)).build();
String act = "";
try {
    var res = http.send(req, BodyHandlers.ofString());
    act = mapper.readTree(res.body())
        .path("action").asText();
} catch (Exception e) { /* let through */ }
if (act.equals("block")) {
    response.sendError(403);
    return;
}
// Ask before the protected action; let through on any error
$ch = curl_init(API . '/v1/decide');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($visitor),
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . KEY],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT_MS => 200,
]);
$d = json_decode(curl_exec($ch) ?: '{}', true);
if (($d['action'] ?? '') === 'block') {
    http_response_code(403);
    exit;
}
// Ask before the protected action; let through on any error
// http: an HttpClient with "Authorization: Bearer KEY"
var act = "";
try {
    using var cts = new CancellationTokenSource(200);
    var res = await http.PostAsJsonAsync(
        Api + "/v1/decide", visitor, cts.Token);
    var d = await res.Content
        .ReadFromJsonAsync<JsonElement>(cts.Token);
    act = d.GetProperty("action").GetString();
} catch { /* let through */ }
if (act == "block") return Results.StatusCode(403);

200 OK RESPONSE

{
  "request_id": "AQEAmJ3…",
  "class": "bot_high",
  "bot_score": 96,
  "human_score": 0,
  "reasons": ["behaviour", "inconsistency"],
  "rules": [],
  "step_up": false,
  "action": "block",
  "intended_action": "block",
  "mode": "block",
  "challenge": null,
  "token_required": false,
  "seen_before": 0,
  "crawler": null
}
·:+* 届いたリクエスト | アクションの手前の Bot Protection x 止められた bot # 通過した人

Dashboard

すべての判定と、その理由を確認

トラフィックが急増したときにチームが開く dashboard です。Bot Protection が何を返したかを 1 時間ごとに、そして各応答の根拠となったルールを表示します。

Bot Protection · 概要
概要画面:クラス別に積み上げた 1 時間ごとの判定数、bot の割合、主な理由、最新の bot

すべての応答を、理由とともに

最新の判定を新しい順に表示し、数秒ごとに更新します。クラス、パス、国、理由、時刻で絞り込み、発動したルールを 1 つずつ確認できます。理由にカーソルを合わせると、その意味が表示されます。

Bot Protection · 判定
クラスで絞り込んだ判定の表。時刻、クラス、bot score、パス、国、理由を表示

仕組み

コードは 2 つだけ。トラフィックは変わりません

1

タグを追加する

ページに入れた小さなスクリプトが、ブラウザが自身について示す情報を Bot Protection に直接送り、署名付きの token をファーストパーティ Cookie に保存します。

<script src="…/v1/tag.js"
  data-site-key="sk_…"
  async></script>
2

アクションの前に問い合わせる

ログイン、サインアップ、チェックアウトなど、bot に狙われるページの前で、サーバーがリクエストの IP、User-Agent、token を短いタイムアウトで送ります。

POST /v1/decide
{"ip": "…", "user_agent": "…",
 "token": "…",
 "path": "/checkout"}
3

アクションを適用する

応答は allow、challenge、block のいずれかです。最初は常に allow を返す monitor mode で始め、信頼できると判断したら block mode をオンにします。エラーやタイムアウトのときは、訪問者を通してください。

if d.Action == "block" {
    return http.StatusForbidden
}

または、Claude Code、Codex、Cursor にプロンプトを 1 つ貼り付けるだけで、両方を追加できます:AI でセットアップ。

Next.js、Express、Django、Rails、Laravel、Spring Boot、ASP.NET Core、Cloudflare Workers などのガイドはドキュメントにあります。

応答

推測が必要な数値ではなく、そのまま使えるクラス

human high

セッション外の証拠がある人。過去の注文や本人名義のカードなど。

allow
human

お客様自身のフィードバックによって人と裏付けられた訪問者。

allow
unknown

どちらとも判断できるだけの証拠がない。初回訪問の多くはここに入ります。

allow
bot

クライアントの挙動から判断した自動化。

challenge
bot high

挙動に強い証拠がある自動化。

block

強い証拠が両方向を指しているときは、応答に step_up が含まれます。CAPTCHA ではなく、パスキーや 3-D セキュアを求めてください。

Bot Protection が読み取るもの

bot が長くは偽れない証拠

一貫性

User-Agent は client hints、プラットフォーム、Web Worker、GPU と一致しているか。パッチを当てたブラウザは自己矛盾を起こします。

本物の入力

ページが受け取った入力の種類:マウス、キー、タッチ、ホイール。スクリプトはカーソルを動かさずにクリックします。

ネットワーク

データセンター、IP から遠く離れたタイムゾーン、プロキシ経由を示す往復時間。

ファーム

多数のネットワークの背後にある 1 台のデバイス、多数の出口で使われる 1 つの token、セッションをまたぐ 1 つのフィンガープリント。ファームが避けられないグループです。

時間をかけた挙動

機械的なリズム、拒否されても続く再試行、ページを一度も読み込まないリクエスト。訪問者ごとに 10 分間の記憶を持ちます。

お客様のフィードバック

完了した注文やチャージバックを Bot Protection に伝えてください。訪問者を human とするのは、セッション外の証拠です。

なぜ API なのか

新たな障害点を増やさない保護

Bot ProtectionCAPTCHAサイト前段のプロキシ
訪問者がパズルを解く一切なし毎回ときどき
トラフィックが経由するいいえいいえ。ただし、すべてのフォームがその応答を待つすべて
ダウンしたとき訪問者は通過するフォームが止まるサイトが止まる
理由がわかるクラス、score、証拠の種類合格か不合格かscore のみ
どこで決めるか自社のコードでベンダーのウィジェットでベンダーのルールで

よくある質問

お問い合わせの前に

本物の顧客が block されることはありませんか?

VPN、モバイルネットワーク、オフィスの共有回線を使っていると score は上がることがありますが、それだけで bot になることはありません。bot と判定するには、クライアントの挙動に自動化の証拠が必要です。過去に購入したことのある顧客は human high として戻ってきます。

Bot Protection は個人データを保存しますか?

識別子は、お客様ごとに毎日ローテーションされるキーでハッシュ化されるため、お客様や日付をまたいで突き合わせることはできません。挙動は 10 分後に破棄され、dashboard 用に集計と最新の判定だけを保持します。

block された bot にもサイトは見えますか?

はい。Bot Protection が判定するのは、新規登録、ログイン、checkout など、お客様のサーバーが問い合わせる操作であり、ページの読み込みではありません。block mode でも bot はページを読み込めます。お客様のサーバーが拒否するのは操作のほうです。

bot のいないサイトへ

アカウントを作成し、2 つのスニペットをコピーすれば、判定が届き始めます。