Takévalidace formuláře, ověřování formulářových dat, client-side validace, server-side validaceZákladní
Definice
Formulářová validace je kontrola, zda data zadaná uživatelem do formuláře odpovídají očekávanému formátu, rozsahu a obchodním pravidlům, ještě než je aplikace zpracuje nebo uloží. Probíhá ve dvou vrstvách: v prohlížeči kvůli rychlé zpětné vazbě a na serveru kvůli bezpečnosti a integritě dat, protože klientskou kontrolu lze obejít.
Dvě vrstvy, které nejsou zaměnitelné
Formulářová validace se odehrává na dvou místech a každé z nich řeší jiný problém. Kontrola v prohlížeči existuje kvůli uživateli: chybu ukáže okamžitě, ušetří zbytečný kolotoč požadavků a sníží počet nedokončených odeslání. Kontrola na serveru existuje kvůli aplikaci: data přicházejí z HTTP požadavku, který může sestavit kdokoli pomocí curl, Postmanu nebo upraveného HTML v konzoli prohlížeče. Klientská validace je tedy služba uživateli, serverová je hranice důvěry.
Z toho plyne pravidlo, které se v praxi porušuje překvapivě často: každé pravidlo vynucené v prohlížeči musí mít protějšek na serveru. Opačně to neplatí, některé kontroly (unikátnost e-mailu, dostupnost skladové položky, platnost slevového kódu) v prohlížeči smysluplně provést nelze bez dotazu na backend.
Co umí prohlížeč sám
HTML poskytuje deklarativní sadu omezení, tzv. constraint validation: atributy required, type, min, max, minlength, maxlength, step a pattern. Prohlížeč z nich odvodí stav validity každého pole a odesílání zablokuje. JavaScriptové API doplňuje objekt validity, metodu checkValidity() a setCustomValidity() pro vlastní hlášky. CSS pseudotřídy :invalid, :valid a :user-invalid umožňují stav ostylovat.
Nativní hlášky mají ale omezení: text určuje prohlížeč, lokalizace se řídí jazykem prohlížeče, ne stránkou, a vzhled bubliny nelze plně ovlivnit. Proto většina produkčních aplikací nativní zprávy vypne přes novalidate a vykresluje vlastní chyby, přičemž samotná pravidla nechá v HTML jako zdroj pravdy.
Kdy chybu ukázat
Načasování rozhoduje o tom, jestli validace pomáhá, nebo obtěžuje. Hlásit chybu při každém stisku klávesy znamená křičet na člověka, který teprve píše první písmeno e-mailu. Osvědčený vzorec: první kontrola pole až po opuštění (blur) nebo po prvním pokusu o odeslání, další už průběžně, aby uživatel viděl, že se chyba opravou vyřešila. Chybová hláška patří vizuálně i programově k poli (aria-describedby, aria-invalid) a má říkat, co s tím: „Datum zadejte ve tvaru DD.MM.RRRR“ místo „Neplatná hodnota“. Souhrn chyb nad formulářem s odkazy na jednotlivá pole pomáhá u dlouhých formulářů a je nutný pro čtečky obrazovky. Kvalitní validace je tak z velké části otázka použitelnosti, ne regulárních výrazů.
Validace versus sanitizace
Validace rozhoduje, zda hodnotu přijmout. Sanitizace hodnotu mění (ořezání mezer, normalizace telefonního čísla). Ani jedna nenahrazuje escapování při výstupu: obrana proti XSS se dělá při vykreslení, obrana proti SQL injection parametrizovaným dotazem. Přísný regulární výraz na e-mail není bezpečnostní opatření a zároveň běžně odmítne platné adresy. Bezpečnostní přínos validace spočívá jinde: v allowlistu povolených hodnot u výčtů, v omezení délky a v typové kontrole vstupu, která zúží prostor pro útok. Podrobněji viz heslo zranitelnost.
Příklady z praxe
Registrační formulář s nativními omezeními
Registrace vyžaduje e-mail a heslo o délce alespoň dvanáct znaků. HTML atributy zajistí, že prohlížeč odesílání zablokuje a pole označí jako neplatné, takže uživatel nečeká na odpověď serveru. Server přesto stejná pravidla zopakuje a navíc ověří, že e-mail v databázi ještě není.
<form action="/registrace" method="post"> <label for="email">E-mail</label> <input id="email" name="email" type="email" required autocomplete="email" aria-describedby="email-err"> <p id="email-err" hidden>Zadejte adresu ve tvaru jmeno@domena.cz.</p> <label for="heslo">Heslo</label> <input id="heslo" name="heslo" type="password" minlength="12" required autocomplete="new-password"> <button type="submit">Vytvořit účet</button> </form>Obejití klientské kontroly a serverové schéma
Útočník otevře konzoli, odstraní atribut required a odešle prázdné pole. Nebo formulář vůbec nepoužije a pošle požadavek přímo. Server proto vstup ověřuje vlastním schématem a při chybě vrací stav 422 se seznamem polí, který si frontend zobrazí u příslušných inputů.
const schema = z.object({ email: z.string().email(), heslo: z.string().min(12), vek: z.coerce.number().int().min(18).max(120), }); const result = schema.safeParse(await req.json()); if (!result.success) { return Response.json( { chyby: result.error.flatten().fieldErrors }, { status: 422 } ); }
Časté omyly
- MýtusKdyž mám validaci v Reactu, na serveru už ji dělat nemusím.
- Ve skutečnostiValidace v prohlížeči je pouze UX vrstva a lze ji obejít vypnutím JavaScriptu, úpravou DOM nebo přímým HTTP požadavkem na API. Server musí kontrolovat každý vstup znovu, jinak se do databáze dostanou hodnoty, se kterými aplikace nepočítá.
- MýtusFormulářová validace chrání před SQL injection a XSS.
- Ve skutečnostiValidace zužuje prostor pro útok, ale skutečnou obranou jsou parametrizované dotazy na straně databáze a escapování při vykreslení výstupu. Data mohou být zcela validní a přesto obsahovat řetězec nebezpečný v jiném kontextu.
- MýtusExistuje jeden správný regulární výraz na e-mailovou adresu.
- Ve skutečnostiSyntaxe e-mailových adres podle RFC je natolik volná, že úplný regulární výraz je nepoužitelně složitý a přísné výrazy odmítají platné adresy (plusové aliasy, nové TLD, diakritika). Praktickou kontrolou je hrubý formát plus ověřovací e-mail.
Časté dotazy
- Jak lokalizovat chybové hlášky formulářové validace do češtiny?
- Nativní hlášky prohlížeče se řídí jazykem nastaveným v prohlížeči, nikoli atributem lang na stránce, takže je česky nezobrazíte spolehlivě. Řešením je přidat formuláři atribut novalidate, ponechat validační atributy v HTML jako zdroj pravdy a chyby vykreslovat vlastním kódem z objektu validity. Alternativou je setCustomValidity, kterou nastavíte vlastní text konkrétnímu poli, ale vzhled bubliny stále určuje prohlížeč. U vícejazyčných aplikací se texty obvykle drží v překladovém slovníku a sdílejí se mezi klientem a serverem.
- Kdy validovat průběžně při psaní a kdy až po odeslání?
- Validace při každém stisku klávesy hlásí chybu dřív, než uživatel stihne pole dopsat, což působí agresivně. Doporučený vzorec: pole poprvé zkontrolovat po jeho opuštění nebo po prvním pokusu o odeslání, a teprve poté hlásit chyby průběžně, aby bylo vidět, že oprava zabrala. Výjimkou jsou pole s okamžitou zpětnou vazbou, například indikátor síly hesla nebo počítadlo zbývajících znaků, kde průběžná reakce dává smysl. CSS pseudotřída :user-invalid tento vzorec do jisté míry řeší nativně.
- Jak zpřístupnit chyby formuláře čtečkám obrazovky?
- Chybové hlášky musí být svázané s polem atributem aria-describedby a neplatné pole označené aria-invalid="true". Samotná hláška by měla být textem, ne pouze červeným rámečkem nebo ikonou, protože barva není informace dostupná všem uživatelům. U delších formulářů se nad formulář vkládá souhrn chyb v živé oblasti s odkazy na jednotlivá pole a fokus se po neúspěšném odeslání přesune na první chybné pole. Popisek pole vždy propojte s inputem přes label a atribut for.
- Jak validovat pole, jejichž pravidla závisí na sobě?
- Podmíněná pravidla, například shodu dvou hesel, povinnou adresu jen při dopravě na adresu nebo datum konce po datu začátku, deklarativní HTML atributy nepokryjí. Taková pravidla se řeší na úrovni celého formuláře: validační schéma dostane celý objekt hodnot a chybu přiřadí ke konkrétnímu poli. Knihovny typu Zod nebo Yup k tomu mají refine a test. Klíčové je vracet chyby ve stejné struktuře jako u jednoduchých polí, aby je frontend uměl zobrazit u správného inputu bez zvláštní větve kódu.
Zdroje
- Client-side form validation(otevře se v novém okně)
- HTML Standard: Forms(otevře se v novém okně)
- OWASP Cheat Sheet Series: Input Validation(otevře se v novém okně)
- Web Content Accessibility Guidelines (WCAG) 2.2(otevře se v novém okně)