Kód, který jsi nenapsal, nesmí tajemství ani vidět
Vygenerovaná aplikace potřebuje sáhnout do uživatelovy databáze. Nejjednodušší řešení je zapsat mu OAuth token do prostředí. Tím ale dáváš dlouhodobé přihlašovací údaje kódu, který napsal jazykový model podle zadání, jež mohl někdo cestou podstrčit. Stačí jedna injektáž nebo jedna nešťastná větev a token odteče.
Vercel to u v0 vyřešil posunutím hranice: token se do sandboxu nikdy nedostane. Doplňuje se až v proxy, která stojí vně a kterou vygenerovaný kód nemůže obejít.
Jak se hranice drží
Sandbox běží s vlastní síťovou izolací a jeho firewall ukončuje TLS certifikátem, který je pro každý sandbox jiný. Díky tomu proxy do provozu vidí, i když klient používá běžné SDK a o žádné proxy neví. Nic se nemusí přepisovat.
V souboru s přihlašovacími údaji uvnitř sandboxu leží placeholder pevné délky 72 bajtů. Existuje jen proto, aby SDK prošlo validací formátu. Sám o sobě neotevírá nic.
Doplnění pak probíhá podle typu požadavku. U volání SQL API sedí token do hlavičky Authorization: Bearer. U přihlašovacího požadavku proxy rozparsuje JSON tělo a vloží ho do pole, které je v té struktuře určené pro autentizaci. Po přihlášení už se nedoplňuje nic, protože dál se pracuje se session tokenem, který vydal Snowflake sám.
Proč nestačí nahradit řetězec
Tady leží ta část, kterou je snadné přehlédnout. Nabízí se implementace o jedné řádce: najdi v odchozím požadavku placeholder a nahraď ho skutečným tokenem.
Tím ale ze své proxy uděláš orákulum. Vygenerovaný kód nemusí token číst — stačí, aby ho někam napsal a nechal si výsledek vrátit. Postavíš dotaz, do kterého placeholder vloží jako textový literál, proxy ho poslušně vymění za pravý token a databáze ti ho vrátí ve výsledku. Únik proběhl bez jediného přístupu k tajemství.
Obrana proto není lepší hledání, ale jiný kontrakt. Doplňuje se jen na místech určených strukturou požadavku, a pokud se placeholder objeví kdekoli jinde, požadavek se odmítne. Ne opraví, odmítne. Jeho výskyt mimo autentizační pole není chyba formátování, je to signál, že se ho někdo pokusil použít jako nástroj.
Stejná logika platí pro cíl. Adresa účtu se odvozuje na straně serveru z toho, kdo je přihlášen, nikoli z toho, co poslal sandbox. Jinak by stačilo poslat požadavek na vlastní doménu a nechat si token doručit. K tomu limity na velikost požadavku, aby nešlo parser vyčerpat nebo zmást.
Obecný tvar
Snowflake je tu jen kulisa. Vzor sedí na jakoukoli situaci, kdy má nedůvěryhodný kód mluvit s cizí službou na dlouhodobé přihlašovací údaje: agentní pluginy, uživatelské skripty, integrace třetích stran.
Tvar je vždycky stejný. Tajemství drž mimo prostor, který nedůvěryhodný kód řídí. Vyměň ho co nejpozději a co nejblíž cíli. Vyměňuj podle struktury protokolu, ne podle výskytu textu. A dohlédni na to, že adresu protistrany neurčuje ten, komu nevěříš. Pokud se dá tajemství vyměnit za životnost jedné session, udělej to a dál se opírej o ni.
Co si z toho odnést
Otázka u vygenerovaného kódu není, jak mu zabránit v úniku tajemství, ale jak zařídit, aby žádné neměl. Přesuň hranici a problém zmizí; obcházet ho filtrováním výstupů znamená vyhrávat závod, ve kterém stačí prohrát jednou.
Když už tajemství někde doplňuješ, nikdy to nedělej vyhledáním a nahrazením v celém požadavku. Textové nahrazení nerozliší, kdo si o dosazení řekl, a tím z tvé infrastruktury udělá nástroj útočníka. Doplňuj do pole, které v protokolu znamená autentizaci, a všechno ostatní ber jako pokus o zneužití.