Zbavte se na webu otravných robotů, vyžeňte je nástrojem go-away

Dnes
Doba čtení: 9 minut

Sdílet

Policista vyhání robota z prostoru
Autor: Root.cz s využitím Zoner AI
Webové servery jsou čím dál více zatěžovány různými roboty, kteří se z nich snaží agresivně získat veškerý obsah. Nástroj go-away je jednoduchým řešením pro blokování přístupu robotů. Jak se nasazuje a co umí?

Problém se scrapovacími roboty

Co se dozvíte v článku
  1. Problém se scrapovacími roboty
  2. Co se s tím dá lehce dělat?
  3. Proof-of-work captcha
  4. Nasazení go-away
  5. Jak to spustit
  6. Podpora GeoIP
  7. Metriky
  8. Různé
  9. Jednoduché a účinné řešení

Provozovatelé obsahových webů v poslední době hlásí větší množství přístupů zajímavých robotů – často to jsou roboti sbírající obsah pro různé AI modely a velmi často tito roboti nedodržují pravidla uvedená v souboru  robots.txt.

U klienta, který provozuje obsahový web určený primárně pro Českou republiku jsme pozorovali pravidelné výpadky způsobené scrapovacími roboty, kteří přetížili backend aplikace.

Co se s tím dá lehce dělat?

Rychlým způsobem, jak problém „vyřešit“ je zablokování přístupu z jiných lokalit, než je Česká republika. Roboti přicházeli z různých lokalit (USA, Indie, Vietnam, Írán, Kazachstán a další), odkud naši obvyklí návštěvníci nepřicházejí.

Další obvyklou možností je zkusit nasadit nějaký rate-limiting, který omezuje počet požadavků z IP adresy nebo celé IP sítě či ASN. Zkusili jsme udělat rychlou statistiku a ukázalo se, že roboti chodí z většího množství různých IP adres i sítí.

Nechtěli jsme odříznout celé Spojené státy, protože odtamtud chodí i „hodní roboti“.

Nasadili jsme tedy jen rate-limiting, kdy jsme na základě databáze GeoIP jsme povolili přístup z České republiky a pro ostatní sítě jsme nasadili rate-limiting na úrovni států – desítky požadavků z minutu pro celý stát. Rate-limiting vracel HTTP stavový kód 429 Too Many Requests, který je pro tyto účely doporučený.

Jako dočasné řešení to fungovalo a backendu aplikace se odlehčilo.

Velmi rychle se ale ozval uživatel, že mu aplikace náhodně vyhazuje chybu 429. Podpora poté zjistila, že se uživatel nachází ve Spojených státech, na které aplikujeme omezení.

Proof-of-work captcha

V poslední době se objevily nástroje, které se snaží roboty zdržet vykonáním nějaké práce, například – Anubis, CAP, či go-away. Hostované řešení nabízí třeba Cloudflare Turnstile.

Obvyklý způsob, jak tyto nástroje fungují je zaslání nějaké výpočetně náročnější úlohy, kterou musí prohlížeč vyřešit, než mu je povolen přístup k obsahu. Typicky se jedná o spočítání inverze nějakého haše (kryptografckého otisku). Protože spočítání úlohy trvá nějakou krátkou dobu (třeba půl sekundy), roboti, kteří by chtěli stahovat velké množství obsahu, jsou tím poněkud zdrženi.

Běžný uživatel toto zpoždění většinou zaznamená pouze během prvního přístupu na web. Při první návštěvě se mu do cookie uloží podepsaný token, kterým prokáže, že už úlohu vyřešil a při následných přístupech už je mu přístup povolen bez dalšího zdržování.

Při nasazování se filtr obvykle umístí před aplikační servery jako další vrstva, přes kterou musí požadavky projít. Požadavky s podepsaným tokenem jsou předány aplikačnímu serveru, požadavky bez platného tokenu dostanou požadavek na výpočet.

go-away schéma

Schéma nasazení go-away 

Autor: Věroš Kaplan, s použitím Gemini

