Bezpečnosť odosielania (SSRF)
Odchádzajúce URL adresy pochádzajú od vašich používateľov. Bez ochrany by https://169.254.169.254/… alebo meno, ktoré sa preloží do vašej privátnej siete, urobili z vašich workerov proxy k interným systémom. Ochranu zabezpečuje Safe Fetch for Laravel; Webhooks ju zapája pri uložení aj pri každom pokuse a kroku presmerovania, pretože DNS sa medzi nimi môže zmeniť.
Pravidlá
Pravidlá vychádzajú zo SafeFetch::policy() vašej aplikácie, pričom blokovania zostanú (blokovaní hostitelia a rozsahy, hostitelia aplikácie) a výnimky sa zahodia — privátni hostitelia, povolené rozsahy a zoznam povolených hostiteľov slúžia vašim vlastným požiadavkám, nie endpointom zákazníkov — a potom sa uplatnia webhooks.ssrf.* a webhooks.outbound.*. Ide o postup, ktorý Safe Fetch odporúča balíkom. Blokovanie má prednosť pred povolením:
| Pravidlo | Nastavenie |
|---|---|
| Len https; pre lokálny vývoj aj http | outbound.allow_http |
| Len kanonické URL: bez userinfo, bez % / spätnej lomky / medzier v autorite, bez číselných zápisov IPv4 (2130706433, 0x7f.1, 0177.0.0.1, 127.1), IDN cez UTS #46 (ext-intl) | — |
| Každá preložená adresa (A + AAAA) verejná — loopback, RFC 1918, link-local vrátane 169.254.169.254, CGNAT vrátane 100.100.100.200, Azure 168.63.129.16, ULA vrátane fd00:ec2::254, multicast, rezervované, dokumentačné a IPv6 s vloženou IPv4 sa odmietnu; jedna privátna odpoveď zablokuje celé meno | — |
| Odmietnuté podľa mena pred prekladom: localhost, *.localhost, vlastní hostitelia aplikácie, ssrf.blocked_hosts | ssrf.block_app_hosts |
| Ďalšie blokované CIDR (vždy vyhrávajú) | ssrf.blocked_ranges |
| CIDR vyňaté z tabuľky privátnych sietí | ssrf.allowed_ranges |
| Prísny zoznam povolených hostiteľov — povolený hostiteľ sa aj tak preloží, overí voči blokovaným rozsahom a pripne | ssrf.allowed_hosts |
| Porty | ssrf.allowed_ports |
Pri uložení
So zapnutým outbound.verify_on_save vytvorenie alebo úprava endpointu prejde všetkými pravidlami vrátane prekladu DNS; pri vypnutom všetkými okrem prekladu. V oboch prípadoch sa uloží kanonická URL. Odmietnutá alebo nepreložiteľná URL vyhodí DestinationBlockedException s jednou správou pre oba prípady — kto URL zadal, nezistí nič o internom DNS — kým reason(), blockedBy() a unresolved() povedia rozdiel vášmu kódu a prevádzke:
use RoundlyConsulting\Webhooks\Exceptions\DestinationBlockedException;
try {
Webhooks::for($team)->outbound()->create($request->validated('url'), events: ['invoice.*']);
} catch (DestinationBlockedException $e) {
// one message for refused and unresolvable hosts — no internal-DNS oracle for whoever typed the URL
$e->reason(); // safe-fetch's Reason, for your code and your operators
$e->blockedBy(); // the rule, or null
$e->unresolved(); // true when the host did not resolve
}Pri každom pokuse
- Požiadavka prechádza cez SafeFetch::guard() s pravidlami — cez manažér, nikdy cez makro Http::safe(), ktoré by mohol nahradiť iný balík — a obmedzenie je pridané pred ním, takže na každý pokus beží jedno obmedzenie ešte pred prekladom DNS.
- Pri každom kroku ochrana overí URL a meno preloží raz, pripne spojenie na overené adresy (CURLOPT_RESOLVE, nové spojenie, bez opätovného použitia, bez proxy z prostredia), vynúti vlastný curl handler, preruší spojenie mimo overenej množiny pred odoslaním prvého bajtu a pripojenú adresu skontroluje aj potom.
- Zablokovaný cieľ doručenie natrvalo zlyhá (blocked, uložené s pravidlom, napríklad blocked (private_address)); nepreložiteľný hostiteľ sa opakuje (unresolved). Pripojená adresa sa zaznamená k pokusu.
Presmerovania
Predvolene vypnuté: 3xx je neúspešný pokus (redirect_not_followed, opakuje sa). S outbound.follow_redirects sa sledujú len 307 a 308 — 301, 302 a 303 by z POST urobili GET — najviac outbound.max_redirects (3), nikdy https → http (redirect_downgrade), každý krok sa znova overí a pripne v rámci jedného časového rozpočtu. Vlastné hlavičky a prihlasovacie údaje idú len na prvý origin; hlavičky webhook-id, webhook-timestamp a webhook-signature idú na každý krok.
Lokálny vývoj
Ak chcete doručovať príjemcovi na vlastnom počítači, povoľte http a potrebnú loopback adresu — nikdy v produkcii:
WEBHOOKS_ALLOW_HTTP=on// config/webhooks.php
'ssrf' => ['allowed_ranges' => ['127.0.0.1/32'], /* … */],WEBHOOKS_SSRF_GUARD=off ochranu úplne odstráni — bez pripnutia a bez overenia; presmerovania sa stále nesledujú — a patrí len za filtrujúcu výstupnú proxy, ktorej dôverujete; webhooks:doctor upozorní. Limit odpovede platí v oboch prípadoch: bez ochrany ide odpoveď cez sink, ktorý ponechá outbound.max_response_bytes a zvyšok zahodí. Odosielanie cez výstupnú proxy pri zapnutej ochrane nie je podporované. Ochrana potrebuje ext-curl; vlastný Guzzle handler pre celú aplikáciu ju neobíde.
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 kryptomienOdoslaní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.