Obrana proti SQL Injection

Školení, která pořádám

Existuje několik způsobů, jak se bránit SQL Injection (což je schopnost uživatele modifikovat SQL dotaz pomocí předaných dat). Některé databázové vrstvy (ať už ty vestavěné jako MySQLi nebo nadstavbové jako PEAR DB) umožňují oddělení SQL dotazů od uživatelských dat, které jsou předávány jako parametry. V MySQLi se to dělá např. takhle:

<?php
$stmt = $mysqli->prepare("SELECT * FROM tabulka WHERE nazev = ? OR id = ?");
$stmt->bind_param("si", $_GET["nazev"], $_GET["id"]);
$stmt->execute();
?>

Pokud takovou vrstvou nedisponujete nebo se bez ní musíte obejít, je nutné parametry SQL dotazů předávat přímo jako jejich součást. A na tomto místě nastupuje obrana proti SQL Injection. Jeden způsob je ošetřit veškerá vstupní data vhodným způsobem (na což se dá zapomenout), druhý je použít direktivu magic_quotes_gpc a veškeré hodnoty uzavírat do apostrofů.

<?php
// ošetření vstupních dat
mysql_query("SELECT * FROM tabulka WHERE nazev = '" . mysql_real_escape_string($_GET["nazev"]) . "' OR id = " . intval($_GET["id"]));

// spolehnutí se na magic_quotes_gpc
mysql_query("SELECT * FROM tabulka WHERE nazev = '$_GET[nazev]' OR id = '$_GET[id]'");
?>

Všimněte si, že v druhé variantě je do apostrofů uzavřená i číselná hodnota. MySQL to umožňuje a pokud předaný řetězec nebude na číslo převoditelný, převede se na nulu. Já osobně mám magic_quotes_gpc rád, protože umožňuje vytvářet jednoduchý a přímočarý kód. Pokud se ale na nastavení této direktivy nemůžete spolehnout, nelze použít ani jeden z těchto způsobů. Druhý způsob nelze použít pochopitelně kvůli riziku SQL Injection, první nejde použít proto, že kdyby byla direktiva zapnutá, tak se předaná data budou escapovat dvakrát – jednou kvůli magic_quotes_gpc a jednou funkcí addslashes. V takovém případě je nutné vytvořit si vlastní funkci a použít ji místo addslashes:

<?php
function gpc_addslashes($str) {
    return (get_magic_quotes_gpc() ? $str : addslashes($str));
}
?>

V některých případech je samozřejmě nutné předaná data ošetřit i v případě zapnuté direktivy magic_quotes_gpc:

<?php
// nefunguje
mysql_query("SELECT * FROM tabulka LIMIT 10 OFFSET '$_GET[offset]'");

// funguje
mysql_query("SELECT * FROM tabulka LIMIT 10 OFFSET " . intval($_GET["offset"]));
?>

Chris Shiflett uvádí, jak se dá při použití některých vícebajtových znakových sad (např. UTF-8 se to netýká) ochrana slashováním obejít, Ilia Alshanetsky navíc dodává, že možnost stejného zneužití se týká i funkce mysql_real_escape_string. V našich zeměpisných šířkách se jedná spíše o teoretickou hrozbu, je to ale hezký příklad toho, že si člověk nemůže být nikdy ničím jistý.

Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací.

Jakub Vrána, Výuka, 2.3.2005, diskuse: 82 (nové: 0)

Diskuse

ikona dgx:

Pozor na jednu věc. Magic quotes (a vůbec addSlashes) nemusí fungovat v souladu se "slashováním", jaké používá databáze. Například databáze MS SQL nebo obecně ODBC slashují zdvojením apostrofu, nikoliv vložením lomítka před něj.

ikona Jakub Vrána OpenID:

V tom případě se dá použít direktiva magic_quotes_sybase.

ikona dgx:

Tím ale neodpadá nutnost na to dávat pozor (nic jiného v komentáři netvrdím). Obzvlášt když nastavení direktivy magic_quotes_sybase záleží od administrátora stroje a programátor je nemůže ovlivnit.

