Korzen
Bot Protection

Auth0

Auth0 と Bot Protection を組み合わせます。ユーザーが作成される前に問い合わせる pre-user-registration Action と、そこで見えるもの・見えないものを説明します。

Auth0 は、パスワードまたはパスワードレスのコードで新規登録するユーザー(Database 接続または Passwordless 接続)を作成する前に Action を実行します。この Action から Bot Protection を呼び出し、新規登録を拒否できます。新規登録ページは Auth0 のドメイン上にある Auth0 のページなので、Action には _kz Cookie が届きません。判定は IP と User-Agent にもとづいて行われます。

タグ

訪問者が新規登録の前に訪れるページや、ログイン・支払いを行うページなど、自社のページにタグを置いてください。そうした判定には token が付きます。

<script src="https://korzen.dev/v1/tag.js" data-site-key="YOUR_SITE_KEY" async></script>

呼び出し

Action です。Auth0 が Action に与える時間は合計 20 秒です。呼び出しはお客様のタイムアウトに従い、それ以外の場合はすべて新規登録を通します。

// Auth0 Dashboard: Actions, Triggers, pre-user-registration; a new Action.
// Add the secret KORZEN_API_KEY to the Action.
exports.onExecutePreUserRegistration = async (event, api) => {
  let d = null;
  try {
    const res = await fetch("https://korzen.dev/v1/decide", {
      method: "POST",
      headers: { Authorization: `Bearer ${event.secrets.KORZEN_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        ip: event.request.ip,
        user_agent: event.request.user_agent,
        ja4: event.security_context?.ja4 || undefined, // behind Cloudflare only: left out without it
        headers: { "accept-language": event.request.language },
        path: "/signup",
        method: "POST",
      }),
      signal: AbortSignal.timeout(200),
    });
    if (res.ok) d = await res.json();
  } catch {} // fail open
  if (d?.action === "block") {
    api.access.deny("korzen_block", "We could not create your account.");
    return;
  }
  // "challenge": Auth0's own CAPTCHA or MFA, if your plan has them
  if (d?.request_id) api.user.setAppMetadata("korzen_request_id", d.request_id);
};

Auth0 が Action に渡すのはブラウザーの言語と User-Agent だけで、リクエストのほかのヘッダーは渡さないため、この呼び出しが送るのは accept-language だけです。同じブラウザービルドが同じ値で送るヘッダーは、Bot Protection がサーバーからの呼び出しで読むものですが、ここにはありません。

トラフィックが Cloudflare を通るテナント(Auth0 の Enterprise プラン)では、イベントに security_context.ja4 もあります。これは訪問者と Auth0 のエッジとの TLS ハンドシェイクの JA4 で、自社サーバーの前の Cloudflare から Bot Protection が読むのと同じ形式です。呼び出しは、値があれば ja4 として送り、なければ省きます。

訪問者の IP

event.request.ip は Auth0 に到達したアドレスで、すでに訪問者のものになっています。設定は不要です。自前のプロキシの背後にカスタムドメインを置いている場合は、訪問者の IP について Auth0 のカスタムドメイン設定を確認してください。

Auth0 からの判定にはタグの token が付きません。ほかのページでタグが検出された後は、token がないと score が少し上がります(no_token)が、それだけで bot になることはありません。

アクションを適用する

読むのは action だけです。block の場合は、その操作がほかの理由で失敗したときと同じエラーを返し、bot に何も悟らせないようにします。challenge の場合は、すでにある確認手段(CAPTCHA、メールや電話の確認コード)を使います。何もなければそのまま続けます。アカウントは monitor mode で始まり、その間 action は常に allow です。block mode をオンにするまで、訪問者にとっては何も変わりません。

request_id を保存する

すべての応答には request_id が含まれます。操作で作成されたもの(アカウントや注文)のカラムに保存するか、その ID と一緒にログに残してください。お問い合わせの際にその判定を特定でき、後からどの応答が偽アカウントを通したのかを調べられます。

手元のマシンでテストする

開発用のテナントを使います。Action エディターの Test でサンプルイベントを使って実行し、次にログインページから新規登録すると、判定が dashboard の判定ページに /signup で表示されます。fail-open は、シークレット KORZEN_API_KEY に誤った値を設定して確認します。新規登録はそのまま完了します。

よくある間違い

  • ソーシャルログインによる新規登録。Google や GitHub などでは pre-user-registration トリガーが実行されません。post-login Action を使えば、初回ログイン時に Bot Protection に問い合わせられます。
  • ブラウザーから /v1/decide を呼び出す。API key がすべての訪問者に渡ってしまいます。呼び出しはサーバーで行ってください。
  • タイムアウトがない、またはエラーで新規登録が止まる。200 以外の応答や、時間内に応答がない場合は、訪問者を通すという意味です。