Korzen
Bot Protection
In deze documentatie

First-party-modus

De tag, zijn beacon en de uitdagingspagina vanaf je eigen domein, via een SDK, een Cloudflare Worker of allebei, voor bezoekers van wie het netwerk Korzen blokkeert.

Optioneel. Een DNS-filter, een adblocker of een netwerk dat de host van Korzen blokkeert, houdt de tag, zijn beacon en de uitdagingspagina tegen. Die bezoekers komen nog steeds door, beoordeeld op alleen wat je server stuurt, maar op een route die het token van de tag vraagt, komen ze niet verder. De first-party-modus serveert alle drie vanaf een pad van je eigen domein, /kz tenzij je een ander kiest, en stuurt ze door naar Bot Protection met het adres van de bezoeker. Niets blokkeert een pad van een site zonder de site te blokkeren.

Er zijn twee manieren om het pad te serveren, en je kunt ze allebei gebruiken:

De manierWat je aanzetWie /kz serveert
Alleen de SDKfirstParty: true in Node, FirstParty: true in Goje server
Alleen de WorkerKORZEN_FIRST_PARTY = "1" en een route voor /kz/*je Cloudflare Worker
De Worker en de SDKallebei, met hetzelfde padde Worker; je server stuurt uitgedaagde bezoekers naar hem

Op elke manier laadt de pagina de tag vanaf je pad in plaats van vanaf de host van Korzen. De tag haalt zijn adres uit zijn eigen src, dus elke request die hij doet gaat ook naar /kz:

<script src="/kz/tag.js" data-site-key="YOUR_SITE_KEY" async></script>

De modus zit in elke SDK: Node (en zijn Worker), Go, Python, PHP, Java en .NET, elk met zijn optie in de README. Een client die je naar een frameworkgids schreef, heeft geen forwarder: gebruik daar de SDK of, achter Cloudflare, de Worker op zichzelf, die de modus aan een origin in elke taal geeft.

Alleen de SDK

Je server serveert het pad. Geef de middleware die Bot Protection al vraagt de optie, en geef het pad een eigen handler met dezelfde opties. De client moet door createClient gemaakt zijn, met je API-sleutel: de forwarder stuurt die bij elke request naar Korzen.

// Express, with korzen.js as the Express guide sets it up
const options = { client: korzen, ip: (_get, req) => req.ip, firstParty: true };

app.use("/kz", protect(options)); // before any body parser: it answers /kz/... itself
app.use(express.urlencoded({ extended: false }));
app.post("/signup", protect(options), signup);

// Next.js: app/kz/[...path]/route.js serves the path
const kz = withKorzen(() => new Response(null, { status: 404 }), { client, ip: ipFrom.vercel, firstParty: true });
export { kz as GET, kz as POST };
o := korzen.Options{IP: korzen.RemoteIP, FirstParty: true}

mux.Handle("/kz/", bot.Protect(http.NotFoundHandler(), o)) // the path: Protect answers it itself
mux.Handle("POST /signup", bot.Protect(http.HandlerFunc(signup), o))
  • Wat je aanzet. firstParty: true op protect of withKorzen (alleen aan als het true is); in Go Options.FirstParty. Daarna wordt de src van de tag op je pagina's /kz/tag.js. Het adres van de bezoeker wordt gelezen zoals de beslissing het leest: de optie ip, in Go Options.IP.
  • Een adres dat de bezoeker niet kan schrijven. Korzen vertrouwt het adres dat de forwarder met je sleutel stuurt, dus de optie ip moet er een lezen dat de bezoeker niet kan zetten: ipFrom.vercel alleen op Vercel, ipFrom.cloudflare (Go: korzen.CloudflareIP) alleen als je server alleen verkeer van Cloudflare aanneemt, ipFrom.lastForwarded achter je eigen load balancer of nginx. Een header die de bezoeker kan schrijven laat een script het adres kiezen dat Korzen beoordeelt.
  • Wat er verandert voor de bezoeker. Niets wat hij ziet. Zijn browser laadt de tag en stuurt zijn beacon naar je domein, dus een bezoeker van wie het netwerk Korzen blokkeert, wordt beoordeeld met het bewijs van de tag en kan een actie doen die het token vraagt. Requests onder het pad worden nooit beoordeeld en bereiken je handlers nooit; elk ander pad wordt beoordeeld zoals voorheen.
  • De uitdagingspagina. Een uitgedaagde paginalading gaat naar /kz/challenge?c=…&r=… op je domein in plaats van dat van Korzen, en je server stuurt de pagina, haar bestanden en haar verzending door. Dezelfde puzzel, en de bezoeker komt terug op de pagina die hij opvroeg.
  • Fail-open. Is Bot Protection traag, onbereikbaar of plat, dan antwoordt elke request onder het pad 503 met een lege body, en laat de tag dat signaal vallen; de pagina en je andere routes wachten nergens op. Fail-open
  • Zo zie je dat het werkt. curl -I https://shop.example/kz/tag.js antwoordt 200, en het netwerkpaneel van de browser toont de requests van de tag naar je eigen domein. Zo zie je dat het werkt

Alleen de Worker

Je Cloudflare Worker serveert het pad, zonder wijziging aan je server, voor een origin in elke taal. Zet de Worker op zoals Cloudflare Workers laat zien, zet dan de modus aan en voeg een route voor het pad toe:

# wrangler.toml
routes = [
  { pattern = "shop.example/kz/*", zone_name = "shop.example" },
  { pattern = "shop.example/signup*", zone_name = "shop.example" },
]

[vars]
KORZEN_URL = "https://korzen.dev"
KORZEN_FIRST_PARTY = "1"
# KORZEN_FIRST_PARTY_PATH = "/kz"   # the path, /kz unless set
  • Wat je aanzet. KORZEN_FIRST_PARTY op 1 of true (elke andere waarde dan 0 of false laat hem uit, met een waarschuwing in de log van de Worker), een route voor shop.example/kz/*, en de src van de tag op je pagina's als /kz/tag.js. Het adres van de bezoeker is CF-Connecting-IP, dat Cloudflare zet.
  • Wat er verandert voor de bezoeker. Niets wat hij ziet. De tag en zijn beacon gaan naar je domein en worden beantwoord aan de edge van Cloudflare; je origin ziet die requests nooit. De routes van de Worker worden beoordeeld zoals voorheen.
  • De uitdagingspagina. Een paginalading die de Worker uitdaagt, gaat naar /kz/challenge op je domein, die de Worker serveert.
  • Fail-open. Net als bij de SDK: een 503 met een lege body voor alleen die request, en alle andere gaan door naar je origin. Fail-open
  • Zo zie je dat het werkt. Zoals hieronder; en npx wrangler tail toont de requests onder /kz, terwijl de accesslog van je origin er geen enkele toont. Zo zie je dat het werkt

De Worker en de SDK

Staat er een Worker voor je site en draait de SDK in je applicatie (voor de context die alleen je applicatie kent), zet de modus dan in allebei aan, met hetzelfde pad.

  • Wat je aanzet. De Worker zoals hierboven, met zijn route voor /kz/*, en firstParty: true (Go: FirstParty: true) op de SDK erachter. De Worker beantwoordt /kz/* zelf, dus de SDK ziet die requests nooit.
  • Wat er verandert voor de bezoeker. Hetzelfde als met alleen de Worker: de tag, zijn beacon en de uitdagingspagina komen van je domein, beantwoord aan de edge.
  • De uitdagingspagina. De firstParty van de SDK stuurt de bezoekers die hij uitdaagt naar /kz/challenge in plaats van naar de host van Korzen, en de Worker serveert die pagina. Zonder gaat een bezoeker die de SDK uitdaagt naar de host van Korzen, die zijn netwerk kan blokkeren.
  • Fail-open. Wie het pad serveert, geeft zelf op en antwoordt 503; geen van beide houdt de ander op. Laten de routes van de Worker /kz/* weg, of verdwijnt de Worker, dan serveert de SDK het pad zelf: geef hem achter Cloudflare dan ip: ipFrom.cloudflare (Go: korzen.CloudflareIP).
  • Zo zie je dat het werkt. Zoals hieronder; npx wrangler tail toont de requests onder /kz, en de log van je server toont er geen.

Wat het pad serveert

De forwarder, de SDK of de Worker, beantwoordt deze en verder niets:

Op je domeinWat het is
GET /kz/tag.jsde tag, met de cacheheaders van Korzen
GET /kz/n, POST /kz/sde nonce van de tag en zijn beacon (een body van hoogstens 64 KiB)
GET /kz/pingdoor de forwarder zelf met 204 beantwoord, nooit doorgestuurd
GET en POST /kz/challenge, GET /kz/challenge/waitde uitdagingspagina en haar verzending (hoogstens 16 KiB)
GET /kz/static/…het script, de stijl en de lettertypen van de uitdagingspagina

Elk ander pad onder /kz is een 404, en een bekend pad met een andere methode een 405; geen van beide bereikt Bot Protection, dus het pad is nooit een open proxy. Naar Korzen stuurt de forwarder de eigen headers van de browser van een korte lijst (User-Agent, Accept, Accept-Language, Content-Type, Origin, Sec-Fetch-*, de client hints, If-None-Match), je API-sleutel als bearer en het adres van de bezoeker. Nooit een cookie, de Authorization van de browser of een X-Forwarded-*. Wat terugkomt houdt de cache- en beveiligingsheaders van Korzen, nooit een Set-Cookie.

Korzen neemt het adres van de bezoeker alleen aan uit een request op deze routes die je API-sleutel draagt, en op de beacon alleen voor je eigen site key; al het andere wordt geweigerd met 403. Je sleutel bereikt de browser nooit.

De cookie _kz van de tag en zijn opslag blijven op je domein, zoals voorheen; de gegevens die Korzen ontvangt zijn dezelfde. Een paar stukken bewijs die Korzen alleen leest als de browser rechtstreeks met Korzen verbindt, ontbreken via een forwarder; de rest van het bewijs van de tag komt aan, en niets van wat ontbreekt telt tegen een bezoeker.

Een ander pad

firstPartyPath (Go: Options.FirstPartyPath; de Worker: KORZEN_FIRST_PARTY_PATH) noemt een ander pad, bijvoorbeeld zodra een blokkeerlijst /kz leert. Het begint met /, eindigt zonder, en is hoogstens 64 tekens in segmenten van A-Z a-z 0-9 . _ ~ -. Een pad dat niet te lezen is, wordt één keer gelogd, en de modus blijft uit. Gebruik hetzelfde pad in de src van de tag, in de SDK en in de Worker.

Fail-open

Het pad houdt je pagina en je andere routes nooit op:

  • Elke doorgestuurde request geeft zelf op: na 1 s voor een GET, 2,5 s voor een POST. Een timeout, een netwerkfout, een antwoord boven 4 MiB of een fout in de forwarder is een 503 met een lege body en Cache-Control: no-store; redirects worden niet gevolgd. De eigen 4xx en 5xx van Bot Protection gaan ongewijzigd door.
  • Zonder het adres van de bezoeker (de optie ip leest er geen) antwoorden de nonce, de beacon en de uitdaging 503; de tag en de bestanden van de uitdagingspagina worden nog steeds geserveerd.
  • De tag laat dat signaal dan vallen, zoals wanneer de host van Korzen plat ligt. Een tag die niets meldde is nooit bewijs van een bot: de bezoeker wordt beoordeeld op wat je server stuurt.

Zo zie je dat het werkt

  • curl -I https://shop.example/kz/tag.js antwoordt 200, en curl -I https://shop.example/kz/ping antwoordt 204.
  • In de ontwikkelaarstools van de browser toont het netwerkpaneel op een pagina met de tag /kz/tag.js, /kz/n, /kz/ping en /kz/s op je eigen domein, beantwoord met 200 of 204, en geen enkele request naar de host van Korzen.
  • Doorloop een beschermde actie en klik dan op Controleer je integratie op de Set-up-pagina: Het token van de tag in die call staat op Gezien. Je beslissingen verschijnen zoals voorheen op de pagina Beslissingen van het dashboard.
  • Met een Content-Security-Policy kan de host van Korzen eruit: script-src 'self'; connect-src 'self'; worker-src blob: is genoeg voor de tag. De tag

Veelgemaakte fouten

  • De tag laadt nog vanaf de host van Korzen: de modus serveert het pad, maar alleen de src van de tag stuurt de browser erheen.
  • Een body parser vóór de forwarder in Express: de body van de beacon wordt gelezen voordat de forwarder hem krijgt. Monteer hem vóór elke parser, of alleen op het pad.
  • De Worker met KORZEN_FIRST_PARTY maar zonder route voor /kz/*: de requests bereiken je origin, die 404 antwoordt, tenzij de SDK het pad daar serveert.
  • Twee paden: de Worker op het ene en de SDK op een ander. De SDK stuurt uitgedaagde bezoekers dan naar een pad dat de Worker niet serveert.
  • /v1/decide met de hand aanroepen: zijn challenge.url is altijd de host van Korzen. Alleen de SDK's en de Worker sturen de bezoeker naar je pad.