(mimochodem, pamatuji jednu ze starších verzí PHP4, kde nezávisle na magic_quotes_sybase byly předávané parametry vždy slashované lomítkem :-(

petr77:

Dobrý den,
konfigurační direktivy magic_quotes_gpc a magic_quotes_sybase jsou od verze PHP 6.0.0. odstraněny.
Viz http://cz2.php.net/manual/en/ref.info.php

DFly:

u insertu a update se da uvozovkovani ciselnych dat tolerovat, ale u selectu pokud se pouzivaji indexy na ciselnych sloupcich tuto vyhodu degradujete, a selecty jsou "pomale" jako bez indexu (a testovni na ciselnou promenou neni tezke, takze proc uvozovky/apostrofy)

ikona Jakub Vrána OpenID:

Přinejmenším v MySQL tohle tvrzení neplatí. Dotaz se nejprve zparsuje, zjistí se datové typy a teprve potom se rozhoduje, jaké indexy se použijí. Kdo nevěří, může si to vyzkoušet:

EXPLAIN SELECT * FROM tabulka WHERE id = '1';

Řekl bych, že ostatní databáze se v tomhle lišit nebudou.

DFly:

bohuzel v tomhle jses na omylu - koukni do dokumentace - ja zpracovavam kompletni UIR-ADR a napr. pri prohledavani vsech ulic v CR s vazbou na cisla domu v ulicich (provazano pres unikatni ciselne identifikatory) a pri zkousce v apostrofech se prohledavani az neskutecne protahlo

a jak pise Zrzek, tak pretypovni je korektni zpusob

ikona Jakub Vrána OpenID:

Já jsem napsal, pro jakou databázi to platí a netvrdím, že to platí univerzálně. Ty jsi používanou databázi bohužel neuvedl, takže těžko se přít.

DFly:

take mluvim o MySQL (4.0)
ale 4.1 jsem jeste na tomto projektu nezkousel

Zrzek:

Pokud pouzijes uvozkovky a pretypovani '2'::integer, tak budou indexy fungovat spravne. Minimalne na Postgresu to takhle funguje.

lll:

nebolo by namiesto toho addslashes lepsie pouzit funkcie na tento konkretny problem robene? mysql_escape_string, mysql_real_escape_string, pg_escape_string, ...

ikona Jakub Vrána OpenID:

Teoreticky určitě ano, ale alespoň u MySQL na tom nesejde.

Havran:

No PostgreSQL volakedy (tusim do 7.2) verzie bral cislo v '' ale od urcitej doby nie - prinutilo ma to prerobit takmer vsetky aplikacie co sme mali na serveri :). Teraz zakazdym hladam cestu ako to zautomatizovat...

Herfik:

U MySQL nepouzivat addslashes ani magic quotes, ale mysql_escape_string!!
pri pouziti prvnich dvou moznosti se poskodi binarni data!

ikona Jakub Vrána OpenID:

A které znaky se podle tebe poškodí? Uložení znaků \x00 \n \r \ ' " \x1a, které escapuje mysql_escape_string, ošetřených funkcí addslashes žádné poškození nezpůsobí.

Radim:

Jeste k poskozovani dat. Tomuto problemu rozumim minimalne ze trech duvodu.

Zrovna ctu  - "User's Manual".

Pokud tento text ulozim Vasim zpusobem, bude ulozeno  "User\'s Manual".
Z meho pohledu jde o vyrazne poskozeni dat!

1. duvod - nelibi se mi to,
2. pokud pristoupim k datum jinym prostredkem nez PHP-PHP, dostanu neco jineho nez jsem vlozil, a to mi jiz dost vadi,
3. pokud jinym prostredkem budu vyhledavat "User's", nenajdu hledany retezec, a to je selhani ucelu.

Neumim to resit, proto vznasim tyto polemicke uzivatelske namitky.

paradentóza:

Nejde o to, jak to bude uloženo (dokud převod do- a z- databáze bude bezztrátový). Jde o to, jak budou data uložená v databázi načtena/interpretována.
Ukládání by mělo být obrněné proti tomu, aby žádná data nemohla "prorazit" uvozovky SQL dotazu a prostoupit do něj. Současně by se při uložení (ani zpětném načtení) nemělo nic ztratit.
Jinými slovy, ať si MySQL interně ukládá data jak chce, tobě to může být jedno, dokud je umí správně a v nezměněné formě načíst zpět.

Ken:

Hmm tady nekdo nepracuje s upravou databazovych dat primo v databazi, jinak by neco takoveho nemohl nikdy napsat...

Radim:

Jakube opet trochu hec.

Pri cteni jsem byl v rozpacích z nasledujici vety.
....Jeden způsob je ošetřit veškerá vstupní data vhodným způsobem (na což se dá zapomenout),....

1. na což se dá zapomenout - pro programatorovu lenost,
2. na což se dá zapomenout - nejde to.

Budu vestit, 1. je spravne. Neni tedy vhodnejsi dusledne osetrit pricinu vsude tam, kde to jen troch jde a nehrat si nadmerne s dusledkem?

ikona dgx:

to je spíš takový všeobecně rozšířený omyl. Snadno si můžete ověřit, že AddSlashes je pro mysql dostatečné.

<?php
// vytvoříme řetězec všech znaků
$s = '';
for (
$i=0; $i<256; $i++) 
  $s
.= chr($i);

// uložíme do databáze (pole musí být typu TEXT)
mysql_query('INSERT INTO `table` SET `str`=\'' . addslashes($s) . '\'');
// přečte me zpět
$res = mysql_query('SELECT * FROM `table` .....');
$rec = mysql_fetch_assoc($res);

// a porovnáme
echo ($rec['str'] === $s) ? 'shodné' : 'rozdílné';
?>

ikona spaze:

pro mysql mozna (i kdyz jsou tam jiste rozdily), v jinejch systemech je uvedeno treba totok:

"addslashes() should NOT be used to quote your strings for SQLite queries; it will lead to strange results when retrieving your data."

nic proti addslashes, ale co se v mladi naucis ;) (muj nazor je ten, ze sama DB vi lepe, co ji muze zpusobit problem)

