フォーム ID

スパム対策

ハニーポット、署名付きタイムスタンプ、スパムスコア、人間確認、許可ドメイン、レート制限。

どのフォームにも、設定なしで多層の保護が付いています。ハニーポット、署名付きの描画タイムスタンプ、スパムスコア、レート制限です。さらに人間確認を追加したり、フォームを自分のドメインに限定したりできます。

保護レイヤーデフォルトでオン結果
レート制限はい(標準プリセット)429 rate_limited
許可ドメインいいえスパム、または 403 origin_not_allowed
人間確認いいえ403 challenge_required または確認ページ
ハニーポットはいスコアに +5
スパムスコアはい5 以上でスパム

スパムの扱い

スパムと判定された送信はステータス spam で保存され、通常の成功の応答を返すので、ボットは何も学べません。スパムは Webhook もメールも送らず、月間の使用量にもカウントされず、30 日後に削除されます。ダッシュボードか REST API で確認でき、スパムではないとマークすると、その時点で Webhook が送られます。

ハニーポット

人が入力することのない非表示の入力欄を追加します。デフォルトの名前は _gotcha で、フォームの設定の Honeypot field(ハニーポットフィールド)でフォームごとに変更できます。隠し方は HTML フォームを参照してください。

ハニーポット
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />

スパムスコア

送信ごとに、次のシグナルからスコアを計算します。5 以上でスパムとして保存されます。

シグナルダッシュボードに表示される理由スコア
ハニーポットに入力があるhoneypot+5
フォームの描画から 2 秒未満で送信されたtimestamp_fast+3
署名付きの描画タイムスタンプが偽造されている、または 24 時間より古いtimestamp_invalid+3
名前や件名のフィールドにリンクがあるurl_in_name+3
同じフィールドが直近 24 時間以内にこのフォームに送られているduplicate+3
リンクが合計 3 つ以上あるmany_urls+2
既知のスパムの定型文(22 種類を内蔵)keyword+2
使い捨てメールのドメインのメールアドレスdisposable_email+2
  • 描画タイムスタンプが存在するのは、署名付きの _ikiform_ts を送る SDK か shadcn/ui コンポーネントでフォームを描画した場合だけです。これを含まないプレーンな HTML の送信は減点されません。
  • 名前のフィールドとは、名前に name、firstname、lastname、fullname、surname、subject を含むフィールドのことで、company、website、domain のような語は含みません。
  • スコアにかかわらず必ずスパムになる理由が 2 つあります。許可ドメインによる origin_mismatch と、Turnstile に接続できない場合の captcha_unavailable です。

人間確認

各フォームには、3 つの選択肢を持つ Spam check(スパムチェック)の設定があります。デフォルトはオフなので、フォームを作成しても、既存の fetch や curl の連携はそのまま動きます。

選択肢内容
オフチャレンジなし。ハニーポット、タイムスタンプ、スコアは引き続き動きます。
Ikiform(推奨)Ikiform が ikiform.com で動かす Cloudflare Turnstile。Cloudflare のアカウントやキーは不要です。
独自の Turnstile キー自分の Turnstile ウィジェットとシークレットキー。トークンのホスト名は許可ドメインと照合されます。

Ikiform の確認の流れ

送信方法ごとの、人間確認トークンの取得の流れ

HTML フォーム

  1. ブラウザーがフォームを送信
  2. トークンがまだないので、ikiform.com が短い確認ページを表示
  3. Turnstile が動き、ページがトークン付きで同じフィールドを再送信
  4. リダイレクト先またはサンクスページへ 303

JavaScript

  1. challenge(formId) が非表示の ikiform.com フレームをマウント
  2. Turnstile が動き、クリックが必要な場合のみ表示
  3. submit(formId, data, { turnstileToken })
  4. 送信 ID 付きで 201

シークレットキーを使うサーバー

  1. Authorization: Bearer ik_sk_... を付けて POST
  2. Ikiform の確認はスキップ
  3. 送信 ID 付きで 201
form.js
import { challenge, submit } from "@ikiform/sdk";

const form = document.querySelector("form");

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const turnstileToken = await challenge("frm_xxxxxxxxxxxxxxxx");
  const result = await submit("frm_xxxxxxxxxxxxxxxx", new FormData(form), { turnstileToken });
  if (result.ok) form.replaceWith("Thanks, we got it.");
});

トークンは 1 回限りで有効期間は 300 秒なので、送信ごとに 1 つ取得してください。トークンのない JSON の送信には challenge_url 付きの 403 challenge_required が、検証に失敗したトークンには 403 captcha_failed が返ります。

403 Forbidden
{
  "ok": false,
  "error": {
    "code": "challenge_required",
    "message": "Complete the human check, then send the form again.",
    "challenge_url": "https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx/challenge"
  },
  "request_id": "req_01k7v2c9t3e8m5q4x6z0a1b2c3"
}
Turnstile 自体に接続できない場合、送信は受け付けられ、理由 captcha_unavailable でスパムとして保存されるので、失うことなく元に戻せます。

独自の Turnstile キー

サイトキーとシークレットキーをフォームの設定に貼り付けます。シークレットは保存時に暗号化されます。フォームにウィジェットを描画して送信に cf-turnstile-response を追加させるか、トークンを turnstileToken として submit に渡します。有効なトークンのない送信には、シークレット API キーを使った送信も含め、403 captcha_failed が返ります。

許可ドメイン

フォームを置くドメインを Allowed domains(許可ドメイン)に登録します。すると送信は、Origin ヘッダー(オリジンがない場合は Referer)で判定して、それらのドメインかそのサブドメインから送られたものでなければならなくなります。どのサイトからの送信も受け付けるには、一覧を空にしておきます。

許可ドメイン送信元一致するか
example.comhttps://example.comはい
example.comhttps://www.example.comはい(サブドメイン)
example.comhttp://localhost:3000いいえ。開発中は localhost を追加
example.comhttps://example.coいいえ
app.example.comhttps://example.comいいえ。親ドメインは一致しない

When a post comes from another site(他のサイトから送信された場合)の設定で、一致しないときの動作を決めます。Mark as spam(デフォルト)か、403 origin_not_allowed で Reject するかです。シークレット API キーを使った送信はこのチェックをスキップします。許可ドメインは、どの _redirect 先に従うか、Ikiform の確認フレームをどこに埋め込めるかも決めます。

レート制限

各フォームには Rate limit のプリセットがあり、訪問者の IP とフォームごとに正確にカウントされます。標準は 1 分に 5 件、緩めは 1 分に 20 件、厳しめは 10 分ごとに 2 件です。詳しいルールはレート制限にあります。