Nasazení go-away

Nasadili jsme nástroj go-away, který je napsaný v jazyce Go, protože bylo snadné ho nasadit jako samostatnou službu v kontejnerech. Zároveň bylo možné s malým úsilím upravit úvodní stránku, která se uživateli zobrazuje.

Konfigurace go-away je jednoduchá. Je potřeba nastavit základní parametry, jako je port, na kterém bude go-away přijímat požadavky, a adresy backendů, na které má požadavky posílat. Je možné nastavit i vzhled stránky s výzvou, která se uživateli zobrazí při prvním přístupu a spoustu dalších drobností.

Ukázkový konfigurační soubor je například v adresáři examples. Za zmínku stojí, že je možné kombinovat nastavení v konfiguračním souboru, z příkazové řádky a z proměnných prostředí.

Druhá část nastavení je v pravidlech (policy), podle kterých go-away rozhoduje, který provoz povolit a který omezit.

Nastavení pravidel se obvykle nachází v souboru go-away-policy.yaml. Kromě souboru s politikou je možné připravit i sadu snippetů (útržků) s pravidly, které se načítají také. go-away pak při startu natáhne všechny soubory s pravidly a spojí je do jedné datové struktury. Tu potom zkusí zpracovat a po načtení pravidel začne přijímat požadavky.

Konfigurační soubor s politikou může obsahovat čtyři základní klíče:

  • networks  – definice sítí (IP rozsahů), které je možné použít v pravidlech
  • conditions  – definice podmínek, které je možné použít v pravidlech
  • rules  – samotná pravidla, která určují, jak se má s požadavky nakládat
  • challenges  – definice výzev (challenge), které pak možné použít v pravidlech

Ukázky kousků konfigurace jsou ve zdrojových kódech v adresáři examples/snippets .

Definice síťových rozsahů

Pod klíčem networks můžeme popsat různé sítě (IP rozsahy), podle kterých pak budeme v pravidlech filtrovat. Je možné definovat sítě ručně rozsahem – to se používá typicky třeba pro rozsahy vnitřních sítí.

Mnohem užitečnější je ale možnost definovat sítě dynamicky načítáním z veřejně dostupných zdrojů s IP rozsahy. To se hodí pro filtrování rozsahů cloudových providerů, jako je třeba AWS, odkud často chodí část provozu, který nechceme obsluhovat.

Ukázka z konfigurace pro definici vnitřních sítí ze vzorové konfigurace:

yaml
networks:
  private:
    # Private network CIDR blocks
    - prefixes:
        # private networks
        - "10.0.0.0/8"
        - "172.16.0.0/12"
        - "192.168.0.0/16"
        - "fc00::/7"
        # CGNAT
        - "100.64.0.0/10"

Ukázka konfigurace síťových rozsahů pro AWS, Google bota a DigitalOcean ze vzorové konfigurace.

Výhodou je, že go-away pak načte IP rozsahy přímo z veřejně dostupných zdrojů a pravidelně je aktualizuje.

networks:
  aws-cloud:
    - url: https://ip-ranges.amazonaws.com/ip-ranges.json
      jq-path: '(.prefixes[] | select(has("ipprefix")) | .ipprefix), (.prefixes[] | select(has("ipv6prefix")) | .ipv6prefix)'
  googlebot:
    - url: https://developers.google.com/static/search/apis/ipranges/googlebot.json
      jq-path: '(.prefixes[] | select(has("ipv4Prefix")) | .ipv4Prefix), (.prefixes[] | select(has("ipv6Prefix")) | .ipv6Prefix)'
  digitalocean:
    - url: https://www.digitalocean.com/geo/google.csv
      regex: "(?P(([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)|([0-9a-f:]+::))/[0-9]+),"

Definice podmínek

V podmínkách si můžeme nadefinovat různé logické výrazy, které pak budeme používat. Výrazy se píšou v jazyce CEL a umožňují filtrovat podle různých vlastností požadavku, nejčastěji pomocí IP adresy klienta a obdržených HTTP hlaviček.

