php únikové úvodzovky pre mysql. Najbežnejším vzorom vstrekovania SQL v PHP je zbytočné úniky znakov

(PHP 4> = 4.3.0, PHP 5)

mysql_real_escape_string - Escape špeciálne znaky v reťazci na použitie v príkaze SQL

Popis

mysql_real_escape_string (reťazec $ unnescaped_string [, zdroj $ link_identifier = NULL]): reťazec

Escape špeciálne znaky v reťazci neunescaped_string, berúc do úvahy aktuálnu znakovú sadu spojenia, takže je bezpečné ho umiestniť do mysql_query ()... Ak sa majú vložiť binárne dáta, musí sa použiť táto funkcia.

mysql_real_escape_string () volá funkciu knižnice MySQL mysql_real_escape_string, ktorá pridáva spätné lomky pred nasledujúce znaky: \ x00, \ n, \ r, \ , " , " a \ x1a.

Táto funkcia musí byť vždy (až na niekoľko výnimiek) použitá na zabezpečenie dát pred odoslaním dotazu do MySQL.

Pozor

Zabezpečenie: predvolená znaková sada

Znaková sada musí byť nastavená buď na úrovni servera, alebo pomocou funkcie API mysql_set_charset () aby to ovplyvnilo mysql_real_escape_string () ... Ďalšie informácie nájdete v časti s konceptmi o znakových sadách.

Parametre

unnescaped_string

Reťazec, ktorý má uniknúť.

Identifikátor_odkazu

Pripojenie MySQL. Ak nie je zadaný identifikátor odkazu, posledný odkaz otvoril mysql_connect () sa predpokladá. Ak sa takýto odkaz nenájde, pokúsi sa ho vytvoriť mysql_connect () bol zavolaný bez argumentov. Ak sa nenájde alebo nevytvorí žiadne spojenie, a E_UPOZORNENIE generuje sa chyba úrovne.

Návratové hodnoty

Vráti uniknutý reťazec, príp FALSE na chybu.

Chyby / výnimky

Vykonanie tejto funkcie bez pripojenia MySQL sa tiež spustí E_UPOZORNENIE chyby na úrovni PHP. Túto funkciu spustite iba s platným pripojením MySQL.

Príklady

Príklad č. 1 Jednoduchý mysql_real_escape_string () príklad

// Pripojiť
$ link = mysql_connect ("mysql_host", "mysql_user", "mysql_password")
ALEBO zomriete (mysql_error ());

// Dopyt
$ dotaz = sprintf ( "SELECT * FROM users WHERE user ="% s "A heslo ="% s "",
mysql_real_escape_string ($ používateľ),
mysql_real_escape_string (heslo $));
?>

Príklad #2 mysql_real_escape_string () vyžaduje príklad pripojenia

Tento príklad ukazuje, čo sa stane, ak pri volaní tejto funkcie nie je prítomné pripojenie MySQL.

Vyššie uvedený príklad prinesie niečo podobné ako:

Upozornenie: mysql_real_escape_string (): Žiadny takýto súbor alebo adresár v /this/test/script.php na riadku 5 Upozornenie: mysql_real_escape_string (): V /this/test/script.php na riadku 5 sa nepodarilo vytvoriť prepojenie na server bool (false) string (41) "SELECT * FROM herci WHERE last_name =" ""

Príklad č. 3 Príklad SQL Injection Attack

// Nekontrolovali sme $ _POST [" heslo "], môže to byť čokoľvek, čo používateľ chcel! Napríklad:
$ _POST ["username"] = "aidan";
$ _POST ["heslo"] = "" ALEBO "" = "";

// Dopyt do databázy a skontroluje, či existujú nejakí vyhovujúci používatelia
$ dotaz = ($ _POST ["používateľské meno"]) "A heslo =" ($ _POST ["heslo"]) "";
mysql_query (dotaz $);

// To znamená, že dotaz odoslaný do MySQL by bol:
echo $ dotaz;
?>

Dotaz odoslaný do MySQL:

To by umožnilo komukoľvek prihlásiť sa bez platného hesla.

Poznámky

Pred použitím je potrebné pripojenie k MySQL mysql_real_escape_string () inak chyba úrovne E_UPOZORNENIE sa generuje a FALSE sa vráti. Ak link_identifier nie je definovaný, použije sa posledné pripojenie MySQL.

Poznámka: mysql_real_escape_string () neutečie % a _ ... Toto sú zástupné znaky v MySQL, ak sú kombinované s PÁČI SA MI TO, GRANT, alebo ZRUŠIŤ.

pred 8 rokmi

Len malá funkcia, ktorá napodobňuje pôvodný mysql_real_escape_string, ale ktorá nepotrebuje aktívne pripojenie mysql. Môže byť implementovaná ako statická funkcia v databázovej triede. Dúfam, že to niekomu pomôže.

funkcia mysql_escape_mimic ($ inp) (
if (is_array ($ inp))
return array_map (__METHOD__, $ inp);

Ak (! Prázdne ($ inp) && is_string ($ inp)) (
return str_replace (pole ("\\", "\ 0", "\ n", "\ r", "" "," "", "\ x1a"), pole ("\\\\", "\ \ 0 "," \\ n "," \\ r "," \\ "", "\\" "," \\ Z "), $ inp);
}

Návrat $ inp;
}
?>

pred 13 rokmi

Všimnite si, že mysql_real_escape_string "nepripája spätné lomky pred \ x00, \ n, \ ra a \ x1a, ako je uvedené v dokumentácii, ale v skutočnosti nahrádza znak reprezentáciou prijateľnou pre MySQL pre dotazy (napr. \ n je nahradené znakom " \ n "doslovný". (\," a "sú zakódované, ako je zdokumentované) To nemení spôsob používania tejto funkcie, ale myslím, že je dobré vedieť.

pred 6 rokmi

Žiadna diskusia o úteku nie je úplná bez toho, aby ste každému povedali, že na generovanie interpretovaného kódu by ste v zásade nikdy nemali používať externý vstup. To platí pre príkazy SQL alebo čokoľvek, čo by ste nazvali akoukoľvek funkciou „eval“.

Takže namiesto použitia tejto strašne nefunkčnej funkcie použite namiesto toho parametrické pripravené príkazy.

