JWT tokeny, které vám uniknou: nejčastější chyby při zabezpečení API
Prvním krokem je vytvořit jednotný popis všech endpointů na jednom místě. Nejlépe ve formátu, který může backend rovnou generovat z kódu, a frontend si ho může stáhnout do svého vývojového prostředí. Vyhněte se ručně psaným dokumentům v textových editorech – ty rychle zastarávají a nikdo je neudržuje. Místo toho používejte nástroje, které popis API generují z anotací nebo z definic datových struktur. Díky tomu bude dokumentace vždy odpovídat skutečnému stavu aplikace, což je nejdůležitější podmínka pro to, aby jí lidé věřili a používali ji.
Když frontend a backend spolupracují na jednom produktu, nejčastějším zdrojem nedorozumění bývá nejasná nebo neúplná dokumentace API. Bez ní vývojář rozhraní neví, jaká data přesně endpoint vrací, jaké datové typy použít nebo jak zpracovat chybové stavy. Výsledkem jsou zbytečné konzultace, přepisování kódu a prodlužování času potřebného na integraci. Klíčem k hladké spolupráci je dokumentace, která je živá, konkrétní a používá společné pojmy.
Druhý typický problém je příliš mnoho testů na stejné úrovni, ale s duplicitním pokrytím. Pokud máte integrační test, který ověřuje zápis do databáze, a zároveň end-to-end test, který dělá to samé přes API, jen plýtváte časem. Každá úroveň má jiný účel. Jednotkové testy chytají chyby v logice, integrační testy kontrolují, že spolu komponenty komunikují správně, a end-to-end testy ověřují, že uživatel projde aplikací bez zaváhání. Než napíšete nový test, zeptejte se: „Jakou chybu chci chytit?" Pokud odpověď je „nevím", test je zbytečný.
Nakonec se vyplatí do dokumentace přidat i praktické interaktivní prostředí, kde si frontend může zavolat API přímo z prohlížeče. Nemusí to být nic složitého – stačí možnost zadat parametry a zobrazit odpověď. Až frontend narazí na nejasnost, místo psaní e-mailu si všechno vyzkouší sám. Taková dokumentace se stává nástrojem, ne přítěží. Pokud se navíc pravidelně kontroluje a aktualizuje při každé změně kódu, spolupráce se výrazně zrychlí a počet chyb klesne na minimum. Důležité je, aby dokumentaci vnímal jako svůj úkol celý tým, nejen backend.
Základní pravidlo je jednoduché: čím nižší úroveň testu, tím více jich má být. Na dně pyramidy leží jednotkové testy, které ověřují jednu funkci nebo metodu izolovaně. Měly by být rychlé (v řádu milisekund), deterministické a neměly by vyžadovat žádnou infrastrukturu. Nad nimi jsou integrační testy, které ověřují spolupráci více komponent – typicky práci s databází, externími službami nebo souborovým systémem. Těch by mělo být výrazně méně, ale stále dost na to, aby pokryly hlavní scénáře propojení. Na vrcholu jsou end-to-end testy, které procházejí celou aplikací přes uživatelské rozhraní. Těch je nejméně, protože jsou pomalé, křehké a jejich údržba stojí hodně času.
Prvním krokem je správná volba algoritmu. Mnoho vývojářů sáhne po algoritmu HS256, protože je jednoduchý, ale vyžaduje sdílený tajný klíč mezi serverem a klientem. To je problém: jakmile klíč unikne, útočník může podepisovat vlastní tokeny. Bezpečnější je použít asymetrický algoritmus RS256 nebo ES256, kde máte privátní klíč pouze na serveru a veřejný klíč distribuujete klientům. Tím eliminujete riziko spojené s kompromitací tajemství. Pokud už ale používáte HS256, dbejte na to, aby klíč byl dostatečně dlouhý a náhodný – minimálně 32 bajtů, ideálně více.
Další častou chybou je ignorování expirace tokenu. JWT obsahuje pole exp, ale pokud ho nenastavíte nebo nastavíte příliš dlouhou platnost, otevíráte dveře útočníkům, kteří ukradnou token a používají ho týdny. Nastavte expiraci na rozumnou dobu – obvykle 15 minut až několik hodin – a pro delší přístup použijte obnovovací tokeny, které mají vlastní životní cyklus a lze je bezpečně zneplatnit. Navíc vždy ověřujte nejen expiraci, ale i čas vydání (iat) a případně čas nepoužitelnosti (nbf), abyste zabránili použití tokenů, které ještě nebyly aktivovány.
Kde nejčastěji pyramida padá a jak to napravit Nejčastější chybou je obrácená pyramida – když máte stovky end-to-end testů a jen pár jednotkových. Tým pak tráví více času opravováním testů než vývojem funkcí. Příčinou bývá snaha testovat všechno přes uživatelské rozhraní, protože „to je nejvěrnější obraz toho, co uživatel vidí". Jenže takový přístup ignoruje, že každý end-to-end test je pomalý a náchylný k selhání kvůli načasování, animacím nebo změnám v rozvržení. Řešení není testy mazat, ale přesunout většinu scénářů na nižší úrovně. Logiku, která se skrývá za formulářem, otestujte na úrovni jednotek nebo integrace. End-to-end testy si nechte jen na kritické cesty – přihlášení, platbu nebo registraci.