NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky
Safe Fetch for Laravel

Chránené požiadavky

Http::safe() chráni akúkoľvek HTTP požiadavku Laravelu. URL adresa sa prestavia z prísneho parsovania, posúdi sa každá adresa, na ktorú sa hostiteľ preloží, spojenie sa cez curl pripne na tieto adresy bez proxy a bez opätovne použitého spojenia a skontroluje sa adresa, na ktorú sa curl skutočne pripojil:

Http::safe()->post($url, $payload);                                          // the configured policy
Http::safe(SafeFetch::policy()->followRedirects())->post($url, $payload);    // a policy for this call
Http::withToken($token)->safe()->post($url, $payload);                       // chains either way
SafeFetch::guard(Http::withToken($token)->timeout(10), $policy)->post($url); // the manager, no macro
SafeFetch::http($policy)->get($url);                                         // a new guarded request

Čo ochrana s požiadavkou urobí

  • Bez curl ju odmietne (cannot_pin, ešte pred zostavením — fail closed).
  • Odmietne požiadavku, ktorá už má vlastného klienta (setClient()), Guzzle voľbu handler (Http::globalOptions(), withOptions() pred alebo po safe()) alebo ktorá už je chránená — UnsafeRequestOptions.
  • Nastaví vlastný pripnutý handler — vždy curl, nikdy stream handler (ten by pripnutie ignoroval), nikdy handler aplikácie.
  • Vypne vlastné presmerovania Guzzle; presmerovania riadi ochrana.
  • Pridá middleware ochrany, ktorý si overenie každého kroku vyžiada od manažéra, takže SafeFetch::fake() vidí všetky kroky.
  • Odosiela cez vlastného klienta, ktorý vidí konečné voľby každej požiadavky skôr než Guzzle — handler pridaný po safe() sa odmietne pri odoslaní.

Pri každom kroku

  • URL adresa sa overí (rovnako ako SafeFetch::vet()) — odmietnutie požiadavku zamietne a nič sa neodošle.
  • Požiadavka ide na prestavanú URL; pri kroku na iný origin, než bol prvý, sa zahodia prihlasovacie údaje (pozrite Presmerovania).
  • Pridajú sa voľby pripnutia, ktoré majú prednosť pred voľbami volajúceho (nižšie). Ak z rozpočtu zostáva menej než milisekunda: DestinationUnavailable (timeout).
  • Pripnutý handler tesne pred curl požiadavku znovu skontroluje: schéma, hostiteľ a port sú overené, hlavička Host uvádza toho istého hostiteľa a port, proxy je vypnutá, presmerovania sú vypnuté, pripnutia aj kontrola pred odoslaním sú nedotknuté a nepribudla žiadna voľba curl mimo zoznamu povolených. Čokoľvek iné je cannot_pin a nič sa nevytočí.
  • Kontrola pred odoslaním (CURLOPT_PREREQFUNCTION): keď sa curl pripojí, a ešte pred odoslaním prvého bajtu, musí byť pripojená adresa a port v overenej množine; inak curl spojenie preruší → cannot_pin. Server uvidí spojenie, ale nič v ňom.
  • Kontrola po odoslaní: primary_ip zo štatistík prenosu musí byť v overenej množine; inak cannot_pin, nahlásené a oznámené ako DestinationRefused. Štatistiky z Http::fake() sa nekontrolujú. Váš vlastný on_stats sa zavolá aj tak.
  • Presmerovanie, ktoré pravidlá sledujú, sa stane ďalším krokom.
[
    'proxy' => '',                    // no proxy at all, environment proxies included
    'allow_redirects' => false,       // redirects are the guard's
    'protocols' => [$scheme],         // Guzzle 7.11+; older 7.x ignore it (the scheme is vetted)
    'curl' => [
        CURLOPT_RESOLVE => ['host:port:a.b.c.d,[v6::1]'],  // every vetted address, one entry; none for a literal
        CURLOPT_FRESH_CONNECT => true,
        CURLOPT_FORBID_REUSE => true,
        CURLOPT_PREREQFUNCTION => $preSendCheck,         // libcurl 7.80+, not Guzzle 7.12.x
    ],
    'timeout' => <what is left of the budget>,
]

Voľby, ktoré ochrana odmietne

UnsafeRequestOptions — chyba programátora, vyhodená výnimkou s názvom voľby — pre čokoľvek, čo by požiadavku vyviedlo mimo ochranu:

  • allow_redirects s inou hodnotou než false (->maxRedirects() po safe()) — presmerovania zapnite v pravidlách.
  • Proxy zvolená volajúcim (withOptions(['proxy' => …]), Http::globalOptions()). Proxy, ktoré len opakujú prostredie (HTTP_PROXY, HTTPS_PROXY, NO_PROXY), sa obídu, neodmietnu: proxy si meno prekladá sama.
  • transport_sharing na úrovni požiadavky.
  • Surová voľba curl mimo zoznamu povolených: CURLOPT_ENCODING, HTTPAUTH, USERPWD, SSL_CIPHER_LIST, TLS13_CIPHERS, SSL_EC_CURVES, CERTINFO, LOW_SPEED_LIMIT, LOW_SPEED_TIME, TCP_KEEPALIVE, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT, TCP_NODELAY, TCP_FASTOPEN, HAPPY_EYEBALLS_TIMEOUT_MS, MAXAGE_CONN a MAXLIFETIME_CONN. Všetko ostatné — RESOLVE, CONNECT_TO, UNIX_SOCKET_PATH, INTERFACE, DNS_*, PROXY*, PRE_PROXY, SSL_VERIFYPEER … — zlyhá bezpečne.
  • sink spolu s maxResponseBytes.
  • Vlastný klient (setClient()), Guzzle voľba handler alebo požiadavka, ktorá už je chránená.