Tomáš:

Já používám mysql_escape_string() je to chyba? Fakt je lepši používat jen addslashes()?

ikona llook:

IMHO je to jedno.

rezna:

ne ze bych rad ozivoval stare diskuze ale rypnu si. jedno to neni - ja bych se vyhnul obojemu a napsal si univerzalni fci ktera dle pouzite DB pouzije to spravne escapovani (samozrejme jeste je treba prihlednout k nastaveni magic_quotes) - navic tahle funkce muze vracet retezec rovnou uzavreny v apostrofech a kdyz bude hodne chytra tak ty apostrofy u ciselnych hodnot vynecha. proste neni nad obecnost - nikdy by se do kodu nemelo neco dratovat natvrdo protoze kdyz potom zjistime ze to chceme upravit jsme tzv. v pr*eli

ikona llook:

Taky se někdy může hodit vylomítkování metaznaků:
<?php
$val
= addcslashes($val, '\'\\"%_');
?>
Tedy pro MySQL a další, co slashujou backslashem. Pro ostatní by bylo nutné asi strtr.

Mordae:

Co pro vkládání používat bin2hex?
<?php
  $data
= bin2hex('Strašně humusný a vůbec "nechutný" řetězec obsahující výraz func() ? "\nano" : "\nne"');
  mysql_query("INSERT INTO tabulka SET sloupec=0x$data");
?>
Tohle funguje parádně. Problém je v použití při selectu, či updatu. V příkazu WHERE se nedá použít přímo. Dá se ale použít ve funkci STRCMP (což je ale dost pomalé):
SELECT * FROM tabulka WHERE STRCMP(col, 0x$data) = 0

V případě čísel bych se příkládněl spíš k php funkci intval(), nebo nějaké obdobné. Když už bych pak chtěl slashovat, raději použiji (u MySQL) náhradu znaků "\\"->"\\\\" a "'"->"\\'", čímž si zajistím, že hodnoty v uvozovkách (... WHERE col = '$col' ...) bude MySQL brát jako bez lomítka a tudíž nic nepřidá. Pokud bych použil addslashes():
<?php
  $text
= addslashes('text s "uvozovkami"');
  mysql_query("INSERT INTO tabulka SET col = '$text'");
?>
dostanu v db záznam se zbytečmými lomítky.

Jestli se pletu, tak se omlouvám, nezapomeňte mi to ale napsat na mordae@mordae.net...

ikona Jakub Vrána OpenID:

Ano, skutečně se pleteš. Než něco napíšeš, najdi si to v dokumentaci nebo vyzkoušej (nejlépe oboje). Zkus v MySQL konzoli zadat SELECT '\"', dostaneš ".

Anit:

