Publikováno
· Autor: Zdeněk Hejzlar · Pod kapotou · Bezpečnost · 3 min čtení
Obsah článku
SQL injection a XSS patří mezi nejstarší útoky na weby a přesto na ně narážím pořád. Oba figurují v žebříčcích nejčastějších zranitelností už roky a oba vznikají ze stejné chyby: aplikace slepě důvěřuje vstupu od uživatele. Dobrá zpráva je, že obrana proti oběma je jednoduchá, když víte, kde je slabina. Ukážu na kódu, jak vznikají a jak se jim v PHP i moderním frameworku jako Laravel spolehlivě bránit.
Jak vzniká SQL injection
SQL injection nastane, když uživatelský vstup skládáte přímo do databázového dotazu. Útočník pak místo jména pošle kousek SQL a ovládne databázi.
// ZRANITELNÉ: vstup jde přímo do dotazu
$email = $_GET['email'];
$sql = "SELECT * FROM users WHERE email = '$email'";
// Útočník pošle: ' OR '1'='1
// a vrátí se mu všechny řádky
Obrana: parametrizované dotazy
Řešení je jednoduché — vstup nikdy nespojujte s dotazem, předávejte ho jako parametr. Laravel to přes Eloquent a query builder dělá za vás automaticky.
// Bezpečně přes Eloquent
$user = User::where('email', $request->email)->first();
// Bezpečně přes query builder s parametrem
$users = DB::select(
'SELECT * FROM users WHERE email = ?',
[$request->email]
);
- Otazník je zástupný symbol, hodnota se doplní bezpečně.
- Přes Eloquent se o parametrizaci nemusíte starat vůbec.
- I u řazení a názvů sloupců hlídám, aby nešly přímo od uživatele.
- Základní práci s modely popisuji v častých chybách začátečníků v Laravelu.
Pozor na jednu pastičku: parametry chrání hodnoty, ale ne názvy sloupců nebo směr řazení. Pokud tyhle části dotazu sestavujete z uživatelského vstupu, musíte je ověřit proti pevnému seznamu povolených hodnot, jinak si zaděláváte na stejný problém jinými dveřmi.
Jak vzniká XSS
Cross-site scripting nastane, když do stránky vypíšete neošetřený vstup a prohlížeč ho spustí jako kód. Útočník tak může krást přihlašovací údaje, číst obsah stránky nebo návštěvníka nenápadně přesměrovat na podvodný web. Zákeřné na tom je, že vstup nemusí přijít jen z formuláře — může být uložený v databázi z dřívějška a spustit se každému, kdo stránku otevře.
// ZRANITELNÉ v čistém PHP
echo "<p>" . $_GET['jmeno'] . "</p>";
// Útočník pošle: <script>ukradniCookie()</script>
Obrana: escapování výstupu
Každý výstup, který pochází od uživatele, je potřeba escapovat. Blade v Laravelu to dělá u dvojitých složených závorek automaticky.
{{-- Blade escapuje automaticky --}}
<p>{{ $jmeno }}</p>
// V čistém PHP escapuj ručně
echo '<p>' . htmlspecialchars($jmeno, ENT_QUOTES) . '</p>';
- Syntaxi
{!! !!}používám jen na data, kterým opravdu věřím. - Vstup navíc validuji přes Form Request.
Ještě dodám jednu vrstvu navíc: hlavičky Content Security Policy dokážou omezit, co smí prohlížeč vůbec spustit, a tím XSS ztíží i v případě, že by se něco proklouzlo. Není to náhrada za escapování, ale pojistka navíc, kterou u citlivějších projektů rád nasazuji. Na jednoduchém webu je to naopak zbytečná komplikace, kterou byste jen udržovali.
Obě zranitelnosti nakonec odrazí jedno pravidlo: nikdy nevěřte vstupu a vždy ošetřete výstup. Když se ho držíte důsledně, máte devadesát procent práce hotovo. Chcete prověřit svůj kód? Napište mi na nezávaznou poptávku.