conditions:
  is-bot-googlebot:
    - |
      (userAgent.contains("+http://www.google.com/bot.html") || userAgent.contains("Google-PageRenderer") ||
       userAgent.contains("Google-InspectionTool") || userAgent.contains("Googlebot"))
       && remoteAddress.network("googlebot")

Je vidět, že kromě testování hlavičky User-Agent je možné ověřit, že někdo, kdo předstírá Googlebota, opravdu přichází z rozsahu IP adres Googlebota.

Challenges (výzvy)

Challenges popisují různé výzvy, které je možné použít v pravidlech. Kromě javascriptové výzvy na proof-of-work je možné použít i jiné výzvy – je možné ověřit, že klient má podporu cookies, že podporuje HTTP/2 preload a podobně.

Zaujalo mě, že go-away umožňuje ověřovat požadavky i pomocí externího HTTP API, což otevírá možnosti pro integraci s jinými systémy.

Detailnější popis je v stránce Challenges v go-away-wiki. Ve výchozím obrazu kontejneru je už sada předdefinovaných výzev definována, takže je nemusíme vytvářet ručně. Laskavého čtenáře tedy odkážu do dokumentace.

Pravidla

Pravidla jsou základem konfigurace go-away  a určují, jak se má s požadavky nakládat. Jednotlivá pravidla obsahují název, podmínky, kde se mají aplikovat a případně i akci, kdy se mají aplikovat.

Nepodepsané požadavky procházejí pravidly shora dolů, dokud se nenajde pravidlo, které odpovídá. Jeho akce se potom aplikuje. Akcí může být povolení přístupu ( pass), zablokování přístupu ( block, drop či deny), popřípadě vyzvání k vyřešení výzvy ( challenge nebo  check).

Seznam dostupných akcí pro pravidla je ve wiki projektuUkázka zjednodušených pravidel:

rules:
  #
  # povolit přístup z vnitřních sítí
  - name: allow-internal
    conditions:
      - '($is-internal-ip)'
    action: pass

# povolit přístup z České republiky
  - name: allow-czech-users
    conditions:
      - '($is-czech-ip)'
    action: pass

# povolit přístup pro hodné roboty
  - name: allow-good-bots
    conditions:
      - '($is-good-bot)'
    action: pass

# zahodit požadavky od špatných robotů
  - name: drop-bad-bots
    conditions:
      - '($is-bad-bot)'
    action: drop

# ostatní uživatele vyzvat k vyřešení challenge
  - name: challenge-everyone-else
    action: challenge
    challenges:
      - js-pow-sha256

Jak to spustit

Protože go-away je napsaný v jazyce Go, je možné ho nasadit jako samostatnou binárku. Pro spuštění je potřeba navíc konfigurační soubor. Soubory s šablonami stránek s výzvami jsou zahrnuté už v binárce. Podrobný návod na instalaci je dostupný v go-away wiki.

Pro testování a produkci je pravděpodobně pohodlnější použít už hotový kontejnerový obraz ve vašem oblíbeném kontejnerovém prostředí. Obsahuje výchozí konfiguraci a připravené snippety konfigurace, takže je nemusíme připravovat ručně.

Výhoda obrazu kontejneru je v tom, že pro konfiguraci go-away je možné použít i proměnné prostředí, což usnadňuje nasazení třeba v Kubernetes.

Ukázka z nastavení proměnných prostředí pro spuštění go-away v kontejneru. Konfigurační soubory jsou namapovány do kontejneru z hostitelského systému do /app/ v kontejneru.

GOAWAY_BACKEND_IP_HEADER='X-Forwarded-For'
GOAWAY_BIND=':8080'
GOAWAY_CACHE='/tmp/go-away-cache'
GOAWAY_CLIENT_IP_HEADER='X-Forwarded-For'
GOAWAY_CONFIG='/app/go-away-config.yml'
GOAWAY_JWT_PRIVATE_KEY_SEED='1234560000000000000000000000000DEAD00BEEF00000000000000000000000'
GOAWAY_POLICY='/app/go-away-policy.yaml'