Zdar!
Jsem lama v oboru, ale mám pocit, že mi vše funguje správně. K  MySQL přistupuji přes funkce, které mám zvlášť oddělené od kódu (zvlášť soubor). Než strčím cokoliv do databáze, použiji AddSlashes, když to vybírám, použiji StripSlashes. Když vkládám čísla, použiji přetypování na integer. Když něco vyhledávám, dotazuji se řetězcem upraveným AddSlashes a tak najdu vše nezávisle na uvozovkách atd...
Kde mám očekávat průšvih?

Díky

ikona Jakub Vrána OpenID:

Automatické slashování dat zvenku zařizuje direktiva magic_quotes_gpc. Pokud je vypnuta, je nutné data ošetřovat funkcí addslashes, pokud je zapnuta, tak by se data naopak nijak ošetřovat neměla. stripslashes() je v obou případech nadbytečné.

Anit:

Je to zvláštní, ale nemohu souhlasit. Funkci AddSlashes jsem začal používat poté, co jsem si sám na svých stránkách pomocí SQL injection obyčejným formulářem na zadávání jména a příjmení nastavil maximální práva. Prostě každá výstupní data z formulářů přeslashuju a pak je to bezpečné.
A teď ta druhá strana. Doma mám EasyPHP 1.8 s SQL, která slashe automaticky odstraňuje a do databáze se dostávají jen čistá data. Na serveru, kde tyto stránky běží naostro, to donedávna fungovalo úplně stejně, ale ejhle. Nedávno spustili novou verzi MySQL a ta slashe neodstraňuje a když si svá data vybírám, jsou zaslashovaná! Tedy jsem začal používat i StripSlashes a je zase vše v pořádku. Funguje to teď stejně na obou verzích MySQL. Když slashe jsou, tak se odstraní, když nejsou, tak se neodstraní a hotovo. Nemusím se dál starat o to, jak je která databáze nastavena.

ikona Jakub Vrána OpenID:

Opravdu není způsob, jak to napsat univerzálně bez ohledu na magic_quotes_gpc.

Pokud magic_quotes_gpc zapnuté je a ty data ještě jednou escapuješ, uloží se do databáze špatně (takže např. maximální délka sloupců na hodnoty obsahující apostrof nebo backshlash nebude stačit).

Pokud magic_quotes_gpc zapnuté není a ty data přicházející z databáze ošetříš funkcí stripslashes(), zmrší se data, která obsahovala např. \'.

Lepší než metoda pokus-omyl je v tomto případě pochopit, jak to funguje, a data ošetřovat v závislosti na magic_quotes_gpc (pokud ji nemůžeš nastavit sám).

Anit:

Hmmm. Je to tak. Dík

Anit:

Tak jsem si nechal vypsat doma i na serveru funkci get_magic_quotes_gpc a obě vrátily 0. Proč se tedy chovají odlišně?

Boreg:

Jsem v PHP (a vůbec programování) začátečník, takže se případně omlouvám, ale:
"Opravdu není způsob, jak to napsat univerzálně bez ohledu na magic_quotes_gpc."
Nějak se mně to nezdá, co takhle pouziti "sprintf". S tím mám nad tím přeci úplnou kontrolu a přehlednosti kódu to příliš neubere. Nebo jsem to snad špatně pochopil?! Celkem by mě to zajímalo, protože jsem si zatím jednotlivé techniky nikterak neosvojil. Díky za reakce

Andrew:

Jestli to chápu podle tvých vět správně (stripslashes je v obou případech nadbytečný), tak do databáze se uloží data již opět bez lomítek. Podle manuálu by to tak i odpovídalo, tudíž poznámka Radima o poškození dat je irelevantní. Stripslashes použiji tedy pouze při automatickém slashování, kdy to neproháním databází?
A ještě k mysql_escape_string(). V manuálu se píše o změně konce řádků na \n. Co na to addslashes a co pak s tím při výpisu?

ikona Jakub Vrána OpenID:

Chápeš to správně.

Co se \n týče, s tím je to obdobně - při ukládání do databáze se taktéž převede na znak konce řádku. Dotaz SELECT 'a\nb' vypadá o něco lépe než SELECT 'a
b', ale oboje funguje stejně.

lama:

Chybí mi na začátku článku informace, co to vlastně ten SQL injection je, odkaz do manuálu je sice pěkná věc, ale řekl bych, že byste to mohl trošičku popsat..

diogenes:

by som povedal ze google nas kazdodenny :-)
ale ak s nim nie si velky kamos, tak security-portal.cz

