A najmenšia pomoc by bola z funkcie, ktorú ste vyvinuli. PHP: \"Citácie\". Písanie mysql dotazov, lomky, úvodzovky Vyhľadávanie v kóde

reťazec úvodzoviek (11)

Snažím sa nájsť najlepší spôsob, ako písať otázky. Tiež chápem, že je dôležité byť konzistentný. Doteraz som náhodne používal jednoduché úvodzovky, dvojité úvodzovky a spätné začiarknutia bez akéhokoľvek skutočného premýšľania.

$query = "INSERT INTO table (id, col1, col2) VALUES (NULL, val1, value2)";

Vo vyššie uvedenom príklade tiež zvážte, že „table“, „col[n]“ a „val[n]“ môžu byť premenné.

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

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

Odpovede

Teraz predpokladajme, že v dotaze MySQL používate priamu premennú príspevku, potom ju použite takto:

$query = "INSERT INTO `table` (`id`, `name`, `email`) VALUES (" ".$_POST["id"]." ", " ".$_POST["name"]." ", " ".$_POST["e-mail"].." ")";

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

Hlavne v Mysql sa tieto typy identifikátorov používajú v dotazoch ` , " , " 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 iba pre funkciu reťazca „01/26/2014 00:00:00“, ako napríklad now() alebo sum ,max .

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

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

Okrem všetkých (dobre vysvetlených) odpovedí neboli nižšie uvedené žiadne a na tieto otázky a odpovede prichádzam často.

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 tých, ktorí sú úplne noví v práci s databázami a ktorí nemusia rozumieť už popísaným technickým výrazom.

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

Možno je dôležité poznamenať, že PHP zaobchádza s jednoduchými a dvojitými úvodzovkami odlišne...

Struny v jednoduchých úvodzovkách sú „doslova“ a je to pomerne veľa reťazcov WYSIWYG. Reťazce v dvojitých úvodzovkách sú interpretované PHP pre možné nahradenie 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ú tabuľky a hodnoty cols premenné, existujú dva spôsoby:

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

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

$query = "INSERT INTO ".$table_name." (id, ".$col1.", ".$col2.") HODNOTY (NULL, ".$val1.", ".$val2."" ) ";

S jednoduchými úvodzovkami "" :

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

Ak je názov stĺpca/hodnoty podobný kľúčovému slovu vyhradenému MySQL, použite spätné označovanie ``.

Poznámka. Ak zadáte názov stĺpca s názvom tabuľky, použite spätné začiarknutia 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). Často sa odporúča vyhnúť sa používaniu vyhradených kľúčových slov ako identifikátorov stĺpcov alebo tabuliek, ak je to možné, aby ste sa vyhli problémom s cenovými ponukami.

Jednoduché úvodzovky by sa mali používať pre reťazcové hodnoty, ako napríklad v zozname VALUES(). MySQL podporuje dvojité úvodzovky aj pre hodnoty reťazcov, ale iné RDBMS 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ú uvádzané v jednoduchých úvodzovkách 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, najmä o alternatívach používania spojovníka ako oddeľovača segmentov v reťazcoch dátumu.

Takže pomocou vášho príkladu by som zdvojnásobil reťazec PHP a použil jednoduché úvodzovky pre hodnoty „val1“, „val2“ . NULL je kľúčové slovo MySQL a nemá hodnotu, a preto sa nepoužíva.

Žiadny z týchto identifikátorov tabuľky alebo stĺpca nie sú vyhradené slová ani nepoužívajú znaky, ktoré vyžadujú citácie, ale aj tak som ich citoval spätne (o tom neskôr...).

Funkcie súvisiace s RDBMS (ako napríklad NOW() v MySQL) by nemali byť citované, hoci ich argumenty podliehajú rovnakým pravidlám alebo pravidlám citovania, ktoré už boli spomenuté.

Začiarknutá(`) tabuľka a stĺpec ┬──── ┬──┬───────┐ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ry = ↓ ↓ ↓ $ INSERT INTO `table` (`id`, `col1`, `col2`, `date`, `updated`) VALUES (NULL, "val1", "val2", "2001-01-01", NOW())"; Kľúčové slovo bez úvodzoviek ─────┴┴┴┘ │ │ │ │ │ │ │││││ Reťazce v jednoduchých úvodzovkách (") ─────— ───┴── ┴────┘ │ │ │││││ DÁTUM s jednoduchými úvodzovkami (") ────────────└└─└└─—└└ — ───┴── Necitovaná funkcia ───────────────────────── ──└└─—└─—— ──┴┴┴┴┘

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é dvakrát. Len sa uistite, že ste správne escapovali premenné na použitie v SQL. (Namiesto toho sa odporúča použiť rozhranie 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 $query = "INSERT INTO `$stôl`(`id`, `col1`, `col2`, `date`) VALUES (NULL, "$val1", "$val2", "$date")";

Pripravené výkazy

Pri práci s pripravenými výkazmi si prezrite dokumentáciu, aby ste zistili, či by mali byť zahrnuté aj výplne výkazov. Zahŕňajú najpopulárnejšie API dostupné v PHP, PDO a MySQLi neoprávnené zástupné symboly, ako väčšina pripravených inštrukcií API v iných jazykoch:

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

Symboly, ktoré vracajú 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 zviažete ID databázy so zadnými oknami.

Pozrite si túto odpoveď a dozviete sa viac o spätných odvodeniach.

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. identifikátor). 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 dotazov. Pozrime sa na upravenú verziu dotazu vo vašej otázke.

$query = "INSERT INTO `table` (`id`, `col1`, `col2`) VALUES (NULL, "$val1", "$val2"");