Podpora GeoIP

Nástroj go-away ve výchozí konfiguraci sice umí filtrovat podle zveřejněných rozsahů IP adres, ale nemá podporu pro GeoIP.

V našem případě jsme před go-away nasadili nginx s modulem geoip2, který přidává do HTTP hlaviček pro backend informace o geografické poloze klienta, v konfiguraci nginx jsou teď následující kousky:

proxy_set_header X-GeoIP-ASN             "$geoip2_asn";
proxy_set_header X-GeoIP-City-Name       "$geoip2_city";
proxy_set_header X-GeoIP-Continent-Code  "$geoip2_city_continent_code";
proxy_set_header X-GeoIP-Country-Code    "$geoip2_city_country_code";

Tím se do požadavků předaných go-away přidají hlavičky, a v pravidlech pak podle nich můžeme filtrovat. Ukázka hlaviček přidanýchnginx modulem geoip2 :

X-GeoIP-ASN: 906090
X-GeoIP-City-Name: Bruntál
X-GeoIP-Continent-Code: EU
X-GeoIP-Country-Code": CZ

V pravidlech pro go-away pak můžeme napsat pravidlo, které povolí přístup z České republiky:

conditions:
  # detect CZ and SK IPs
  is-czech-ip:
    - '"X-GeoIP-Country-Code" in headers && headers["X-GeoIP-Country-Code"] == "CZ"'
    - '"X-GeoIP-Country-Code" in headers && headers["X-GeoIP-Country-Code"] == "SK"'

is-european-ip:
    - '"X-GeoIP-Continent-Code" in headers && headers["X-GeoIP-Continent-Code"] == "EU"'

is-liberec:
    - '"X-GeoIP-City-Name" in headers && headers["X-GeoIP-City-Name"] == "Liberec"'

rules:
  # enable access from .CZ
  - name: allow-czech-locations
    conditions:
      - '($is-czech-ip)'
    action: pass

# add your next rules here

Metriky

Pomocník go-away umí exportovat metriky ve formátu Prometheus, což nám umožnilo sledovat počet vyřešených výzev a další statistiky. Metriky jsou dostupné na endpointu /metrics na samostatném portu, odkud je může sbírat Prometheus či jiný monitorovací systém.

V metrikách mě třeba zaujalo, že poměr robotů a živých uživatelů byl v našem případě přibližně 100:1, máte tedy stovku robotů na jednoho skutečného uživatele.

Různé

Utilita počítá s tím, že jedna instance go-away může chránit více webů na různých URL. V našem případě jsme nasadili tři instance go-away na třech různých strojích, které přehazují požadavky na něklik aplikačních serverů pro různé domény v pozadí.

Lehkou nevýhodou go-away je slabší dokumentace. Dobrá zpráva je, že k dispozici je zdrojový kód a méně pochopitelné části je možné prozkoumat přímo tam.

Mírnou nevýhodou je název softwaru. Klasický postup vhodit do vyhledávače „go-away example configuration“ nefunguje, protože vyhledávač vrací výsledky s textem „go away“ (jdi pryč).

Školení Linux

Jednoduché a účinné řešení

V tento okamžik nám go-away vyřešilo problém s roboty a zároveň povolilo přístup pro existující skutečné uživatele na cestách.

Předpokládáme, že roboti se ochranu časem naučí obcházet, ale v tento okamžik jsme spokojeni. Protože jde o jednoúčelovou izolovanou komponentu v celém stacku, počítáme s tím, že v případě potřeby ji vyměníme za jinou modernější alternativu. V mezičase mají vývojáři čas na to, aby se zamysleli nad optimalizací výkonu aplikace.

Autor článku

Věroš Kaplan pracuje jako nájemný správce serverů na volné noze. Před několika lety objevil kouzlo automatizace a už ho to nepustilo. Zastává názor, že nudné úkoly mají dělat počítače – a měly by se tedy používat i pro správu dalších počítačů.