Andrew:

Dotaz na názor. Je podle Tebe výhodnější vždy na začátku doplnit v závislosti na magic_quotes_gpc případné slashe na všechny vstupní data ($_POST, ...), nebo u každé hodnoty zvlášť až před vlastním použitím (vstupem do databáze)?

ikona Jakub Vrána OpenID:

Můj názor je ten, že nejlepší je konfiguraci nějak nastavit (např. v souboru .htaccess) a ve skriptech s ní počítat.

Pokud to možné není (projekt poběží na různých systémech, jejichž konfiguraci ovlivnit nelze), přijde mi lepší proměnné zvenku ošetřit společně a ve skriptech se na to opět spoléhat.

Nicméně varianta s jednotlivým ošetřováním (např. funkcí gpc_slashes()) mi o moc horší nepřijde. Hlavní nevýhodu vidím v tom, že je nutné vždy ošetřovat jak data používaná v SQL dotazech, tak data používaná k jinému účelu. Výhoda je naopak v tom, že je ve skriptech na první pohled vidět, že jsou data ošetřená - viz http://php.vrana.cz/lokalni-psani-kodu.php.

cmircea:

Here it is dude:
http://www.askbee.net/articles/php/SQL_Inj…_injection.html

Miky:

Chtěl bych se zeptat, pokud chci ochranit napriklad prihlasovaci formular proti SQL injection, jestli pomuze napriklad omezeni textoveho pole pro login napriklad rozmerem 12 pismenem, cimz znacne snizim moznou delku podvrhnuteho SQL dotazu? Bude takove to omezeni fungovat nebo to muze hacker nejak snadne obejit?

ikona Jakub Vrána OpenID:

Pokud se omezení provede atributem maxlength, tak to obejít samozřejmě lze - třeba tak, že si útočník vytvoří kopii formuláře bez tohoto atributu. Obecně se na takovéto věci nevyplatí spoléhat.

ikona dgx:

Miky, uvažuješ úplně špatným směrem. Jedinou obranou (a 100%) proti SQL injection je používání escapovacích funkcí. Tím, že je 100%, není třeba ji kombinovat s žádnou jinou ochranou.

(další věc je, že třeba mysql_real_escape_string() má své muchy, viz poslední odstavec, proto raději neuvádím konkrétní escapovací fci).

flam:

Chtěl bych se zde podělit o svou zkušenost s vypisováním escapovaných hodnot z databáze MySQL.

Do databáze jsem ukládal řetězec sa"sa'sa\sa . Do tabulky jsem vložil dva řádky, jeden s magic_quotes_gpc = on (řetězec jsem ošetřil funkcí addslashes) a druhý s magic_quotes_gpc = off (řetězec jsem nijak neupravoval). Když jsem pak provedl mysql dump nebo se podíval na obsah tabulky v phpMyAdminu, oba řádky byly identické.

Když jsem tabulku vypsal v HTML stránce, zjistil jsem odlišné chování pro různé verze databáze.

databáze MySQL 4.1.9 vypsala toto: sa"sa'sa\sa

databáze MySQL 4.0.24 vypsala toto: sa\"sa\'sa\\sa

Chci se zeptat, zda je toto chování závislé na nějakém nastavení databáze a pokud ano, čím jej ovlivním, nebo zda to je věc konkrétní verze databáze MySQl.

ikona dgx:

magic_quotes je PHP relikvie, přežitek z dob minulých a zdroj bezpečnostních problémů. Na lepších hostinzích je vypnuté. Pokud tomu tak není, je NUTNÉ data "odescapovat" rekurzivním voláním stripslashes().

Teprve poté máme k dispozici čistá data. Můžeme je validovat, ošetřit, např. zkontrolovat délku a případně je oříznout (prostě aby se vešla do databázového pole). Atd.

Nyní máme k dispozici data určená pro vložení do databáze. Všimněte si, že dosud jsme nic neescapovali. Nebyl důvod.

Existuje několik cest, jak předat data databázi (jsou různé databáze, různá rozhrani). Asi nejčastějším je vytvoření SQL dotazu. A právě tady přichází na řadu escapování. Je nutné k tomu použít příslušnou funkci, kterou disponuje ta která databáze. AddSlashes() takovou funkcí NENÍ, např. pro MySQL se používá mysql_real_escape_string(). Samozřejmě při získání dat z databáze máme zase "čistá data".

