Korzen
Bot Protection
Nesta documentação

Modo first-party

A tag, o beacon e a página de desafio servidos pelo seu próprio domínio, por um SDK, por um Cloudflare Worker ou pelos dois, para visitantes cuja rede bloqueia a Korzen.

Opcional. Um filtro de DNS, um bloqueador de anúncios ou uma rede que bloqueia o host da Korzen impede a tag, o beacon e a página de desafio. Esses visitantes continuam passando, julgados só pelo que o seu servidor envia, mas numa rota que exige o token da tag eles não conseguem passar. O modo first-party serve os três a partir de um caminho do seu próprio domínio, /kz se você não escolher outro, e os encaminha ao Bot Protection com o endereço do visitante. Nada bloqueia o caminho de um site sem bloquear o site.

Há duas formas de servir o caminho, e você pode usar as duas:

A formaO que você ligaQuem serve /kz
Só o SDKfirstParty: true no Node, FirstParty: true no Goo seu servidor
Só o WorkerKORZEN_FIRST_PARTY = "1" e uma rota para /kz/*o seu Cloudflare Worker
O Worker e o SDKos dois, com o mesmo caminhoo Worker; o seu servidor manda para ele os visitantes desafiados

Em qualquer forma, a página carrega a tag do seu caminho em vez do host da Korzen. A tag tira o seu endereço do próprio src, então toda requisição que ela faz também vai para /kz:

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

O modo existe em todos os SDKs: Node (e o seu Worker), Go, Python, PHP, Java e .NET, cada um com a sua opção no README. Um cliente escrito a partir de um guia de framework não tem forwarder: nesse caso, use o SDK ou, atrás do Cloudflare, só o Worker, que dá o modo a uma origem em qualquer linguagem.

Só o SDK

O seu servidor serve o caminho. Passe a opção ao middleware que já consulta o Bot Protection e dê ao caminho um handler próprio com as mesmas opções. O client precisa ser criado por createClient, com a sua chave de API: o encaminhador a envia à Korzen em cada requisição.

// 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))
  • O que você liga. firstParty: true em protect ou withKorzen (ligado só quando é true); no Go, Options.FirstParty. Depois, o src da tag nas suas páginas passa a ser /kz/tag.js. O endereço do visitante é lido como a decisão o lê: a opção ip, no Go Options.IP.
  • Um endereço que o visitante não pode escrever. O Korzen confia no endereço que o forwarder envia com a sua chave, então a opção ip precisa ler um que o visitante não pode definir: ipFrom.vercel só na Vercel, ipFrom.cloudflare (Go: korzen.CloudflareIP) só se o seu servidor recebe tráfego apenas do Cloudflare, ipFrom.lastForwarded atrás do seu próprio load balancer ou nginx. Um cabeçalho que o visitante pode escrever deixa um script escolher o endereço que o Korzen julga.
  • O que muda para o visitante. Nada que ele veja. O navegador carrega a tag e envia o beacon ao seu domínio, então um visitante cuja rede bloqueia a Korzen é julgado com a evidência da tag e consegue fazer uma ação que exige o token dela. As requisições sob o caminho nunca são julgadas e nunca chegam aos seus handlers; todo outro caminho é julgado como antes.
  • A página de desafio. Um carregamento de página desafiado vai para /kz/challenge?c=…&r=… no seu domínio em vez do da Korzen, e o seu servidor encaminha a página, os arquivos dela e o envio. O mesmo desafio, e o visitante volta à página que pediu.
  • Fail open. Quando o Bot Protection está lento, fora do ar ou inalcançável pelo seu servidor, cada requisição sob o caminho responde 503 com o corpo vazio, e a tag descarta esse sinal; a página e as suas outras rotas não esperam por nada. Fail open
  • Como saber se funciona. curl -I https://shop.example/kz/tag.js responde 200, e o painel de rede do navegador mostra as requisições da tag indo para o seu próprio domínio. Como saber se funciona

Só o Worker

O seu Cloudflare Worker serve o caminho, sem mudar nada no seu servidor, para uma origem em qualquer linguagem. Configure o Worker como mostra Cloudflare Workers, depois ligue o modo e adicione uma rota para o caminho:

# 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
  • O que você liga. KORZEN_FIRST_PARTY com 1 ou true (qualquer outro valor além de 0 ou false o deixa desligado, com um aviso no log do Worker), uma rota para shop.example/kz/* e o src da tag nas suas páginas como /kz/tag.js. O endereço do visitante é o CF-Connecting-IP, que o Cloudflare define.
  • O que muda para o visitante. Nada que ele veja. A tag e o beacon vão para o seu domínio, respondidos no edge do Cloudflare; a sua origem nunca vê essas requisições. As rotas do Worker são julgadas como antes.
  • A página de desafio. Um carregamento de página que o Worker desafia vai para /kz/challenge no seu domínio, que o Worker serve.
  • Fail open. O mesmo do SDK: um 503 com o corpo vazio só para aquela requisição, e todas as outras seguem para a sua origem. Fail open
  • Como saber se funciona. Como abaixo; e npx wrangler tail lista as requisições sob /kz, enquanto o log de acesso da sua origem não mostra nenhuma. Como saber se funciona

O Worker e o SDK

Quando um Worker está na frente do seu site e o SDK roda na sua aplicação (para o context que só a sua aplicação conhece), ligue o modo nos dois, com o mesmo caminho.

  • O que você liga. O Worker como acima, com a rota para /kz/*, e firstParty: true (Go: FirstParty: true) no SDK atrás dele. O Worker responde /kz/* sozinho, então o SDK nunca vê essas requisições.
  • O que muda para o visitante. O mesmo que só com o Worker: a tag, o beacon e a página de desafio vêm do seu domínio, respondidos no edge.
  • A página de desafio. O firstParty do SDK manda os visitantes que ele desafia para /kz/challenge em vez do host da Korzen, e o Worker serve essa página. Sem ele, um visitante que o SDK desafia vai para o host da Korzen, que a rede dele pode bloquear.
  • Fail open. Quem serve o caminho desiste por conta própria e responde 503; nenhum segura o outro. Se as rotas do Worker deixarem /kz/* de fora, ou o Worker for retirado, o SDK serve o caminho sozinho: atrás do Cloudflare, passe a ele ip: ipFrom.cloudflare (Go: korzen.CloudflareIP).
  • Como saber se funciona. Como abaixo; npx wrangler tail lista as requisições sob /kz, e o log do seu servidor não mostra nenhuma.

O que o caminho serve

O encaminhador, o SDK ou o Worker, responde a estas e a nada mais:

No seu domínioO que é
GET /kz/tag.jsa tag, com os headers de cache da Korzen
GET /kz/n, POST /kz/so nonce da tag e o beacon (um corpo de no máximo 64 KiB)
GET /kz/pingrespondido 204 pelo próprio encaminhador, nunca encaminhado
GET e POST /kz/challenge, GET /kz/challenge/waita página de desafio e o envio dela (no máximo 16 KiB)
GET /kz/static/…o script, o estilo e as fontes da página de desafio

Qualquer outro caminho sob /kz é um 404, e um conhecido com outro método um 405; nenhum dos dois chega ao Bot Protection, então o caminho nunca é um proxy aberto. À Korzen o encaminhador envia os headers do próprio navegador de uma lista curta (User-Agent, Accept, Accept-Language, Content-Type, Origin, Sec-Fetch-*, os client hints, If-None-Match), a sua chave de API como bearer e o endereço do visitante. Nunca um cookie, o Authorization do navegador ou um X-Forwarded-*. O que volta mantém os headers de cache e de segurança da Korzen, nunca um Set-Cookie.

A Korzen só aceita o endereço do visitante de uma requisição nessas rotas que traga a sua chave de API, e no beacon só para a sua própria site key; qualquer outra coisa é recusada com 403. A sua chave nunca chega ao navegador.

O cookie _kz da tag e o armazenamento dela continuam no seu domínio, como antes; os dados que a Korzen recebe são os mesmos. Algumas evidências que a Korzen só lê quando o navegador se conecta direto a ela não existem através de um encaminhador; o resto da evidência da tag chega, e nada do que falta conta contra um visitante.

Outro caminho

firstPartyPath (Go: Options.FirstPartyPath; o Worker: KORZEN_FIRST_PARTY_PATH) define outro caminho, por exemplo quando uma lista de bloqueio aprender /kz. Ele começa com /, termina sem uma, e tem no máximo 64 caracteres em segmentos de A-Z a-z 0-9 . _ ~ -. Um que não se lê é registrado no log uma vez, e o modo fica desligado. Use o mesmo caminho no src da tag, no SDK e no Worker.

Fail open

O caminho nunca segura a sua página nem as suas outras rotas:

  • Cada requisição encaminhada desiste por conta própria: depois de 1 s num GET, 2,5 s num POST. Um timeout, um erro de rede, uma resposta acima de 4 MiB ou uma falha no encaminhador é um 503 com o corpo vazio e Cache-Control: no-store; redirecionamentos não são seguidos. Os 4xx e 5xx do próprio Bot Protection passam como vieram.
  • Sem o endereço do visitante (a opção ip não lê nenhum), o nonce, o beacon e o desafio respondem 503; a tag e os arquivos da página de desafio continuam sendo servidos.
  • A tag então descarta esse sinal, como faz quando o host da Korzen está fora do ar. Uma tag que não reportou nunca é evidência de bot: o visitante é julgado pelo que o seu servidor envia.

Como saber se funciona

  • curl -I https://shop.example/kz/tag.js responde 200, e curl -I https://shop.example/kz/ping responde 204.
  • Nas ferramentas de desenvolvedor do navegador, numa página com a tag, o painel de rede mostra /kz/tag.js, /kz/n, /kz/ping e /kz/s no seu próprio domínio, respondidos 200 ou 204, e nenhuma requisição ao host da Korzen.
  • Faça uma ação protegida e depois clique em Verifique sua integração na página Configuração: O token da tag nessa chamada mostra Visto. As suas decisões aparecem como antes na página Decisões do painel.
  • Com uma Content-Security-Policy, o host da Korzen pode sair dela: script-src 'self'; connect-src 'self'; worker-src blob: basta para a tag. A tag

Erros comuns

  • A tag ainda carregada do host da Korzen: o modo serve o caminho, mas só o src da tag leva o navegador até ele.
  • Um body parser antes do encaminhador no Express: o corpo do beacon é lido antes de o encaminhador recebê-lo. Monte-o antes de qualquer parser, ou só no caminho.
  • O Worker com KORZEN_FIRST_PARTY mas sem rota para /kz/*: as requisições chegam à sua origem, que responde 404, a menos que o SDK sirva o caminho lá.
  • Dois caminhos: o Worker num e o SDK noutro. O SDK então manda os visitantes desafiados para um caminho que o Worker não serve.
  • Chamar /v1/decide à mão: o challenge.url dele é sempre o host da Korzen. Só os SDKs e o Worker mandam o visitante para o seu caminho.