Úprimne povedané, používanie údajov poskytnutých používateľom na zostavovanie príkazov SQL by sa malo považovať za profesionálnu nedbalosť a váš zamestnávateľ alebo klient by vás mal brať na zodpovednosť za to, že nepoužívate pripravené parametrické príkazy.

Čo to znamená?

Znamená to namiesto vytvorenia príkazu SQL takto:

"INSERT INTO X (A) VALUES (. $ _ POST ["a"]. ")"

Mali by ste použiť funkciu mysqli "s Prepare () () na vykonanie príkazu, ktorý vyzerá takto:

"INSERT IN TO X (A) VALUES (?)"

Poznámka: To neznamená, že by ste nikdy nemali generovať dynamické príkazy SQL. Znamená to, že na generovanie týchto príkazov by ste nikdy nemali používať údaje poskytnuté používateľom. Akékoľvek údaje poskytnuté používateľom by mali byť odovzdané ako parametre do príkazu po tom, ako bol pripravený.

Ak napríklad vytvárate malý rámec a chcete vykonať vloženie tabuľky na základe identifikátora URI požiadavky, „je vo vašom najlepšom záujme nebrať hodnotu $ _SERVER [" REQUEST_URI "] (alebo akúkoľvek jeho časť) a priamo to zreťaziť so svojím dopytom. Namiesto toho by ste mali analyzovať časť hodnoty $ _SERVER ["REQUEST_URI"], ktorú chcete, a namapovať ju prostredníctvom nejakého druhu funkcie alebo asociatívneho poľa na používateľa, ktorý nie je poskytnutá hodnota. Ak mapovanie nevytvára žiadnu hodnotu, viete, že niečo nie je v poriadku s údajmi poskytnutými používateľom.

Nedodržanie tohto pravidla spôsobilo množstvo problémov so vstrekovaním SQL v rámci Ruby On Rails, aj keď používa parametrické pripravené príkazy. Takto bol GitHub v jednom okamihu hacknutý. Žiadny jazyk teda nie je imúnny voči tomuto problému. To je dôvod, prečo je to všeobecný osvedčený postup a nie niečo špecifické pre PHP a prečo by ste si ho SKUTOČNE mali osvojiť.

Tiež by ste mali stále vykonávať určitý druh validácie údajov poskytovaných používateľmi, a to aj pri použití parametricky pripravených výkazov. Je to preto, že údaje poskytnuté používateľom sa často stanú súčasťou nejakého vygenerovaného kódu HTML a chcete sa uistiť, že údaje poskytnuté používateľom „nespôsobia bezpečnostné problémy v prehliadači.

pred 9 rokmi

V príklade č. 2 o SQL injection je zaujímavý vtip: AND má prioritu pred OR, takže vložený dotaz sa v skutočnosti vykoná ako WHERE (user =" aidan "AND password =" ") ALEBO" "=" ", takže namiesto toho ak vráti databázový záznam zodpovedajúci ľubovoľnému používateľskému menu (v tomto prípade „aidan“), v skutočnosti by vrátil VŠETKY databázové záznamy. V žiadnom konkrétnom poradí. Útočník sa teda môže prihlásiť ako akýkoľvek účet, ale nie nevyhnutne s akýmkoľvek kontrolu nad tým, o aký účet ide.

Potenciálny útočník by samozrejme mohol jednoducho upraviť svoje parametre tak, aby sa zamerali na konkrétnych používateľov, ktorých zaujíma:

// Napr. hodnoty útočníka
$ _POST ["username"] = "";
$ _POST ["heslo"] = "" ALEBO používateľ = "správca" A "" = "";

// Chybný dotaz
$ dotaz = "SELECT * FROM users WHERE user ="$ _POST [používateľské meno] "A heslo =" $ _POST [heslo] "";

echo $ dotaz;

// Dotaz odoslaný do MySQL by znel:
// SELECT * FROM users WHERE user = "" AND password = "" OR user = "administrator" AND "" = "";
// čo by umožnilo komukoľvek získať prístup k účtu s názvom "administrátor"

?>

pred 1 rokom