Data také můžeme chtít vypsat do HTML. Opět je nutné je escapovat - k tomuto účelu slouží funkce HtmlSpecialChars().

Podstatné je uvědomit si, že celou dobu pracujeme s čistými daty a vždy je escapujeme až podle potřeby a funkcí, který je pro daný vstup/výstup určená.

ikona Jakub Vrána OpenID:

Popis je v zásadě v pořádku, jen doplním toto:

1. Direktiva magic_quotes_gpc není ani tak zdrojem bezpečnostních problémů, jako spíš velkého zmatku. Proto bude v PHP 6 odstraněna.

2. Spíše než rekurzivní volání stripslashes() je lepší zásobníkové - viz http://php.vrana.cz/vypnuti-magic_quotes_gpc.php.

3. Funkce addslashes() se v MySQL s běžně používanými kódováními použít dá bez jakýchkoliv problémů, mysql_real_escape_string() je ale samozřejmě "správnější".

ikona dgx:

Souhlas. Jen ty "běžně používá kódování" je trošku nepřesný termín. Tuším, že bezpečnostní díra při použití addslashes se otevírá při použití dvou- a vícebajtových kódování, je to tak?

ikona Jakub Vrána OpenID:

Jenom některých. Například nejpoužívanější UTF-8 je s touto funkcí kompatibilní.

ikona dgx:

Nojo, UTF-8 není dvou- a vícebajtové kódování ;)

flam:

Nevšiml jsem si, že při testování zobrazování dat z  databáze MySQL 4.0.24 mám magic_quotes_gpc = on, takže u testovaných verzí je funkčnost stejná.

Na lepších hostinzích možná je vypnuté, nicméně weby se bohužel nedělají pouze pro umístění na těchto hostinzích, takže se v aplikacích musí počítat i jiným nastavením serveru.

Jinak ja osobně ukládám data do databáze upravené funkcí HtmlSpecialChars(), takže při zobrazení dat již nemusím žádné další funkce volat. Data, která ukládám jsou určena pro zobrazení na webu, takže mě vyhovuje, že jsou takto upravena.

ikona Jakub Vrána OpenID:

Pokud můžeš zajistit, že se do databáze neescapovaná data nedostanou (třeba omylem při přímém vstupu z phpMyAdmin) a nejsou potřeba jinde, tak pořád musíš počítat s tím, že escapovaná data jsou delší, takže i když uživatel do políčka s maximální délkou 20 znaků zadá 20 znaků, tak může dojít k ořezu. Escapovat je opravdu lepší, až když to je potřeba.

flam:

Omylem z phpMyAdmin se do databáze data nedostanou, protože phpMyAdmin je přístupný pouze pod heslem. A pokud by to někdo prolomil, už bych bezpečné ukládání dat do databáze nemusel řešit.
Ale zkusil jsem si přes phpMyAdmin (verze 2.6.1, magic_quotes_gpc = off) vložit tento můj oblíbený řetězec sa"sa'sa\sa a zadařilo se bez problemů. PhpMyAdmin je inteligentní nástroj, který si vkládání dat hlídá sám.

Jedna věc me ale není jasná. Vytvořil jsem si sloupec typu VARCHAR (5) a z formuláře jsem do něj vložil opět tento řetězec sa"sa'sa\sa. Před vložením jsem provedl addslashes(). Z řetězce se stane toto: sa\"sa\'sa\\sa , takže bych očekával, že se vloží pouze sa"s (5 znaků když počítám zpětná lomítka) ale vložilo se sa"sa, tedy půvoních 5 znaků bez zpětných lomítek. Nevíte japato?

ikona Jakub Vrána OpenID:

"Omylem" jsem měl na mysli skutečně omyl - tedy situaci, kdy oprávněný administrátor vloží třeba x < 2, i když chtěl vložit x &lt; 2, což způsobí nevaliditu stránky.

Když databáze dostane 'sa\"sa', pochopí to jako sa"sa.

Fox:

V čem by mohl být problém, že mi funkce mysql_escape_string()nefunguje. Např.

SELECT id_zprava, nadpis, text, id_kateg
FROM zprava
WHERE id_zprava = '".mysql_escape_string($id_zprava)."'

