Jak na to: SQL injection, magic_quotes_gpc, addslashes() a stripslashes()
V názvu článku jsem vyjmenoval slova, která jsou strašákem nejednoho PHP programátora. Strašák to je ale pouze uměle vytvořený, vycházející z neznalosti problematiky.
Dost často se ve spojení s SQL injection (typ útoku hackera) zmiňuje PHP konstanta magic_quotes_gpc. Prý, že pokud je zapnutá, tak se dá SQL injection předejít. A víte, že od PHP 5.3 bude standardně magic_quotes_gpc vypnutá?
Znamená to, že poté budou SQL dotazy napadnutelné pomocí SQL injection? Ale vůbec ne.
Konstanta magic_quotes_gpc totiž s ochranou SQL injection nemá v podstatě nic společného. Pouze zde existuje průsečík v možnosti jejího využití. Proto ji neznalí programátoři začali přisuzovat větší váhu, než má. Váha magic_quotes_gpc je samozřejmě nulová, zvláštně, když od PHP 5.3 bude vypnutá a v PHP 6 zanikne úplně.
Vysvětleme si tedy, oč jde. SQL injection je vážný problém.
SQL injection: Co to je a jak se bránit?
SQL injection je označení pro typ útoku hackera. Jedná se o změnu, podvržení původního SQL dotazu. Dotaz tedy udělá ne to, co chcete vy, ale co chce hacker.
Pokud chceme z databáze získat nějaký záznam (v tomto případě údaje o uživateli zdenekvecera) použijeme obdobný dotaz:
$sql = "SELECT *
FROM uzivatel
WHERE nick='zdenekvecera' L
IMIT 1";
Všimněte si, že vkládané hodnoty uzavíráme mezi apostrofy. Je to zcela korektní a běžná praxe.
Pokud nick uživatele předáváme například v parametru adresy GET, může dotaz vypadat takto:
$sql = "SELECT *
FROM uzivatel
WHERE nick='".$_GET['uzivatel_nick']."' LIMIT 1";
A zde nastává kritický moment, protože uvedený příklad je potenciálně nebezpečný. Snadno lze napadnout pomocí SQL injection.
Představte si, že $_GET[‚uzivatel_nick“] nebude obsahovat nick uživatele (zdenekvecera), ale řetězec se znaky, které dokáží podobu SQL dotazu změnit. Mezi takové znaky patří apostrof:
'
Pokud bude proměnná $_GET[‚uzivatel_nick‘] obsahovat:
' OR
id
=2 ORDER BY
id
DESC --
Tak dotaz bude vypadat takto:
$sql = "SELECT *
FROM uzivatel
WHERE nick='' OR
id
=2 ORDER BY
id
DESC --' LIMIT 1";
Dvě pomlčky MySQL interpretuje jako komentář, takže výsledná podoba provedeného dotazu je následující:
$sql = "SELECT *
FROM uzivatel
WHERE nick='' OR i
d
=2 ORDER BY
id
DESC";
Útočník tedy dokázal změnit podmínku WHERE. Místo nicku uživatele se vyhledává ID.
Relativně snadno tak může útočník získat citlivé údaje (skryté e-maily, čísla platebních karet), přístup k administrátorským účtům nebo třeba smazat data z databáze.
Co je tedy hlavní problém? Absence escapování
Problém je v tomto případě apostrof. Aby se apostrof do databáze uložil jako apostrof, a nebyl interpretován jako znak uvozující konec řetězce, muselo by se před apostrof umístit zpětné lomítko (\), z angličtiny známé jako backslash. Tato akce se nazývá escapování.
Tento kód je tedy bezpečný:
$sql = "SELECT *
FROM uzivatel
WHERE nick='\' OR id = 2 ORDER BY id DESC --' LIMIT 1";
Jediná změna je v tom, že před apostrof se umístilo zpětné lomítko a v databázi se vyhledá následující řetězec:
' OR id = 2 ORDER BY id DESC --
Zpětné lomítko je součástí jen po dobu zpracování, do databáze se nezapisuje (při INSERT nebo UPDATE) a ani není součástí při výběru (SELECT). Do databáze se tedy zpětné lomítko neuloží.
Řešení je tedy jednoduché, stačí přidat před apostrof zpětné lomítko (escapovat).
Jak přidat zpětné lomítko (backshlash)?
Snadno. Každá databáze nabízí funkci, která ošetří nebezpečné znaky u vstupního řetězce. Apostrof totiž není jediný. Kromě něj způsobuje problém třeba i uvozovka či zpětné lomítko.
MySQL (resp. PHP) má funkci mysql_real_escape_string().
Takto vykonaný dotaz by byl zcela korektní a bezpečný:
$sql = "SELECT *
FROM uzivatel
WHERE nick='".mysql_real_escape_string($_GET['uzivatel_nick'])."' LIMIT 1";
Funkce mysql_real_escape_string() totiž ošetří problémové znaky a vrátí jejich bezpečnou podobu (se zpětnými lomítky), jak už bylo uvedeno výše.
' => \'
" => \"
\ => \\
Tímto končí problematika SQL injection.
PHP také ošetřuje apostrofy a uvozovky pomocí addslashes()
Podobně, jako mysql_real_escape_string(), se i chová addslashes(). Je tedy vhodné addslashes() pro ochranu před SQL injection použít? Jednoznačně ne.
Funkce mysql_real_escape_string() ošetřuje všechny problémové znaky, které jsou pro MySQL typické. Navíc, na rozdíl od addslashes(), zohledňuje i znakovou sadu (kódování).
SQL dotaz ošetřený pomocí addslashes() není 100% ochráněn před SQL injection (viz zde a zde).
addslashes() tedy pro escapování znaků kvůli SQL injection nepoužívejte.
Co s tím má společného magic_quotes_gpc a stripslashes()?
Vůbec nic. Pokud máte zapnutou PHP konstantu magic_quotes_gpc, tak u $_POST, $_GET a $_COOKIE automaticky dochází k přidávání zpětných lomítek například před uvozovku, apostrof a zpětné lomítko. Ještě jednou zdůrazňuji, že automaticky.
Tyto proměnné ale nemají s SQL databází, potažmo s SQL injection, vůbec nic společného.
Když do formuláře s názvem komentar napíšete:
it's ok
a odešlete, v proměnné $_POST[‚komentar‘] budete mít:
it\'s ok
Opakuji, že se tak děje pouze tehdy, když je magic_quotes_gpc zapnutá.
Pokud chcete takovou proměnnou vypsat na stránce, vložit do formuláře nebo uložit do databáze, musíte nejprve zavolat funkci stripslashes() s parametrem $_POST[‚komentar‘]. Ta totiž odstraní z řetězce nadbytečná zpětná lomítka.
Kdyby byla konstanta magic_quotes_gpc vypnutá, proměnná $_POST[‚komentar‘] bude klasicky obsahovat:
it's ok.
Jestli je magic_quotes_gpc zapnutá zjistíte pomocí funkce get_magic_quotes_gpc().
Zapnutá magic_quotes_gpc a ochrana před SQL injection
Malý příklad. Představte si, že konstanta magic_quotes_gpc je zapnutá a vy proměnnou $_POST[‚komentar‘] ihned vkládáte do SQL dotazu.
V tom případě byste nejprve měli z proměnné $_POST[‚komentar‘] odstranit nadbytečná zpětná lomítka pomocí stripslashes() a následně ošetřit řetězec pomocí mysql_real_escape_string().
Pokud byste stripslashes() nepoužili, k dotazu by se přidaly další lomítka:
it\\\'s ok
To by znamenalo, že se lomítko uloží i do databáze. V databázi byste tak měli:
it\'s ok
Když je ale konstanta magic_quotes_gpc vypnutá, krok s stripslashes() musíte naopak vynechat.
A to je celá věda
Až bude magic_quotes_gpc standardně deaktivovaná (PHP 5.3) nebo zrušena (PHP 6), tyto problémy odpadnou. Nic se ale nezmění na tom, že bude potřeba ošetřovat SQL dotazy tak, jak je uvedeno výše. SQL injection je vážný problém.
David Grudl napsal o escapování pěkný souhrnný článek, doporučuji k přečtění.
Comments ( 7 )