Odmietnutia ochrany (DestinationRejected, DestinationUnavailable) prídu nezabalené — Laravel z nich nespraví RequestException ani ConnectionException. Výnimka z kódu vnútri ochrany (StrayRequestException z Http::fake(), rate limiter pridaný po safe(), uvoľnenie úlohy vo fronte) sa k volajúcemu dostane tak, ako bola vyhodená. Chyba prenosu chránenej požiadavky je vlastná ConnectionException Laravelu, ktorej správa obsahuje URL adresu — nikdy ju nezobrazujte koncovému používateľovi.

Pooly

Ochrana nahradí handler poolu svojím pripnutým handlerom — jedným na pool, spoločným pre jeho chránené požiadavky — takže pool zostane súbežný, pričom nové spojenia a kontrola pred odoslaním pri každom kroku udržia každú požiadavku v jej vlastnej overenej množine. Dve požiadavky na rovnakého hostiteľa a port s rôznymi overenými množinami sa odmietnu, nezmiešajú. Výsledkom odmietnutej požiadavky je výnimka:

use Illuminate\Http\Client\Pool;

$responses = Http::pool(fn (Pool $pool): array => [
    $pool->safe()->get('https://hooks.example/a'),
    $pool->safe()->get('https://inward.example/b'),
]);

$responses[0]->body();   // the answer
$responses[1];           // a DestinationRejected when the guard refused that member

Opakovanie

retry() prechádza zásobníkom znovu, takže sa overí každý pokus. Laravel opakuje pri každej výnimke, ak callback when nepovie inak — aj pri odmietnutí — preto skončite pri prvom:

Http::safe()->retry(3, 100, fn (Throwable $e) => ! $e instanceof SafeFetchException)->post($url, $payload);

Ďalší middleware

Poradie je na vás. Obmedzovač pridaný pred ochranu beží raz na volanie, ešte pred prekladom DNS; pridaný po safe() beží pri každom kroku. Uprednostnite prvé:

// Runs once per call, before any DNS lookup — prefer this order
Http::withMiddleware($throttle)->safe()->post($url, $payload);

// http-client-rate-limits-for-laravel's macro works the same way
Http::rateLimit(30)->safe()->post($url, $payload);

Limit odpovede — maxResponseBytes

Keďže ochrana vynúti curl, stream => true už nestreamuje: curl stiahne celé telo skôr, než ho uvidíte. Na obmedzenie odpovede nastavte limit v pravidlách:

$response = Http::safe(SafeFetch::policy()->maxResponseBytes(4096))->post($url, $payload);

$response->status();                       // the real status
$response->body();                         // at most 4 096 bytes
SafeFetch::truncated($response);           // true when the body was cut
$response->handlerStats()['primary_ip'] ?? null; // the address it connected to, truncated or not
  • Telo dlhšie než limit sa oreže a prenos preruší — nech ho server posiela akokoľvek (bez Content-Length, po častiach, komprimované: počítajú sa dekódované bajty).
  • Odpoveď si ponechá stav aj hlavičky a pribudne jej Safe-Fetch-Truncated: 1. Telo presne vo veľkosti limitu značku nedostane.
  • Ochrana z každej odpovede odstráni hlavičku Safe-Fetch-Truncated poslanú serverom, s limitom aj bez neho, takže ju nastaví len vlastné orezanie ochrany.
  • Sink patrí ochrane: sink volajúceho spolu s limitom sa odmietne. fetch() a download() majú vlastný limit, ktorý namiesto orezania zlyhá.

Pripnutie požiadavky, ktorú si zostavíte sami

$vetted = SafeFetch::vet($url);
Http::withOptions($vetted->pinningOptions())->get($vetted->url);

pinningOptions() je pole volieb vyššie bez kontroly pred odoslaním; spolieha sa, že handler voľby curl dodrží, a žiadny krok sa znovu nekontroluje. Uprednostnite SafeFetch::guard().

Kompatibilita s Guzzle

illuminate/http 13 povoľuje guzzlehttp/guzzle ^7.8.2 || ^8.0. Voľby pripnutia používajú len to, čo prijíma každá z týchto verzií:

GuzzlePoznámky
7.8.2Spodná hranica, testovaná vo vetve prefer-lowest. Bez voľby protocols (ignoruje sa; schéma sa aj tak overuje). Composer predvolene blokuje vydania Guzzle s bezpečnostnými upozorneniami — na takom Guzzle odporúčame aktualizovať.
7.12.0–7.12.3CURLOPT_PREREQFUNCTION je tam označená ako zastaraná (opravené v 7.13.0), preto sa kontrola pred odoslaním preskočí; kontrola primary_ip po požiadavke zostáva.
7.16.xTestované; testy zlyhajú pri akomkoľvek upozornení na zastaranie, aj potlačenom cez @.
8.xTestované na každej verzii PHP (8.4, 8.5, 8.6).

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.