$id_zprava(číslo) předávám pomocí $_GET. Očekával bych, že když v odkazu zadám ručné jiné číslo, které budu předávat do sql dotazu, tak požadovany sql dotaz neproběhne,ale opak je pravdou. Dotaz proběhne jakoby žádná ochrana neproběhla.

ikona Jakub Vrána OpenID:

Z tvého dotazu není přesně patrné, co by podle tebe funkce mysql_escape_string (lépe mysql_real_escape_string) měla dělat, ale s největší pravděpodobností si to myslíš špatně.

Funkce slouží pro ošetření textu tak, aby se dal použít uvnitř SQL řetězce.

Fox:

Jde o to, že funkce mysql_escape_string() se má používat na všechny vstupy od uživatele, které půjdou přímo do sql - tedy v mém případě jde o proměnnou $id_zprava, která se mi předá do sql přes $id_zprava = $_GET["id_zprava"].
Možná jsem to pochopil špatně, že mysql_escape_string() má zabránit tomu, aby někdo zadal do odkazu místo

http://www.server.cz/index.php?file=zpravy&id_zprava=161
např.
http://localhost/index.php?file=zpravy&id_zprava=200

a tím předá do sql dotazu hodnotu, která by tam neměla být.

ikona Jakub Vrána OpenID:

A jak by to chudák ta funkce mohla poznat? Tohle se testuje obvykle na základě toho, jestli databáze něco vrátí. Pokud potřebuji ověřit např. ještě to, jestli záznam patří danému uživateli, musím tuto podmínku do dotazu přidat.

Fox:

už mi je to jasné, díky za odpověď

tomasr:

Jakube pokud se data maji upravovat funkcí htmlspecialchars() až po výstupu z databáze, tak jak mam zajistit validitu kódu (případně zamezit XSS) v editačních polích (třeba textarea), když tam jsou data jako vstup, i výstup. Když je neošetřim, tak je problém s validitou a když je ošetřim, tak může dojít k ořezu. Co s tím?

ikona Jakub Vrána OpenID:

Nikoliv po výstupu z databáze, ale při jakémkoliv výstupu. Výstupem v textarea asi myslíš případ, kdy se odešle formulář a protože jsou v něm chyby, tak se zobrazí znovu s daty, která zadal uživatel. Tam je použití htmlspecialchars() na místě, při příštím odesláním prohlížeč odešle zase čistý text.

tomasr:

Aha ale když to ošetřim tim htmlspecialchars() apak to uložim (už to projde), tak se mito tak uloží do databáze, to nevadí?
A co třeba ořezávání dlouhejch řetězců pomocí wordwrap(). Dočetl jsem se, že se to má dělat taky až při výstupu, tudíž v těch editačních polích nedocílím nijak toho, abych neměl dlouhatánskej řádek znaků, jelikož je to zároveň i vstup.
A ještě by mě zajímala jedna věc. Vypnul jsem si magic_quotes a zkoušel jsem svou funkci na escapování. V databázi k žádnýmu slashování evidentně nedochází (ani při zapnutém magic_quotes). Proto nepoužívám stripslashes(). Mohl bys mi prosím vysvětlit, proč se to neslashuje a k čemu je pak ta funckce stripslashes() dobrá?

ikona Jakub Vrána OpenID:

Vtip je v tom, že když do prohlížeče zadáš <input value="&lt;&gt;">, tak ti odešle <>.

Zalamování textů vypisovaných do textarea nemusíš řešit, o to se postará prohlížeč <textarea wrap="soft"> je výchozí nastavení.

Když do databáze uložíš 'a\'b', vrátí ti a'b. To je stejný princip jako u prohlížeče. stripslashes je dobrá třeba v případě, kdy je zapnuté magic_quotes a ty data potřebuješ neošetřená.

tomasr:

No mě to prohlížeč nijak nezalamuje, když tam napíšu nějaký slovo o třeba 50 znacích bez mezer.
Já myslel, že se má používat při jakýmkoliv SELECTU z databáze. Pošlo 'ahoj', uloží se mi 'ahoj', co na tom mám tedy odstraňovat?

tomasr:

Už jsem použití stripslashes() pochopil, děkuju moc za rady:). Akorát ještě jedna věc...používám tu fci na inputy, pro případy, kdy je špatně vyplněn a proto nedojde k uložení. Když tam zadám 'ahoj', tak se na to použije funkce a vrátí mi to v inputu value=''ahoj'' (mam tam value='$hodnota'), tudíž se nezobrazí nic a poruší se validita...jak řešit tohle, jde to?