@feedr
Jeho poznámku som spracoval takto:
$ string = "asda \ 0sd \ x1aas \\\\\\\\ dasd \" asdasd \ na \ "\" sdasdad ";
$ pole1 = pole ("\\\\\\\\", "\ 0", "\ n", "\ r", "" "," "", "\ x1a");
$ array2 = pole ("\\\\\\\\\\\\\\\\", "\\\ 0", "\\\ n", "\\\ r", "\\\" "," \\\ "", "\\\ Z");
echo ($ reťazec);
echo (PHP_EOL);
pre ($ i = 0; $ i if ($ i == 0)
$ p = "/ (?inak
$ p = "/ (?echo ($ i);
ozvena ($ p);
echo ($ array2 [$ i]);
$ string = preg_replace ($ p, $ pole2 [$ i], $ string);
echo ("\ t");
echo ($ reťazec);
echo (PHP_EOL);
}
echo (PHP_EOL);
echo ($ reťazec);

pred 2 rokmi

To citovať Sam na Numb Safari

["Žiadna diskusia o úteku nie je úplná bez toho, aby ste každému povedali, že na generovanie interpretovaného kódu by ste v podstate nikdy nemali používať externý vstup. To platí pre príkazy SQL alebo čokoľvek, čo by ste nazvali akoukoľvek funkciou "eval".

Takže namiesto použitia tejto strašne nefunkčnej funkcie použite namiesto toho parametrické pripravené príkazy.

Úprimne povedané, používanie údajov poskytnutých používateľom na zostavovanie príkazov SQL by sa malo považovať za profesionálnu nedbalosť a váš zamestnávateľ alebo klient by vás mali brať na zodpovednosť za to, že nepoužívate pripravené parametrické príkazy. "]

Sam má pravdu..........

Nemyslím si však, že je rozumné zastaviť všetky dezinfekcie a jednoducho preniesť úlohu na parametricky pripravené výkazy.

Konkrétny vývojár pracujúci v konkrétnej situácii bude vždy vedieť viac o platnom vstupe (špecifickom pre daný kontext).

Ak požiadate používateľa, aby zadal hodnotu, ktorú ste mu už zadali, a viete, že všetky takéto hodnoty začínajú AB ****** a reťazec by mal mať dĺžku 7 alebo 11, ale nikdy inú, potom máte základ dobrého predbežného dezinfekčného prostriedku - rôzne prípustné dĺžky reťazca môžu naznačovať staršie údaje.

Nikdy by som nechcel jednoducho odovzdať odpadky, ktoré mohol používateľ so zlými úmyslami odovzdať prostredníctvom formulára, do pripravených parametrických výkazov, vždy by som si chcel najprv urobiť vlastné kontroly zdravého rozumu a v niektorých prípadoch môžu byť chybné z dôvodu opatrnosti a jednoducho vyberte úplné prerušenie operácie databázy.

Týmto spôsobom sa moja DB nezanáša nebezpečnými príkazmi, ktoré sú zabezpečené – jednoducho sa nezanáša, čo je lepšie.

Bezpečnosť vo vrstvách – sanitácia a validácia by sa mali stále zvážiť v každej situácii PRED použitím pripravených vyhlásení.

Okrem toho, pokiaľ môžem čítať do oficiálneho doc
==============================================

„Únik a injekcia SQL

Viazané premenné sa odosielajú na server oddelene od dopytu, a preto doň nemôžu zasahovať. Server používa tieto hodnoty priamo v bode vykonávania po analýze šablóny príkazu. Viazané parametre nie je potrebné escapovať, pretože sa nikdy nenahradia priamo do reťazca dopytu "

To mi naznačuje, že nebezpečenstvu sa vo vnútornostiach vyhýba alternatívnym zaobchádzaním, nie zrušením.

To znamená, že veľký projekt s neúplnou konverziou na pripravené vyhlásenia, starý kód v rôznych častiach organizácie alebo servery, ktoré spolu hovoria, by mohli preniesť zlé správy z miesta alebo situácie s imunitou na miesto, ktoré nie je imúnne.

Pokiaľ je sanitácia kompetentne vykonaná bez toho, aby vznikali ďalšie riziká, osobne by som sa držal určitých vrstiev sanitácie a potom by som zavolal pripravené vyhlásenia.


Najprv trochu o tom, prečo sú tieto lomky vôbec potrebné.
Ak v dotaze nahradíme akékoľvek údaje, potom na odlíšenie týchto údajov od príkazov SQL musia byť uvedené v úvodzovkách.
Napríklad, ak píšete
SELECT * FROM table WHERE meno = Bill
potom základňa rozhodne, že Bill je názov iného poľa, nenájde ho a dá chybu. Preto musia byť nahradené údaje (v tomto prípade meno Bill) uzavreté v úvodzovkách - potom ich základňa bude považovať za reťazec, ktorého hodnota musí byť priradená do poľa názvu:
SELECT * FROM table WHERE name = "Bill"
Úvodzovky však možno nájsť aj v samotných údajoch. Napríklad,
SELECT * FROM table WHERE name = "D" Artagnan "
Tu databáza rozhodne, že "D" sú dáta a Artagnan je príkaz, ktorý nepozná a taktiež vyhodí chybu. Preto je potrebné sledovať všetky údaje, aby sme databáze vysvetlili, že úvodzovky (a niektoré ďalšie špeciálne znaky), ktoré sa v nich nachádzajú, sa týkajú údajov.
V dôsledku toho dostaneme správnu požiadavku, ktorá nespôsobí chyby:
SELECT * FROM table WHERE name = "D \" Artagnan "

Zistili sme teda, že pri nahrádzaní údajov do dopytu by ste mali dodržiavať dve pravidlá:
- všetky údaje vložené do dopytu musia byť uzavreté v úvodzovkách (jednoduché alebo dvojité, ale jednoduché je pohodlnejšie a často používané).
- vo všetkých reťazcových premenných musia byť špeciálne znaky ukončené lomkami.

Osobitne je potrebné poznamenať: pridané lomky NEPREDAJÚ k základni. Potrebné sú iba v žiadosti. Pri vstupe do databázy sú lomky vyradené. Preto je bežnou chybou používať lomítka pri získavaní údajov z databázy.

V skutočnosti sa všetko vyššie uvedené vzťahuje na reťazcové údaje a dátumy. Čísla je možné vkladať bez blikania a bez úvodzoviek. Ak to urobíte, potom NUTNE! vynútiť údaje na požadovaný typ pred ich vložením do dotazu, napríklad:
$ id = intval ($ id);
Pre jednoduchosť (a spoľahlivosť) však môžete pracovať aj s číslami ako s reťazcami (keďže mysql ich stále prevedie na správny typ). V súlade s tým budeme sledovať a pripájať všetky údaje vložené do žiadosti.

Existuje ešte jedno pravidlo - voliteľné, ale malo by sa dodržiavať, aby sa predišlo chybám:
Názvy polí a tabuliek by mali byť uzavreté v jednoduchých úvodzovkách - "` "(kláves s týmto znakom sa nachádza na štandardnej klávesnici naľavo od klávesu" ​​1 ") Koniec koncov, názov poľa sa môže zhodovať s mysql kľúčové slová, ale ak použijeme zadné úvodzovky, MySQL pochopí, že všetko je správne:
SELECT * FROM `table` WHERE` date` = "2006-04-04"
Mali by ste rozlišovať medzi týmito úvodzovkami a nezamieňať si jeden s druhým. Malo by sa tiež pamätať na to, že spätné zarážky sa nevyhýbajú lomkám.

Takže sme sa naučili, ako správne nahradiť údaje do požiadavky.
ALE! Dynamické zloženie dotazov nie je obmedzené na nahradenie údajov. Často musíme v dotaze nahradiť príkazy SQL a názvy polí. A tu už prechádzame k téme bezpečnosti:

SQL Injection je hackerský útok, kedy sa dáta odovzdané skriptu upravia tak, že dotaz vygenerovaný v tomto skripte sa začne vykonávať úplne inak, než na čo bol určený.
Pravidlá ochrany proti takýmto útokom možno rozdeliť do dvoch bodov:
1. Práca s dátami.
2. Práca s ovládacími prvkami požiadavky.

Prvý bod sme podrobne zvážili vyššie. Dá sa povedať, že to v skutočnosti nie je ochrana. Súlad s pravidlami pridávania údajov do dotazu je diktovaný predovšetkým požiadavkami SQL SYNTAX. A ako vedľajší efekt tu máme aj ochranu proti hackingu.

Druhý bod je oveľa komplikovanejší, keďže neexistuje také univerzálne pravidlo ako pre údaje – spätné začiarknutie neochráni názov poľa pred úpravou hackerom. Nie je možné uvádzať názvy tabuliek, príkazy SQL, parametre príkazov LIMIT a iné príkazy v úvodzovkách.
Preto základné pravidlo pri nahrádzaní ovládacích prvkov v dotaze je:
Ak potrebujete dynamicky nahradiť SQL príkazy alebo názvy polí, databáz, tabuliek do dotazu, tak za žiadnych okolností by sa nemali vkladať priamo do dotazu.
Všetky možnosti pre takéto pridania musia byť napísané ADVANCE vo vašom skripte a vybrané na základe toho, čo používateľ zadal.
Napríklad, ak potrebujete zadať názov poľa do objednávky operátorom, potom ho v žiadnom prípade nenahrádzajte priamo. Najprv to musíme skontrolovať. Vytvorte napríklad pole platných hodnôt a nahraďte ho do požiadavky iba vtedy, ak sa v tomto poli nachádza odovzdaný parameter:
objednávky $ = pole ("názov", "cena", "množstvo");
$ kľúč = array_search ($ _GET ["triediť"], $ objednávky));
$ orderby = $ objednávky [$ kľúč];
$ dotaz = "VYBERTE * Z` tabuľky` ORDER BY $ orderby ";
Hľadáme slovo zadané používateľom v poli predtým opísaných možností a ak ho nájdeme, vyberieme zodpovedajúci prvok poľa. Ak sa nenájde žiadna zhoda, vyberie sa prvý prvok poľa.
Do požiadavky sa teda dosadí nie to, čo používateľ zadal, ale to, čo bolo napísané v našom skripte.
To isté musíte urobiť vo všetkých ostatných prípadoch.
Napríklad, ak je klauzula WHERE dynamicky vytvorená:
if (! prázdne ($ _ GET ["cena"])) $ kde. = "cena =" ". mysql_real_escape_string ($ _GET [" cena "])." "";
$ dotaz = "SELECT * FROM" tabuľka "WHERE $ kde";
Je pre mňa ťažké predstaviť si prípad, keď je možné názov tabuľky dynamicky dosadiť do dopytu, ale ak sa tak stane, názov by sa mal tiež vložiť iba z množiny, ktorá bola predtým napísaná v skripte.
Parametre operátora LIMIT by mali byť vynútené na celočíselný typ pomocou aritmetických operácií alebo funkcie intval ().
Nemyslite si, že tu uvedené príklady vyčerpajú všetky možnosti dynamického vytvárania dotazov. Musíte len pochopiť princíp a aplikovať ho vo všetkých takýchto prípadoch.

Funkcie práce s operátorom LIKE
Úplne samostatným prípadom je operátor LIKE.
Po prvé, okrem zvyčajných lomiek musia byť lomky zdvojené v premenných, ktoré sa dosadia do LIKE. To znamená, že ak premenná obsahuje znak \, musí sa zdvojnásobiť a potom vykonať obvyklé streamovanie cez mysql_real_escape_string.
Napríklad, ak hľadáme reťazec
\ sa nazýva "spätná lomka" a chceme presnú zhodu, potom použijeme len mysql_real_escape_string a dotaz je štandardný:
SELECT * FROM test WHERE pole = "znak \\ sa nazýva \" spätná lomka \ "" Ak chceme nahradiť tento reťazec v LIKE, potom musíme najprv nahradiť každú lomku dvoma a potom použiť mysql_real_escape_string. Výsledkom bude
SELECT * FROM table WHERE pole LIKE "% znak \\\\ sa nazýva \" spätná lomka \ "%"
Po druhé, všimnite si, že žiadna z funkcií pridávania lomky nepridáva lomky k metaznakom vyhľadávania "%" a "_" použitým v operátore LIKE. Ak teda používate tento operátor a nechcete, aby sa symboly _ a % používali ako masky, pridajte lomky ručne. To sa dá urobiť pomocou príkazu
$ data = addClashes ($ data, "% _"); Pozor - toto nie sú škrabance! Táto funkcia má vo svojom názve dodatočné „c“.

Ukazuje sa teda, že premenné použité v operátore LIKE musíme spracovať oddelene.
najprv nahraďte jednu lomku dvoma, napríklad pomocou nasledujúceho kódu:
$ var = str_replace ("\\", "\\\\", $ var); potom (môžete, spolu so všetkými ostatnými údajmi, ktoré sú súčasťou žiadosti), lomíme:
$ var = mysql_real_escape_string ($ var); a potom, ak chceme, aby sa _ a % presne zhodovali, urobíme to
$ var = addClashes ($ var, "_%");
Výsledkom je, že ak hľadáme napríklad takýto reťazec
znak \ sa nazýva "obrátená lomka" a znak _ sa nazýva "podčiarkovník" potom po spracovaní by to v žiadosti malo vyzerať takto:
"% znak \\\\ sa nazýva \" spätná lomka \ "a \ _ sa nazýva \" podčiarkovník \ " To znamená, že lomka, ktorá bola pôvodne v riadku, sa zoštvornásobila. Ostatné postavy vyšli ako obvykle. Navyše - znak podčiarknutia sa mihol.

O lomkách. Ako sa ich zbaviť
Lomka alebo spätná lomka, z angličtiny spätná lomka je spätná lomka ("\"), ktorá sa neočakávane objaví sama osebe vo vašich premenných. Je pridaný k niektorým špeciálnym znakom, ale väčšinou si ho všimnete kvôli úvodzovkám.
Stáva sa to kvôli špeciálnym nastaveniam PHP, ktoré sú na hostingu štandardne povolené. Teoreticky môžu tieto nastavenia zlepšiť bezpečnosť skriptov, ktoré pracujú s databázou. Automatické pridávanie lomítok má v praxi často za následok zmätok a nepohodlie, a to ako pri práci s databázou, tak aj pri jej absencii.
Nižšie podrobne rozoberieme oba tieto prípady.

Direktívy php.ini, ktoré sa súhrnne nazývajú „magické úvodzovky“, sú zodpovedné za automatické pridávanie lomítok:
magic_quotes_gpc a magic_quotes_runtime Ak je prvý povolený, PHP automaticky pridáva lomky k údajom prijatým od používateľa – z požiadaviek POST, GET a cookies (ako aj do prihlasovacieho mena a hesla prijatého prostredníctvom HTTP autorizácie).
Ak je to druhé, potom sa k údajom získaným počas vykonávania skriptu pridajú lomky - napríklad zo súboru alebo databázy.

Ak pracujete bez databázy, alebo ak pracujete s databázou správne (čo bude popísané nižšie), extra lomky vás len obťažujú a musíte sa ich zbaviť. Najjednoduchší a najsprávnejší spôsob vypnutia automatického pridávania je v nastaveniach PHP.
Dá sa to urobiť buď opravou zodpovedajúcich direktív v php.ini, ak k nemu máte prístup, alebo vytvorením súboru .htaccess v konečnom adresári stránky a pridaním riadkov
php_flag magic_quotes_gpc 0
php_flag magic_quotes_runtime 0

Ak to nemôžete zakázať týmto spôsobom, budete musieť napísať kód rôzneho stupňa zložitosti, aby ste vymazali prichádzajúce údaje od lomiek. (Ak však chcete napísať prenosnú aplikáciu, ktorá nezávisí od nastavení PHP, musíte ju aj tak napísať. A zaradiť ju do samostatného bloku na začiatku vašich skriptov).

S údajmi získanými počas práce sa pracuje najjednoduchšie: stačí napísať na začiatok skriptu
set_magic_quotes_runtime (0); Pre údaje prijaté od používateľa je všetko oveľa komplikovanejšie. Pre tento kód potrebujeme dve funkcie:

  • môžete skontrolovať, či bolo PHP pridané pomocou funkcie get_magic_quotes_gpc.
  • stripslashes odstraňuje lomky.
    Preto pomocou prvej musíte skontrolovať, a ak ju PHP pridalo, potom iterujte všetky prichádzajúce premenné a vymažte ju pomocou druhej.
    Ak pracujete správne s register_globals = off, potom stačí použiť lomítka na všetky polia obsahujúce dáta prichádzajúce z prehliadača.
    do všetkých skriptov lokality môžete zahrnúť napríklad nasledujúci kód:
    funkčné pásy (& $ el) (
    if (is_array ($ el))
    foreach ($ el ako $ k => $ v)
    prúžky ($ el [$ k]);
    else $ el = lomítka ($ el);
    }
    if (get_magic_quotes_gpc ()) (
    prúžky ($ _GET);
    prúžky ($ _POST);
    prúžky ($ _COOKIE);
    prúžky ($ _REQUEST);
    if (isset ($ _ SERVER ["PHP_AUTH_USER"])) stripy ($ _SERVER ["PHP_AUTH_USER"]);
    if (isset ($ _ SERVER ["PHP_AUTH_PW"])) pásy ($ _SERVER ["PHP_AUTH_PW"]);
    }
    V prípade nesprávneho nastavenia register_globals bude ťažké vôbec nájsť prijateľné riešenie, preto je lepšie – opakujem – pracovať hneď so správnymi nastaveniami.

    Poznámky

    • Medzi dôvodmi, prečo by ste sa nemali spoliehať na „čarovné citáty“, je aj ďalší. Veľmi nepravdepodobné, ale predsa. Kúzelné úvodzovky v skutočnosti nie sú dve smernice, ale tri. Tretím je magic_quotes_sybase. Nielen, že namiesto lomky pridá úvodzovky, ale tiež zruší efekt magic_quotes_gpc. Ak nejakým zázrakom majú obe tieto smernice stav „zapnuté“, potom posledná nebude fungovať! To znamená, že spoliehajúc sa na „magické úvodzovky“ v tomto prípade získame všetky potešenie z nesprávne zostavených dopytov. Vo všeobecnosti, čisto teoreticky, by sa prítomnosť tejto smernice mala brať do úvahy, pretože prináša aj také prekvapenie, ako je ... zmena správania funkcií lomítka a lomítka! Ak magic_quotes_sybase = on, potom tieto funkcie začnú pridávať a odstraňovať jeden úvodzovky namiesto lomky, resp.
    • Všetky uvedené príklady sú len pre databázu Mysql. Špecifické pravidlá pre vytváranie dopytov sa môžu líšiť pre iné DBMS, ale všeobecný princíp zostáva rovnaký:
      • ak API pre prácu s databázou alebo knižnicou tretej strany poskytuje špeciálne funkcie na vytváranie dopytov, a existuje možnosť ich využitia, potom je v prvom rade potrebné ich využiť.
      • ak takéto funkcie neexistujú, mali by ste sa v dokumentácii pozrieť na funkciu escapovania špeciálnych znakov pre tento DBMS.
    Poznámka: formuláre
    Pri výstupe hodnoty do vstupných značiek formulárov lomky nepomáhajú.
    Ak chcete zobraziť celý text v takomto poli, hodnota musí byť uzavretá v úvodzovkách a k výstupným údajom použiť funkciu htmlspecialchars ().
    Príklad:

    Treba tiež poznamenať (hoci to nemá nič spoločné s úvodzovkami a lomkami), že funkcia htmlspecialchars by sa mala použiť pri výstupe do prehliadača vo všeobecnosti pre všetky údaje prijaté od neovereného používateľa. Prečo by sa to malo robiť, si môžete prečítať na Google na požiadanie, čo je XSS zraniteľnosť
    od phpfaq.ru
  • reťazec úvodzoviek (11)

    Snažím sa nájsť najlepší spôsob, ako písať otázky. Tiež chápem dôležitosť dôslednosti. Doteraz som náhodne používal jednoduché úvodzovky, dvojité úvodzovky a spätné začiarknutie bez toho, aby som o tom skutočne premýšľal.

    $ dotaz = "INSERT INTO tabuľky (id, stĺpec1, stĺpec2) HODNOTY (NULL, hodnota1, hodnota2)";

    Vo vyššie uvedenom príklade tiež zvážte, že „tabuľka“, „stĺpec [n]“ a „val [n]“ môžu byť premenné.

    Aký je na to štandard? Čo robíš?

    Odpovede na podobné otázky som čítal asi 20 minút, ale zdá sa, že na túto otázku neexistuje definitívna odpoveď.

    Odpovede

    Teraz predpokladajme, že vo svojom dotaze MySQL používate premennú priameho príspevku, potom ju použite takto:

    $ query = "INSERT INTO` table` (`id`,` name`, `email`) VALUES (" ". $ _ POST [" id "]." "," ". $ _ POST [" name "] ." "," ". $ _ POST [" email "]." ")";

    Toto je najlepší postup na používanie premenných PHP v MySQL.

    Väčšinou v Mysql sa tieto typy identifikátorov používajú v dopytoch `,", "a ().

      "alebo" použite na zahrnutie reťazca ako hodnotu "01-26-2014 00:00:00" alebo "01-26-2014 00:00:00". Tento identifikátor sa používa len pre funkciu riadku "26-01-2014 00:00:00", napríklad teraz () alebo súčet, max.

      `použite na zahrnutie tabuľky alebo tabuľky, napríklad vyberte názov stĺpca z názvu_tabulky, kde id = 2 "

      () sa používajú iba na jednoduché uzavretie častí dopytu, napr. výber názvu stĺpca z názvu tabuľky, kde (id = "2" a pohlavie = "muž") alebo meno = "rakesh.

    Okrem všetkých (dobre vysvetlených) odpovedí neboli nižšie uvedené žiadne a ja som častým prispievateľom do týchto otázok a odpovedí.

    Stručne; MySQL si myslí, že chcete počítať na vlastnú tabuľku/stĺpec a pomlčky ako „e-mail“ interpretujte ako e-mail.

    Odmietanie zodpovednosti. Preto som si myslel, že to pridám ako odpoveď „Pre informáciu“ pre tých, ktorí sú v práci s databázami úplne noví a možno nerozumejú už popísaným technickým výrazom.

    (Vyššie sú dobré odpovede týkajúce sa povahy vašej otázky SQL, ale môže to byť relevantné aj vtedy, ak ste v PHP nováčikom.)

    Možno je dôležité poznamenať, že PHP spracováva jednoduché a dvojité úvodzovky odlišne ...

    Reťazce v jednoduchých úvodzovkách sú „doslova“ a predstavujú pomerne veľa reťazcov WYSIWYG. Reťazce v dvojitých úvodzovkách sú interpretované PHP pre možnú substitúciu premenných (spätné referencie v PHP nie sú presne reťazce, vykonajú príkaz v shelli a vrátia výsledok).

    $ foo = "bar"; echo "existuje $ foo"; // Je tu $ foo echo "existuje $ foo"; // Je tu bar echo `ls -l`; // ... zoznam adresárov

    Ak sú stĺpce a hodnoty tabuliek premenné, existujú dva spôsoby:

    S dvojitými úvodzovkami "" je úplný dopyt:

    $ query = "INSERT INTO $ table_name (id, $ col1, $ col2) VALUES (NULL," $ hodnota1 "," $ hodnota2 ")";

    $ query = "INSERT INTO". $ table_name. "(id,". $ col1. ",". $ col2. ") VALUES (NULL," ". $ hodnota1." "," ". $ hodnota2." " )";

    S jednoduchými úvodzovkami "":

    $ dotaz = "INSERT INTO". $ názov_tabulky. "(id,". $ col1. ",". $ col2. ") VALUES (NULL,". $ hodnota1. ",". $ hodnota2. ")";

    Použite back ticks', keď názov stĺpca / hodnoty vyzerá ako vyhradené kľúčové slovo MySQL.

    Poznámka. Ak zadávate názov stĺpca s názvom tabuľky, použite spätné značky takto:

    `názov_tabuľky`. `názov_stĺpca`<- Примечание: исключить. из задних клещей.

    Backticks by sa mali používať pre identifikátory tabuliek a stĺpcov, ale sú potrebné len vtedy, keď je identifikátorom vyhradené kľúčové slovo MySQL alebo keď identifikátor obsahuje medzery alebo znaky mimo obmedzenej množiny (pozri nižšie). Vždy, keď je to možné, je dobré vyhnúť sa používaniu rezervovaných kľúčových slov ako identifikátorov stĺpcov alebo tabuliek, aby ste sa vyhli problémom s úvodzovkami.

    Jednoduché úvodzovky by sa mali používať pre reťazcové hodnoty, napríklad v zozname VALUES (). MySQL podporuje dvojité úvodzovky aj pre hodnoty reťazcov, ale iné RDBMS viac akceptujú jednoduché úvodzovky, takže je dobré namiesto dvojitých úvodzoviek použiť jednoduché úvodzovky.

    MySQL tiež očakáva, že doslovné hodnoty ​​DATE a DATETIME budú mať jednoduché úvodzovky ako reťazce, ako napríklad "2001-01-01 00:00:00". Ďalšie informácie nájdete v dokumentácii k literatúre o dátume a čase, konkrétne o alternatívach k použitiu spojovníka ako oddeľovača segmentov v reťazcoch dátumu.

    Takže pomocou vášho príkladu by som dvakrát prehodil reťazec PHP a použil jednoduché úvodzovky pre hodnoty „val1“, „val2“. NULL je kľúčové slovo MySQL a špeciálna (ne) hodnota, a preto sa nepoužíva.

    Žiadne z týchto ID tabuliek alebo stĺpcov nie sú vyhradené slová ani nepoužívajú znaky, ktoré sa majú citovať, ale aj tak som ich citoval spätne (viac o tom neskôr ...).

    Funkcie súvisiace s RDBMS (ako napríklad NOW () v MySQL) by sa nemali citovať, hoci ich argumenty sa riadia rovnakými citačnými alebo citačnými pravidlami, ktoré už boli spomenuté.

    Začiarknutie (`) tabuľka a stĺpec ───────┬─────┬──┬──┬──┬──┬──┬───┬─────┬──┬──┬──┬──┬───┬──┬────‬ ┬──┬────────┐ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ $ dotaz = " INSERT INTO `table` (` id`, `col1`,` col2`, `date`,` updated`) VALUES (NULL, "val1", "val2", "2001-01-01", NOW ())"; Kľúčové slovo bez úvodzoviek ─────┴┴┴┘ │ │ │ │ │ │ │││││ Reťazce s jednoduchými úvodzovkami (") ┴‴└┴‴└┴┴┴┴└┴┴┴┴┴┴└─┴└── ┴────┘ │ │ │││││ DÁTUM s jednoduchými úvodzovkami (") ─────────────││││││ DÁTUM s jednoduchými úvodzovkami funkcia ───────┘ │││││ nekótovaného ───────────────────────────────── ── ─────┴┴┴┴┘

    Variabilná interpolácia

    Vzory citácií pre premenné sa nemenia, aj keď ak máte v úmysle interpolovať premenné priamo do reťazca, v PHP musia byť citované dvojito. Len sa uistite, že sa vyhýbate premenným na správne použitie v SQL. (Namiesto toho sa odporúča použiť API, ktoré podporuje pripravené príkazy ako obranu proti vstrekovaniu SQL.)

    // To isté s niektorými nahradeniami premenných // Tu je názov tabuľky premenných $ tabuľka v spätných úvodzovkách a premenné // v zozname VALUES sú v jednoduchých úvodzovkách $ dotaz = "INSERT INTO „$ stôl“.(`id`,` col1`, `col2`,` date`) VALUES (NULL, "$ val1", "$ val2", "dátum $")";

    Pripravené výkazy

    Pri práci s pripravenými výkazmi si prezrite dokumentáciu, aby ste zistili, či sa majú zahrnúť výplňové vyhlásenia. Najpopulárnejšie API dostupné v PHP, PDO a MySQLi predpokladajú neoprávnené zástupné symboly, ako väčšina pripravených príkazov API v iných jazykoch:

    // príklad CHOP s pomenovanými parametrami, neuvedený $ dotaz = "INSERT INTO` table` (`id`,` col1`, `col2`,` date`) VALUES (: id,: col1,: col2,: date) " ; // Príklad MySQLi s? parametre, neuvedený $ dotaz = "INSERT INTO` table` (`id`,` col1`, `col2`,` date`) VALUES (?,?,?,?) ";

    Symboly vracajúce spätný odkaz v identifikátoroch:

    Napríklad:

    To isté možno urobiť pre názvy tabuliek a názvy polí. Toto je veľmi dobrý zvyk, ak pripojíte svoje ID databázy k zadným oknám.

    Pozrite si túto odpoveď a dozviete sa viac o backtrackingu.

    Teraz o Double quotes & Single Quotes (Michael to už spomenul).

    Ale na definovanie hodnoty musíte použiť jednoduché alebo dvojité úvodzovky. Pozrime sa na ďalší príklad.

    INSERT INTO `tablename` (` id, `title`) VALUES (NULL, title1);

    Tu som schválne zabudol zabaliť title1 do úvodzoviek. Server teraz akceptuje title1 ako názov stĺpca (t.j. id). Ak teda chcete uviesť, že ide o hodnotu, musíte použiť dvojité alebo jednoduché úvodzovky.

    INSERT INTO `tablename` (` id, `title`) VALUES (NULL," title1 ");

    Teraz, v kombinácii s PHP, dvojité úvodzovky a jednoduché úvodzovky uľahčujú písanie dopytu. Pozrime sa na upravenú verziu dotazu vo vašej otázke.

    $ dotaz = "INSERT INTO` table` (`id`,` col1`, `col2`) VALUES (NULL," $ hodnota1 "," $ hodnota2 ")";

    Teraz pomocou dvojitých úvodzoviek v PHP vytvoríte premenné $ val1 a $ val2 na použitie ich hodnôt, čím vytvoríte platný dotaz. Páči sa mi to

    $ val1 = "moja hodnota 1"; $ val2 = "moja hodnota 2"; $ dotaz = "INSERT INTO` table` (`id`,` col1`, `col2`) VALUES (NULL," $ hodnota1 "," $ hodnota2 ")";

    INSERT INTO `table` (` id`, `col1`,` col2`) VALUES (NULL, "moja hodnota 1", "moja hodnota 2")

    Bolo tu veľa užitočných odpovedí, ktoré všetky vyvrcholili v dvoch bodoch.

    1. BACKTICKS (`) sa používajú okolo názvov identifikátorov.
    2. Okolo hodnôt sa používajú JEDNA UVÁDZAČKY (“).

    A ako povedal @MichaelBerkowski

    Backticks by sa mali používať pre identifikátory tabuliek a stĺpcov, ale sú potrebné len vtedy, keď je identifikátorom vyhradené kľúčové slovo MySQL alebo keď identifikátor obsahuje medzery alebo znaky mimo obmedzenej množiny (pozri nižšie). Vždy, keď je to možné, je dobré vyhnúť sa používaniu rezervovaných kľúčových slov ako identifikátorov stĺpcov alebo tabuliek, aby ste sa vyhli problémom s úvodzovkami.

    Existuje prípad, keď identifikátor nemôže byť vyhradené kľúčové slovo alebo obsahujú medzery alebo znaky mimo obmedzenej množiny, ale určite vyžadujú spätné odkazy okolo nich.

    123E10 je platný názov identifikátora, ale aj platný literál INTEGER.

    [Bez toho, aby ste zachádzali do podrobností, ako získate takéto ID meno] Povedzme, že chcem vytvoriť dočasnú tabuľku s názvom 123456e6.

    Na backticks nie je žiadna CHYBA.

    DB> vytvorte dočasnú tabuľku `123456e6` (` id` char (8)); Dopyt je v poriadku, ovplyvnených 0 riadkov (0,03 s)

    CHYBA, ak nepoužívate spätné volania.

    DB> vytvorte dočasnú tabuľku 123451e6 (`id` char (8)); ERROR 1064 (42000): Máte chybu v syntaxi SQL; skontrolujte príručku, ktorá zodpovedá verzii vášho servera MariaDB, kde nájdete správnu syntax, ktorá sa má použiť v blízkosti "123451e6 (` id` char (8)) "v riadku 1

    123451a6 je však jemný názov identifikátora (žiadne spätné značky).

    DB> vytvorte dočasnú tabuľku 123451a6 (`id` char (8)); Dopyt je v poriadku, ovplyvnených 0 riadkov (0,03 s)

    Je to úplne preto, že 1234156e6 je tiež exponenciálne číslo.

    Jednoduché úvodzovky by sa mali používať pre reťazcové hodnoty, napríklad v zozname VALUES ().

    Obmedzenia sa bežne používajú na označenie identifikátora a môžu byť tiež bezpečné v dôsledku náhodného použitia rezervovaných kľúčových slov.

    Keď sa skombinuje PHP a MySQL, dvojité úvodzovky a jednoduché úvodzovky výrazne zjednodušia čas písania dopytov.

    V MySQL sú dva typy úvodzoviek:

    1. "zahrnúť reťazcové literály
    2. `pre zahrnutie identifikátorov, ako sú názvy tabuliek a stĺpcov

    A potom je tu „toto je špeciálny prípad. Dá sa na to zvyknúť jeden z vyššie uvedených cieľov naraz v závislosti od sql_mode servera sql_mode:

    1. Predvolené"znak" možno použiť na vnorenie reťazcových literálov "
    2. V režime ANSI_QUOTES možno použiť znak na zahrnutie identifikátorov, ANSI_QUOTES

    Nasledujúci dotaz prinesie rôzne výsledky (alebo chyby) v závislosti od režimu SQL:

    VYBERTE "stĺpec" Z tabuľky WHERE foo = "bar"

    ANSI_QUOTES deaktivované

    Dotaz vyberie reťazcový doslovný "stĺpec", kde stĺpec foo sa rovná reťazcu "bar"

    ANSI_QUOTES povolené

    Dotaz vyberie stĺpec stĺpca, kde stĺpec foo sa rovná stĺpcu

    Kedy použiť

    • Navrhujem, aby ste sa vyhli používaniu "takže váš kód nezávisí od režimov SQL
    • Vždy uvádzajte ID, pretože je to osvedčený postup (diskutujte o tejto téme dosť veľa otázok)

    Existuje jasný rozdiel medzi používaním „“ a „“.

    Keď sa v celom texte používa „“, nejde o „transformáciu ani preklad“. Tlačí sa tak, ako je.

    S "", bez ohľadu na to, čo obklopuje, je "preložené alebo premenené" na svoju hodnotu.

    Prekladom/konverziou mám na mysli nasledovné: čokoľvek obsiahnuté v jednoduchých úvodzovkách nebude „preložené“ do ich hodnôt. Budú prijaté, pretože sú v úvodzovkách. Príklad: a = 23, potom echo „$ a“ vygeneruje $ a na štandardnom výstupe. Zatiaľ čo echo „$ a“ vytvorí 23 na štandardnom výstupe.

    Z povahy mojej práce mám vykonávať bezpečnostné audity zdrojového kódu webových aplikácií.
    Veľa webových aplikácií a veľa kódu...

    Nie je žiadnym tajomstvom, že injekcie SQL sú najčastejšou zraniteľnosťou webovej aplikácie na strane servera. Existujú platformy a frameworky, kde sú takéto veci takmer úplne vylúčené, napríklad ORM "ohm a iné. Štatistiky nám však vytrvalo hovoria o absolútnej prevahe webových aplikácií na internete s jednoduchými zreťazenými SQL dotazmi. Okrem toho existujú prípady, kedy ORM je všeobecne použiteľné napríklad vtedy, keď nielen parametre výrazu, ale aj samotná logika dotazu na úrovni operátora musia závisieť od údajov používateľa.

    Takže začnime.

    Zbytočný únik postáv
    Nachádza sa v 83 % webových aplikácií PHP zraniteľných voči SQL injection
    Aplikovanie funkcií unikania znakov ako napr
    mysql_escape_string
    mysql_real_escape_string
    lomítka
    bez úvodzoviek. Najčastejšie sa to prejavuje v číselných parametroch (všetky druhy * _id).
    Príklad
    $ sql = "VYBERTE používateľa ZO zoznamu používateľov WHERE userid =". mysql_real_escape_string ($ _ GET ["uid"]);

    Zdanlivo bezpečný kód, ale len naoko. Tu sa v mojej praxi vkradol najčastejší vzor vstrekovania SQL v PHP. Na napadnutie tejto zraniteľnosti útočník jednoducho nemusí použiť znaky "" \ x00 \ r \ n \ x1a vo vektore útoku.
    Napríklad:
    /index.php?uid=-777 UNION SELECT heslo ZO zoznamu používateľov

    Hľadajte v kóde
    Komplikované sémantikou jazyka. Na jednoduché vyhľadávanie možno použiť egrep:
    egrep -Rin "(vybrať | aktualizovať | vložiť | odstrániť | nahradiť). * (z | nastaviť | do. * (mysql_escape_string | mysql_real_escape_string | pridá lomítka)". | grep -v "[\" "] [" \ "]"

    Logika hľadaného výrazu je nasledovná - nájdite všetky reťazce, v ktorých nie je sekvencia úvodzoviek ("", "", "", "") naľavo od funkcií filtrovania. Metóda, samozrejme, nie je ani zďaleka 100 %, ale nie je možné vyžadovať, aby regulárny výraz vykonal sémantickú analýzu.
    Pre pohodlie zobrazovania informácií môžete funkciu farebne zvýrazniť v konzole:
    egrep -Rin "(vybrať | aktualizovať | vložiť | odstrániť | nahradiť). * (z | nastaviť | do. * (mysql_escape_string | mysql_real_escape_string | pridá lomítka)". | grep -v "[\" "] [" \ "]" | egrep --color "(mysql_escape_string | mysql_real_escape_string | addlashes)"

    Na ochranu pred touto štandardnou zraniteľnosťou je najlepšie použiť typové obsadenie.
    Vždy to funguje rýchlejšie a spoľahlivejšie ako všetky druhy filtrovania a skríningu.
    Vo vyššie uvedenom príklade môže byť záplata takáto:
    $ sql = "VYBERTE používateľa ZO zoznamu používateľov WHERE userid =". intval ($ _ GET ["uid"]);

    Týmto sa krátka skica končí. Vyzývam všetkých vývojárov webu, aby sa pokúsili vyčistiť svoje zdroje pre takéto konštrukcie. Ešte lepšie je rozšíriť vyššie uvedený vyhľadávací skript pre ľudí.