# Perlindungan spam

> Honeypot, timestamp bertanda tangan, penilaian spam, pemeriksaan manusia, domain yang diizinkan, dan rate limit.

Setiap formulir mendapat perlindungan berlapis tanpa penyiapan: honeypot, timestamp render bertanda tangan, penilaian spam, dan rate limit. Di atas itu, Anda bisa menambahkan pemeriksaan manusia dan membatasi formulir ke domain Anda sendiri.

| Lapisan | Aktif secara default | Hasil |
| --- | --- | --- |
| [Rate limit](https://ikiform.com/id/docs/rate-limits.md) | Ya, preset Standard | `429 rate_limited` |
| [Domain yang diizinkan](#allowed-domains) | Tidak | Spam, atau `403 origin_not_allowed` |
| [Pemeriksaan manusia](#human-check) | Tidak | `403 challenge_required` atau halaman pemeriksaan |
| [Honeypot](#honeypot) | Ya | +5 ke skor |
| [Penilaian spam](#scoring) | Ya | Spam di 5 atau lebih |

## Cara spam ditangani

Kiriman yang dinilai sebagai spam disimpan dengan status `spam` dan tetap mendapat respons sukses biasa, jadi bot tidak belajar apa pun. Spam tidak mengirim webhook maupun email, tidak dihitung dalam pemakaian bulanan Anda, dan dihapus setelah 30 hari. Tinjau di dashboard atau dengan [REST API](https://ikiform.com/id/docs/api.md#mark-spam); menandai kiriman sebagai bukan spam akan mengirim webhook-nya saat itu.

## Honeypot

Tambahkan input tersembunyi yang tidak pernah diisi orang. Nama default-nya `_gotcha`; Anda bisa mengubahnya per formulir di **Honeypot field** pada pengaturan formulir. Teknik menyembunyikannya ada di [Formulir HTML](https://ikiform.com/id/docs/html.md#honeypot).

`Honeypot`:

```html
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
```

## Penilaian spam

Setiap kiriman mendapat skor dari sinyal-sinyal ini. Di 5 atau lebih, kiriman disimpan sebagai spam.

| Sinyal | Alasan di dashboard | Skor |
| --- | --- | --- |
| Honeypot terisi | `honeypot` | +5 |
| Dikirim kurang dari 2 detik setelah formulir dirender | `timestamp_fast` | +3 |
| Timestamp render bertanda tangan dipalsukan atau lebih tua dari 24 jam | `timestamp_invalid` | +3 |
| Tautan di field nama atau subjek | `url_in_name` | +3 |
| Field yang sama dikirim ke formulir ini dalam 24 jam terakhir | `duplicate` | +3 |
| Tiga tautan atau lebih secara total | `many_urls` | +2 |
| Frasa spam yang dikenal (22 bawaan) | `keyword` | +2 |
| Alamat email di domain email sekali pakai | `disposable_email` | +2 |

- Timestamp render hanya ada jika formulir dirender oleh SDK atau [komponen shadcn/ui](https://ikiform.com/id/docs/shadcn.md), yang mengirim `_ikiform_ts` bertanda tangan. Kiriman HTML biasa tanpa timestamp tidak diberi penalti.
- Field nama adalah field apa pun yang namanya mengandung `name`, `firstname`, `lastname`, `fullname`, `surname`, atau `subject`, tetapi bukan kata seperti `company`, `website`, atau `domain`.
- Dua alasan memaksa spam berapa pun skornya: `origin_mismatch` dari [domain yang diizinkan](#allowed-domains) dan `captcha_unavailable` saat Turnstile tidak bisa dihubungi.

## Pemeriksaan manusia

Setiap formulir punya pengaturan **Spam check** dengan tiga pilihan. Pengaturan ini mati secara default, jadi integrasi `fetch` dan curl yang sudah ada tetap berfungsi saat Anda membuat formulir.

| Pilihan | Fungsinya |
| --- | --- |
| Off | Tanpa challenge. Honeypot, timestamp, dan penilaian tetap berjalan. |
| Ikiform (disarankan) | Cloudflare Turnstile yang dijalankan Ikiform di ikiform.com. Tidak perlu akun atau key Cloudflare. |
| Key Turnstile Anda sendiri | Widget Turnstile dan secret key Anda. Hostname token dicocokkan dengan domain yang diizinkan. |

### Cara pemeriksaan Ikiform berjalan

Cara token didapat tergantung cara Anda mengirim:

- **Kiriman formulir HTML:** kiriman tanpa token mendapat halaman singkat "Memeriksa bahwa Anda manusia" di ikiform.com. Halaman itu menjalankan Turnstile dan mengirim ulang field aslinya dengan token, lalu browser mengikuti redirect Anda seperti biasa.
- **JavaScript:** panggil `challenge(formId)` dari SDK. Fungsi ini memasang frame kecil ikiform.com yang tetap tak terlihat kecuali Turnstile butuh klik, lalu resolve dengan token. Berikan ke `submit` sebagai `turnstileToken`. Kiriman `fetch` tanpa token mendapat `403 challenge_required`.
- **Komponen shadcn/ui:** sudah ditangani untuk Anda.
- **Server dengan secret key:** pemeriksaan dilewati, karena server tidak bisa menyelesaikannya.

`form.js`:

```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.");
});
```

Token hanya sekali pakai dan berlaku 300 detik, jadi ambil satu per kiriman. Kiriman JSON tanpa token mendapat `403 challenge_required` beserta `challenge_url`; token yang gagal diverifikasi mendapat `403 captcha_failed`.

`403 Forbidden`:

```json
{
  "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"
}
```

> Jika Turnstile sendiri tidak bisa dihubungi, kiriman diterima dan disimpan sebagai spam dengan alasan `captcha_unavailable`, jadi Anda bisa memulihkannya alih-alih kehilangannya.

### Key Turnstile Anda sendiri

Tempel site key dan secret key Anda ke pengaturan formulir. Secret dienkripsi saat disimpan. Render widget di formulir Anda agar menambahkan `cf-turnstile-response` ke kiriman, atau berikan token ke `submit` sebagai `turnstileToken`. Kiriman tanpa token yang valid mendapat `403 captcha_failed`, termasuk kiriman dengan secret API key.

## Domain yang diizinkan

Cantumkan domain tempat formulir Anda berada di **Allowed domains**. Kiriman lalu harus berasal dari salah satunya atau subdomainnya, dinilai dari header `Origin`, atau `Referer` jika tidak ada origin. Biarkan daftar kosong untuk menerima kiriman dari situs mana pun.

| Domain yang diizinkan | Kiriman dari | Cocok |
| --- | --- | --- |
| `example.com` | `https://example.com` | Ya |
| `example.com` | `https://www.example.com` | Ya, subdomain |
| `example.com` | `http://localhost:3000` | Tidak, tambahkan `localhost` selama pengembangan |
| `example.com` | `https://example.co` | Tidak |
| `app.example.com` | `https://example.com` | Tidak, domain induk tidak cocok |

**When a post comes from another site** menentukan apa yang terjadi saat tidak cocok: **Mark as spam** (default) atau **Reject** dengan `403 origin_not_allowed`. Kiriman dengan secret API key melewati pemeriksaan ini. Domain yang diizinkan juga menentukan tujuan `_redirect` mana yang diikuti dan di mana frame pemeriksaan Ikiform boleh disematkan.

## Rate limit

Setiap formulir punya preset **Rate limit**, dihitung secara presisi per IP pengunjung dan formulir. Standard mengizinkan 5 kiriman per menit, Relaxed 20 per menit, dan Strict 2 setiap 10 menit. Aturan lengkapnya ada di [Rate limit](https://ikiform.com/id/docs/rate-limits.md).