ikona Jakub Vrána OpenID:

Buď jde místo apostrofů použít uvozovky (<?php echo 'value="' . htmlspecialchars($value) . '"'; ?>) nebo atribut ENT_QUOTES (<?php echo "value='" . htmlspecialchars($value, ENT_QUOTES) . "'"; ?>).

tomasr:

Použil jsem ten atribut ENT_QUOTES a funguje to, díky.

ikona v6ak:

Já osobně jsem si magic_quotes_gpc moc (=vůbec) neoblíbil. Přijde mi to hnusné. A to nemusím ani chtít nasadit MVC. K funkcím jako gpc_addslashes jsem taky skeptický:
http://v6ak.profitux.cz/clanky/co-je-spatneh…-quotes-gpc.php

patom:

Hoši sorry.. každej není nadupanej jako vy.. a musí si vystačit s tím co je na netu např. php manuálem. Jenže pokud tu píšete o tom ,že se dá obejít jak addslashes tak escape string .. tak co máme použít pro obyčejné bezpečné vložení dat do databáze ? aby nemohlo dojít ke změně URL apod. ? Můžete napsat stručný a jasný kód na obranu proti SQL Injection a nehádat se v diskuzi ... na všem je pravdy trochu

fedor:

Dobry den.Mam takyto kod a mam zapnute magic quotes gpc=on, treba premenne $datum a $id_obj ponechat len ako su tu teda '$datum' uvedene, alebo tam treba dat "$datum"

Vdaka za odpoved

$query = "select * from objednavky where datum='$datum' AND id_obj='$id_obj'";

ikona Jakub Vrána OpenID:

Záleží na tom, kde se vzalo $datum a $id_obj. Pokud to je z dat od uživatele (např. z $_GET), tak je kód v pořádku.

JB:

chtel bych se zeptat, jestli prakticky navod k funkci mysql_real_escape_string je skutecne prakticky a pouzitelny:
http://www.php.net/manual/en/function.mysql-…-string.php

Diky

ikona Jakub Vrána OpenID:

Praktickým návodem asi myslíš "Best practice". Z mého pohledu je to v pořádku s tou jedinou připomínkou, že ošetření v závislosti na magic_quotes_gpc dělám raději centrálně: http://php.vrana.cz/vypnuti-magic_quotes_gpc.php.

Jakub Rychtář:

Dobrý den, snad vám nevadí že sem píši po dvou letech, nicméně. Napsal jsem si ověřování přihlášení jak je na hoře (je to bezpečné?) ale nevím na co je to or OR id = ? , musí to tam být a jakou práci to má plnit? Děkuji.

HTMLHERGOT:

@dgx: Davide, kde máš ten článek, myslím, že se jmenoval "Escapování - ultimátní příručka" nebo podobně..? Chtěl bych na něj někoho odkázat a nemůžu ho najít, dík.

Michal:

http://phpfashion.com/escapovani-definitivni-prirucka

Ales:

Ahoj, mam dotaz. SQL injection chapu, jen nechapu jak mi hacker odhali nazev me tabulky popripade jmeno pole kde se nachazi emailova adresa nebo heslo nebo telefonni cislo??

Natoz jak ho potom vypise aby ho mohl zkopirovat???
diy za vase poznatky

ikona Jakub Vrána OpenID:

Pro získání názvů tabulek a sloupců se dá použít databáze information_schema. Data se dají získat např. tak, že se pomocí UNION přilepí k nějakému regulérnímu výsledku.

Ales:

ok, nazvy poli a tabulek jsou jasne ... a jak vypsat polozky?

ikona Jakub Vrána OpenID:

Zase klidně UNIONem.

Ales:

no to jo, ale nejaky php script vam to musi cyklem vypsat ... nebo to jde i jinak?

v6ak:

Stačí vypsat jeden záznam, zbytek získám pomocí limit 1 offset $i.

Pokud nevypisuje záznamy, ale jen chybovou hlášku, lze to (aspoň v MySQL) narvat tam.

Pokud se dozvím jen to, zda dotaz prošel, nebo skončil s chybou, získám jeden bit. Prakticky lze použít SQLsus. Zdlouhavé pro dunp celé DB, ale funkční.

Pokud se nedozvím ani to, mohu zkusit sleep a měřit čas.

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.