Teraz pomocou dvojitých úvodzoviek v PHP prinútite premenné $val1 a $val2 používať svoje hodnoty, čím vytvoríte platný dotaz. Páči sa mi to

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

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

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

  1. BACKTICKS (`) sa používajú okolo názvov identifikátorov.
  2. SINGLE UMOTES (") sa používajú okolo hodnôt.

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). Často sa odporúča vyhnúť sa používaniu vyhradených kľúčových slov ako identifikátorov stĺpcov alebo tabuliek, ak je to možné, aby ste sa vyhli problémom s cenovými ponukami.

Existuje prípad, keď identifikátor nemôže byť vyhradené kľúčové slovo alebo obsahujú medzery alebo znaky mimo limitovaného súboru 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 som zachádzal do detailov, ako by ste získali takéto ID meno] Povedzme, že chcem vytvoriť dočasnú tabuľku s názvom 123456e6.

Žiadna CHYBA na spätných odrážkach.

DB > vytvoriť 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é volanie.

DB > vytvoriť 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 dobré ID meno (bez spätných označení).

DB > vytvoriť 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, ako napríklad v zozname VALUES().

Backticks sa zvyčajne používajú na označenie identifikátora a môžu byť tiež bezpečné kvôli občasnému použitiu vyhradených kľúčových slov.

V kombinácii s PHP a MySQL, dvojité úvodzovky a jednoduché úvodzovky výrazne zjednodušujú čas písania dotazov.

V MySQL existujú dva typy úvodzoviek:

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

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

  1. Predvolené"znak" možno použiť na vnorenie reťazcových literálov "
  2. V režime ANSI_QUOTES “ možno symbol použiť 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 zakázané

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

Povolené ANSI_QUOTES

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

Kedy použiť

  • Navrhujem, aby ste sa vyhli používaniu ", aby váš kód nezávisel od režimov SQL
  • Vždy zahrňte identifikátory, pretože ide o osvedčený postup (diskutujte o tejto téme pomerne veľa otázok)

Existuje jasný rozdiel medzi použitím " " a " " .

Keď sa v celom texte použije „ “, nedochádza k žiadnej „transformácii alebo prekladu“. Je vytlačený tak, ako je.

S „ “ sa všetko, čo obklopuje, „prekladá alebo premieňa“ na svoju hodnotu.

Prekladom/konverziou myslím toto: čokoľvek obsiahnuté v jednoduchých úvodzovkách nebude „preložené“ na ich hodnoty. 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.

(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 $unescaped_string [, zdroj $link_identifier = NULL]): reťazec

Escape špeciálne znaky v reťazci neunescaped_string s prihliadnutím na 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 údajov 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 o konceptoch 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 zomrie(mysql_error());

//Dopyt
$query = sprintf ( "SELECT * FROM users WHERE user="%s" AND password="%s"",
mysql_real_escape_string($user),
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 na databázu, aby skontroloval, či existujú nejakí vyhovujúci používatelia
$query = ( $_POST [ "používateľské meno" ]) " AND password=" ( $_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 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 );

If(!empty($inp ) && is_string ($inp )) (
return str_replace (array("\\" , "\0" , "\n" , "\r" , """ , """, "\x1a" ), array("\\\\" , "\ \0" , "\\n" , "\\r" , "\\"" , "\\"" , "\\Z" ), $inp );
}

Vrátiť $inp ;
}
?>

pred 13 rokmi

Všimnite si, že mysql_real_escape_string nepridáva spätné lomky pred \x00, \n, \r 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" literteral). (\, ", a " sú zakódované, ako je zdokumentované) Toto nemení spôsob, akým by ste túto funkciu mali používať, ale myslím si, že je dobré vedieť.

pred 6 rokmi

Žiadna diskusia o úniku 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"].)"

Na vykonanie príkazu, ktorý vyzerá takto, by ste mali použiť funkciu Prepare() () mysqli:

"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 sa mali preniesť 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 ho zreťaziť s vaší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 mali SKUTOČNE 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á prednosť pred OR, takže vložený dotaz sa v skutočnosti vykoná ako WHERE (user="aidan" AND password="") ALEBO ""="", takže namiesto toho pri vrátení databázového záznamu zodpovedajúceho ľubovoľnému používateľskému menu (v tomto prípade „aidan“) by to v skutočnosti vrátilo 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ál útoku 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 [ "používateľské meno" ] = "" ;
$_POST["heslo"] = "" ALEBO používateľ = "administrátor" A "" = "";

// Chybný dotaz
$dotaz = "SELECT * FROM users WHERE user="$_POST [ meno používateľa ] " AND 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($string);
echo(PHP_EOL);
for($i=0; $i ak ($i==0)
$p = "/(?inak
$p = "/(?echo($i);
echo($p);
echo($pole2[$i]);
$string = preg_replace($p, $pole2[$i], $string);
echo("\t");
echo($string);
echo(PHP_EOL);
}
echo(PHP_EOL);
echo($string);

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 mal 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 žiadnu 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ýpisov, vždy by som si chcel najskôr urobiť vlastné kontroly zdravého rozumu a v niektorých prípadoch sa môžu pomýliť na strane opatrnosti a jednoducho vyberte úplné prerušenie operácie databázy.

Týmto spôsobom sa moja databáza 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 dotazu, 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 nemusia byť escapované, pretože sa nikdy nenahradia priamo do reťazca dotazu"

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

To znamená, že veľký projekt s neúplnou konverziou na pripravené výpisy, starý kód v rôznych častiach organizácie alebo servery, ktoré sa navzájom rozprávajú, by mohli preniesť zlé správy z imunitného miesta alebo situácie na miesto, ktoré nie je imúnne.

Pokiaľ je dezinfekcia kompetentne vykonaná bez vzniku ďalších rizík, 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 vo všeobecnosti potrebné.
Ak do dotazu dosadíme akékoľvek údaje, tak na odlíšenie týchto údajov od príkazov SQL musia byť umiestnené v úvodzovkách.
Napríklad, ak píšete
SELECT * FROM table WHERE meno = Bill
potom databáza rozhodne, že Bill je názov iného poľa, nenájde ho a vyhodí chybu. Preto musia byť nahradené údaje (v tomto prípade meno Bill) uzavreté v úvodzovkách - potom to databáza bude považovať za reťazec, ktorého hodnota musí byť priradená do poľa názvu:
SELECT * FROM table WHERE name = "Bill"
Úvodzovky sa však môžu objaviť aj v samotných údajoch. napr.
SELECT * FROM table WHERE name = "D"Artagnan"
Tu databáza rozhodne, že "D" sú dáta a Artagnan je príkaz, ktorý nepozná a tiež vyhodí chybu. Preto je potrebné sledovať všetky údaje, aby sme databáze vysvetlili, že v nich nájdené úvodzovky (a niektoré ďalšie špeciálne znaky) odkazujú na údaje.
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 reťazca do dotazu by sa mali dodržiavať dve pravidlá:
- všetky vložené údaje reťazca musia byť uzavreté v úvodzovkách (jednoduché alebo dvojité, ale jednoduché sú pohodlnejšie a častejšie používané).
- špeciálne znaky musia byť označené lomkami.

Osobitne treba poznamenať: pridané lomky NEVSTUPUJÚ do databázy. Potrebné sú iba v žiadosti. Pri náraze na základňu sú lomky odhodené. Preto je častou chybou používanie stripslashes pri získavaní údajov z databázy.

Všetko vyššie uvedené platí pre reťazcové dáta a dátumy. Čísla je možné vkladať bez koncoviek alebo ich obklopovať úvodzovkami. 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ť s číslami ako s reťazcami (keďže mysql ich stále prevádza na požadovaný typ). Podľa toho vysledujeme všetky údaje vložené do žiadosti a uzatvoríme ich do úvodzoviek.

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 symbolom 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 spätnú citáciu, potom 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. Mali by ste si tiež zapamätať, že lomítkam neuniknete.

Takže sme sa naučili, ako správne nahradiť údaje do požiadavky.
ALE! Dynamická konštrukcia dotazu nie je obmedzená na nahradenie údajov. Často musíme do dotazu nahradiť príkazy SQL a názvy polí. A tu prejdeme k téme bezpečnosti:

SQL Injection je metóda hackerského útoku, kedy sú dáta prenášané do skriptu upravené tak, že dotaz vygenerovaný v tomto skripte začne vykonávať niečo úplne iné, 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 dotazu.

Prvý bod sme podrobne rozobrali vyššie. Dá sa povedať, že v skutočnosti nejde o obranu. 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 ťažší, pretože neexistuje jednotné univerzálne pravidlo pre údaje - spätné začiarknutie neochráni názov poľa pred zmenou hackerom. Nie je možné použiť úvodzovky na ochranu názvov tabuliek, príkazov SQL, parametrov príkazov LIMIT a iných príkazov.
Preto základným pravidlom pri nahrádzaní ovládacích prvkov do dotazu je:
Ak potrebujete dynamicky vkladať SQL príkazy alebo názvy polí, databáz, tabuliek do dotazu, tak ich za žiadnych okolností nevkladajte 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 za žiadnych okolností 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:
$orders =array("meno" , "cena" , "množstvo" );
$key = array_search($_GET["sort"], $orders));
$objednávka = $objednávky [ $kľúč ];
$dotaz = "VYBERTE * Z "tabuľky" OBJEDNAŤ PODĽA$objednávka " ;

V poli vopred popísaných možností hľadáme slovo zadané používateľom 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 teda nie je nahradené to, čo používateľ zadal, ale to, čo bolo napísané v našom skripte.
To isté sa musí urobiť vo všetkých ostatných prípadoch.
Napríklad, ak sa klauzula WHERE generuje dynamicky:
if (!empty($_GET [ "cena" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "cena" ]). """ ;
$dotaz = "SELECT * FROM `tabuľka` WHERE $where " ;

Je pre mňa ťažké predstaviť si prípad, keď je možné názov tabuľky vložiť do dopytu dynamicky, ale ak sa tak stane, názov musí byť tiež vložený iba z množiny preddefinovanej 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 pre dynamickú konštrukciu dopytov. Musíte len pochopiť princíp a aplikovať ho vo všetkých takýchto prípadoch.

Vzhľadom na charakter mojej práce musí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 zraniteľnosti vstrekovania SQL sú najbežnejšie zo všetkých zraniteľností webových aplikácií na strane servera. Sú platformy a frameworky, kde sú takéto veci takmer úplne vylúčené, napríklad ORM a pod. Ale štatistiky nám vytrvalo hovoria o absolútnej prevahe webových aplikácií s jednoduchými zreťazenými SQL dopytmi na internete. Okrem toho existujú prípady, keď je ORM všeobecne použiteľné Nemôže napríklad vtedy, keď nielen parametre výrazov, ale aj samotná logika dopytu na úrovni operátora musia závisieť od údajov používateľa.

Takže, začnime.

Zbytočný únik postavy
Nachádza sa v 83 % webových aplikácií PHP zraniteľných voči SQL injekciám
Pomocou funkcie escape pre znaky 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"]);

Zdá sa, že ide o bezpečný kód, ale iba na povrchu. Vkradol sa sem najbežnejší vzor SQL injekcií v PHP v mojej praxi. Na napadnutie tejto zraniteľnosti sa útočník jednoducho musí vyhnúť použitiu znakov " " \x00 \r \n \x1a v útočnom vektore.
Napríklad:
/index.php?uid=-777 UNION SELECT heslo ZO zoznamu užívateľov

Hľadajte v kóde
Komplikované sémantikou jazyka. Pre jednoduché vyhľadávanie môžete použiť egrep:
egrep -Rin "(vyberte|aktualizovať|vložiť|vymazať|nahradiť).*(z|nastaviť|do).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]"

Logika hľadaného výrazu je nasledovná: nájdite všetky riadky, v ktorých nie je sekvencia znakov úvodzoviek ("", "", "", "") naľavo od funkcií filtrovania. Metóda, samozrejme, nie je ani zďaleka 100 %, ale na vykonanie sémantickej analýzy nie je možné vyžadovať regulárny výraz.
Na uľahčenie zobrazovania informácií môžete funkciu v konzole farebne zvýrazniť:
egrep -Rin "(vyberte|aktualizovať|vložiť|vymazať|nahradiť).*(z|nastaviť|do).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|addslashes)"

Na ochranu pred touto zraniteľnosťou so zástupnými znakmi je najlepšie použiť pretypovanie.
Toto vždy 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 esej končí. Vyzývam všetkých webových vývojárov, aby sa pokúsili skontrolovať svoje zdroje pre takéto návrhy. Ešte lepšie je rozbaliť daný vyhľadávací skript pre ľudí.

Takže v podstate som sa pohrabal hlboko v oblastiach MySQL a PHP... konkrétne o bezpečnostných opatreniach, ktoré by som mal prijať pri práci s databázou a vstupmi do formulárov. Doposiaľ som zistil, že veľmi odporúčame nasledovné:

  1. Pripravené výkazy
  2. Použitie _real_escape_string()
  3. NEPOUŽÍVANIE magických úvodzoviek, pretože to mätie databázy a nakoniec vám dáva veci ako „Nevolal si to...“.

Toto všetko je skvelé a dávam si na to pozor. Bol som však zvedavý, či by som mal uniknúť znakom ako znak dolára [$], znak percenta [%] a možno aj iné. Mohol by dotaz interpretovať znak dolára ako premennú PHP? A čo syntax LIKE, o ktorej som počul, že používa symbol % alebo dokonca zástupný znak? Pripravené vyhlásenia by sa o to všetko technicky mali postarať, ale ja som len chcel byť na bezpečnej strane a uistiť sa, že som všetko správne zahrnul. V prípadoch, keď zabudnem použiť pripravené vyhlásenia alebo ich jednoducho zanedbávam, som dúfal, že táto druhá línia obrany mi môže povedať, že sa môžem zbaviť závratov.

Tu je to, čo momentálne používam na únik:

Funkcia escape($connection, $data)( $new_data = trim($data); $new_data = i_real_escape_string($connection, $new_data); $new_data = addcslashes($new_data, "%_$"); $new_data = htmlšpeciálne znaky ($new_data, ENT_NOQUOTES); návrat $new_data; )

Je to teda správne? Robím niečo strašne zle? Upozorňujeme, že pri vrátení údajov databázy budem musieť odstrániť spätné lomky pred znakmi $,% a _.

Robím niečo strašne zle?

Najprv o vašom výskume.

Pripravené vyhlásenia - jedinýúžasná vec, ktorú si našiel.

Hoci použitie mysqli_real_escape_string (za predpokladu, že používate pripravené príkazy) by bolo zbytočné a škodlivé(vytvorí výsledok, ktorý ste si sami poznamenali: „Volali ste, nie je...“).

A Magic Quotes boli z jazyka už dávno odstránené - takže v skutočnosti nestoja za nič.

Takže aj väčšina vašich počiatočných predpokladov je jednoznačne nesprávna.

Teraz k vašej otázke.

Mohol by dotaz interpretovať znak dolára ako premennú PHP?

A čo syntax LIKE, o ktorej som počul, že používa symbol % alebo dokonca zástupný znak?

Áno, počuli ste správne. Presným účelom operátora LIKE je vykonať vyhľadávanie vzorov. Vypnutie týchto znakov v LIKE by nemalo najmenší zmysel.

Zakaždým, keď sa chystáte použiť operátor LIKE, musíte sa rozhodnúť, ktorý konkrétny znak použijete a ktorý nepovolíte. Nemôžete použiť jednorazové riešenie. Nehovoriac o tom, že vo všetkých ostatných interakciách s mysql znak % nemá žiadny zvláštny význam.

O toto všetko by sa mali technicky postarať pripravené výkazy

Pripravené výkazy nemajú nič spoločné so znakmi $ alebo %. Pripravené príkazy odkazujú na SQL injection, ale nemôže to spôsobiť žiadny znak (nemohli by ste nazvať "injection" správne použitým operátorom LIKE, však?).

Nakoniec k tomu najhoršiemu.

V prípade, že zabudnete použiť pripravené vyhlásenia alebo ich jednoducho zanedbáte,

nič ťa nezachráni.

A najmenšia pomoc by bola z funkcie, ktorú ste vyvinuli.

Zhrnúť.

  1. Zbavte sa tejto funkcie.
  2. Použite výplne * reprezentovať každú jednotlivú premennú v dotaze.
  3. Znaky % a _ uniknite na vstupe iba v prípade, že budú použité v operátore LIKE a nechcete, aby boli interpretované.
  4. Na výstup použite htmlspecialchars(), nie vstup mysql.

*prečítajte si pripravené vyhlásenia, ak vám tento pojem nie je známy.

Nemusíte sa vyhýbať znaku dolára. MySQL sa na tento znak špecificky nepozerá a PHP ho rozpoznáva iba v zdrojovom kóde, nie v hodnotách reťazcov (pokiaľ v reťazci nezavoláte eval, ale to je úplne iný červ).

Znaky % a _ by ste museli opustiť iba vtedy, ak by ste ako argument LIKE používali vstup používateľa a nechceli by ste, aby používateľ mohol používať zástupné znaky. Môže sa to stať, ak spracovávate vyhľadávací formulár. Pri ukladaní do databázy ho nemusíte používať.

Pri prístupe do databázy nemusíte používať špeciálne znaky html. Toto by sa malo používať iba pri zobrazovaní údajov používateľovi na stránke HTML, aby sa zabránilo vstrekovaniu XSS.

Podľa toho, aké dáta a na čo slúžia.

Ak zistíte, že predvolené príkazy PHP sú príliš veľké a zložité, odporúčam vám pozrieť sa na niektoré triedy dostupné na github, aby ste získali predstavu o zjednodušených dotazoch.

Príklad vkladania dopytov s touto triedou

$data = Array ("login" => "admin", "active" => true, "firstName" => "John", "lastName" => "Doe", "password" => $db->func( "SHA1(?)",Array ("tajné heslo+soľ")), // heslo = SHA1("tajné heslo+soľ") "vytvorenéAt" => $db->now(), // vytvorenéAt = TERAZ() " expiruje" => $db->now("+1Y") // expiruje = TERAZ() + interval 1 rok // Podporované intervaly [s]sekunda, [min]minúta, [h]hodina, [d]deň, [Mesiac rok); $id = $db->insert("používatelia", $data); if ($id) echo "užívateľ bol vytvorený. Id=" . $id; else echo "vloženie zlyhalo: ". $db->getLastError();