Slovník pojmů
SQL injection
SQL injection vzniká, když se vstup uživatele stane součástí SQL kódu. Parametry chrání hodnoty; názvy sloupců a řazení potřebují předem daný whitelist.
Stručná definice
Hodnota nesmí měnit strukturu dotazu.
SQL injection vzniká při skládání SQL řetězce z příkazu a neověřeného vstupu. Databáze pak nedokáže poznat, která část je původně zamýšlená hodnota a která SQL syntaxe. Chyba může vést k nečekanému čtení, změně nebo smazání dat podle práv databázového účtu aplikace.
Prepared statement s placeholderem předá databázi strukturu dotazu a hodnotu odděleně. Hodnota e-mailu, čísla objednávky nebo data proto nemění SQL příkaz. Validace vstupu stále pomáhá business pravidlům a srozumitelným chybám, ale není náhradou parametrizace.
Použití
Kde ochranu hlídat
Riziko se netýká jen veřejného formuláře; vstup může přijít z API, importu, administrace i interního skriptu.
- filtrování objednávek podle e-mailu, stavu nebo data
- vyhledávání produktů a exporty administrace
- API endpointy s parametry, stránkováním a řazením
- importy externích identifikátorů a integrační reporty
- ručně psané SQL, DQL fragmenty i dynamické query builder výrazy
Praktický příklad
Filtr objednávek a bezpečné řazení
Následující první řádek ukazuje chybu: e-mail by se přilepil do SQL textu. Bezpečná varianta posílá e-mail jako parametr. Směr řazení a název sloupce se naopak parametrizovat nedají, proto aplikace použije mapu předem povolených voleb a výchozí hodnotu.
Ukázka používá Doctrine DBAL jen pro názornost. Stejný princip platí pro PDO i ORM: data oddělit parametrem, strukturu dotazu určit kódem a neposílat do ní volný text od klienta.
// Nedělat: SQL kód a data jsou v jednom řetězci.
$sql = "SELECT * FROM orders WHERE customer_email = '$email'";
$sortColumns = ['date' => 'created_at', 'number' => 'order_number'];
$sort = $sortColumns[$requestedSort] ?? 'created_at';
$rows = $connection->executeQuery(
'SELECT id, order_number, created_at
FROM orders
WHERE customer_email = :email
ORDER BY ' . $sort . ' DESC',
['email' => $email],
);
Jak funguje
Od vstupu k bezpečnému dotazu
Bezpečný tok rozlišuje hodnotu, kterou lze parametrizovat, od části SQL struktury, kterou musí aplikace sama pevně určit.
- Aplikace přijme vstup E-mail, filtr či zvolený směr řazení je nedůvěryhodný, i když pochází z interní administrace.
- Rozdělí hodnoty a identifikátory Hodnoty jdou do parametrů; název sloupce, tabulky a SQL klíčové slovo nejsou běžné parametry.
- Ověří povolené volby Dynamické řazení se převede přes whitelist, například created_at nebo order_number, nikdy nepřilepí přímo z requestu.
- Provádí prepared statement DBAL, PDO nebo ORM předají typovanou hodnotu přes placeholder a databáze zachová strukturu dotazu.
- Omezí následky chyby Databázový účet má jen nutná práva a klient dostane bezpečnou chybovou odpověď bez SQL detailů.
Důležité pojmy
Bezpečnost je v rozhraní mezi SQL a daty.
Každá technika chrání jinou část problému; žádná sama neřeší všechna business pravidla.
Prepared statements a placeholdery
Placeholder :email nebo ? zastupuje hodnotu. Driver ji pošle databázi odděleně od SQL textu a správně zpracuje podle typu. Ruční escapování řetězce je křehčí a není náhradou.
Whitelist identifikátorů
Název sloupce, tabulky nebo ASC/DESC obvykle nelze předat jako parametr. Aplikace vybírá z pevně definované mapy známých možností, nikoli z volného textu requestu.
ORM a QueryBuilder
Doctrine ORM a DBAL snižují riziko při práci s parametry, ale ručně složené SQL/DQL fragmenty nebo Expression mohou chybu znovu otevřít. QueryBuilder není automaticky bezpečný řetězec.
Nejmenší databázová oprávnění
Aplikační účet má mít jen práva potřebná pro provoz. Neodstraní chybu v dotazu, ale omezuje, co lze při jejím zneužití provést.
Vztah k podobným pojmům
SQL injection není XSS ani problém vyřešený jen ORM.
Přesné rozlišení pomáhá zvolit správnou obranu na správné straně aplikace.
- Doctrine DBAL
- Nabízí parametrizované SQL, transakce a query builder. Správné použití parametrů zůstává odpovědností aplikačního kódu.
- Doctrine ORM
- Mapuje entity a běžné dotazy často parametrizuje. Nechrání libovolné nativní SQL či ručně sestavenou část DQL.
- Validace a escapování
- Validace kontroluje povolený business formát, escapování řeší konkrétní kontext výstupu. Pro SQL hodnoty je základ parametrizace.
- XSS
- XSS vykonává vložený kód v prohlížeči; SQL injection mění databázový dotaz na serveru. Mají jiné vektory i obrany.
Výhody a omezení
Správný mechanismus je jednoduchý, hranice dotazu musí být promyšlená.
Přínosy
- parametry jednoznačně oddělí data od SQL syntaxe
- whitelist dává bezpečný a čitelný výběr řazení či sloupců
- DBAL, PDO a ORM podporují standardní bezpečnou cestu
- omezená databázová práva zmenší následky další chyby
Rizika a chyby
- ruční konkatenace i po escapování se snadno rozbije při změně kontextu
- ORM nechrání nativní SQL ani dynamické fragmenty automaticky
- parametr nelze použít místo názvu tabulky, sloupce či SQL klíčového slova
- WAF může útok zachytit, ale nenahrazuje opravu zdrojového kódu
Hranice použití
Parametrizace je výchozí, ne volitelný detail.
Každá hodnota pocházející mimo pevně napsaný SQL příkaz patří do parametru. To platí i pro čísla, datum, API payload či data z dřívějšího importu: dnešní interní hodnota může být zítřejší neověřený vstup. Jednotné databázové rozhraní a code review pomáhají nebezpečnou konkatenaci zachytit včas.
Dynamické řazení, název sloupce nebo směr sortu jsou jiný případ. Je-li potřebujete měnit, návrh má vytvořit mapu povolených hodnot a vybrat pouze z ní. Nikdy není cílem umožnit klientovi psát SQL; cílem je nabídnout malou, jasně definovanou množinu funkcí aplikace.
Na co myslet
Ochranu ověřovat v kódu i oprávněních databáze.
Bezpečné dotazy mají být běžnou cestou, ne rozhodnutím každého jednotlivého vývojáře.
- parametrizovat každou dynamickou hodnotu v SQL, DQL i query builderu
- pro řazení, sloupce a tabulky používat pevný whitelist
- nevracet klientovi databázovou chybu, SQL text ani stack trace
- dávat aplikaci minimální nutná databázová oprávnění
- testovat filtrování a řazení včetně neočekávaných vstupů a kontrolovat ruční SQL v review
Časté otázky
Co parametrizace chrání a co ne
Stačí validovat e-mail nebo číslo?
Ne. Validace je užitečná pro business pravidla, ale bezpečné oddělení hodnoty od SQL kódu zajišťuje parametrizovaný dotaz.
Mohu parametrizovat název sloupce v ORDER BY?
Obvykle ne. Placeholder je pro hodnotu, ne pro SQL identifikátor. Použijte mapu povolených názvů a vyberte pouze z ní.
Jsem v bezpečí, když používám ORM?
Běžná práce s entitami a parametry riziko výrazně snižuje. Ručně složené native SQL, DQL fragmenty nebo nebezpečné expression mohou SQL injection znovu umožnit.
Vyřeší problém web application firewall?
Může zachytit část pokusů, ale nemůže spolehlivě nahradit parametrizaci v aplikaci. Oprava patří do kódu a databázových oprávnění.
Jak pracuji s daty v praxi
Databázové dotazy posuzuji spolu s integritou a oprávněním.
Při vývoji backendů řeším explicitní SQL, ORM, transakce, constrainty i bezpečnou hranici mezi HTTP vstupem a databází.