Dhe ndihma më e vogël do të ishte nga funksioni që keni zhvilluar. PHP: \"Citate\". Shkrimi i pyetjeve mysql, prerjet, ikja e kuotave Kërkimi në kod

vargu i thonjëzave (11)

Po përpiqem të gjej mënyrën më të mirë për të shkruar pyetje. Unë gjithashtu e kuptoj rëndësinë e të qenit konsistent. Deri më tani, unë kam përdorur rastësisht thonjëza teke, thonjëza të dyfishta dhe mbrapa pa ndonjë mendim të vërtetë.

$query = "FUT VLERAT NË tabelën (id, col1, col2) (NULL, val1, val2)";

Gjithashtu, në shembullin e mësipërm, konsideroni se "tabela", "col[n]" dhe "val[n]" mund të jenë variabla.

Cili është standardi për këtë? Çfarë po bën?

Unë kam lexuar përgjigje për pyetje të ngjashme për rreth 20 minuta, por nuk duket se ka një përgjigje përfundimtare për këtë pyetje.

Përgjigjet

Tani supozoni se jeni duke përdorur ndryshoren e postimit të drejtpërdrejtë në pyetjen MySQL, atëherë përdorni atë si kjo:

$query = "INSERT NË `tabela` (`id`, `emri`, `email`) VALUES (" ".$_POST["id"]." ", " ".$_POST["emri"]." ", " ".$_POST["email"].." ")";

Kjo është praktika më e mirë për përdorimin e variablave PHP në MySQL.

Kryesisht në Mysql, këta lloj identifikuesish përdoren në pyetjet ` , " , " dhe ().

    " ose " përdoret për të përfshirë një varg si vlerë "01/26/2014 00:00:00" ose "01/26/2014 00:00:00" . Ky identifikues përdoret vetëm për funksionin e vargut "01/26/2014 00:00:00", si p.sh. tani() ose shuma ,max.

    ` përdoret për të përfshirë një tabelë ose tabelë tabele, p.sh. zgjidhni emrin e kolonës nga emri_tabelës ku id = "2"

    () përdoren vetëm për të bashkangjitur thjesht pjesë të një pyetjeje, për shembull zgjidhni emrin e kolonës nga emri_tabelës ku (id = "2" dhe gjinia = "mashkull") ose emri = "rakesh.

Përveç të gjitha përgjigjeve (të shpjeguara mirë), nuk u përmend asnjë më poshtë dhe unë vij shpesh në këtë pyetje dhe përgjigje.

Me pak fjalë; MySQL mendon se ju doni të bëni matematikë në tabelën/kolonën tuaj dhe interpretoni vizat si "email" si e-mail.

Mohimi i përgjegjësisë. Kështu që mendova ta shtoja këtë si një përgjigje "FYI" për ata që janë krejtësisht të rinj në punën me bazat e të dhënave dhe që mund të mos i kuptojnë termat teknikë të përshkruar tashmë.

(Ka përgjigje të mira më lart në lidhje me natyrën SQL të pyetjes suaj, por kjo mund të jetë gjithashtu e rëndësishme nëse jeni i ri në PHP.)

Mund të jetë e rëndësishme të theksohet se PHP i trajton citatet e vetme dhe të dyfishta në mënyra të ndryshme...

Vargjet me një citim janë "literale" dhe janë mjaft vargje WYSIWYG. Vargjet e dyfishta interpretohen nga PHP për zëvendësimin e mundshëm të variablave (referencat prapa në PHP nuk janë saktësisht vargje, ato ekzekutojnë komandën në shell dhe kthejnë rezultatin).

$foo = "bar"; jehonë "ka një $foo"; // Ka një $foo jehonë "ka një $foo"; // Ka një jehonë bar `ls -l`; // ... një listë drejtorish

Nëse tabelat dhe vlerat e cols janë variabla, atëherë ekzistojnë dy mënyra:

Me thonjëza të dyfishta "" pyetja e plotë është:

$query = "FUT NE $tabela_name (id, $col1, $col2) VLERAT (NULL, "$val1", "$val2")";

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

Me thonjëza të vetme "" :

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

Përdorni backticks `` kur emri i kolonës/vlerës është i ngjashëm me një fjalë kyçe të rezervuar MySQL.

Shënim. Nëse specifikoni një emër kolone me një emër tabele, përdorni prapavijë si kjo:

"emri_tabelës" . "emri_kolona".<- Примечание: исключить. из задних клещей.

Backticket duhet të përdoren për identifikuesit e tabelave dhe kolonave, por nevojiten vetëm kur identifikuesi është një fjalë kyçe e rezervuar MySQL ose kur identifikuesi përmban karaktere hapësinore ose karaktere jashtë grupit të kufizuar (shih më poshtë). Shpesh rekomandohet të shmangni përdorimin e fjalëve kyçe të rezervuara si identifikues të kolonave ose tabelave nëse është e mundur për të shmangur problemin e kuotave.

Thonjëzat e vetme duhet të përdoren për vlerat e vargut, të tilla si në listën VALUES(). Thomat e dyfishta mbështeten nga MySQL gjithashtu për vlerat e vargjeve, por thonjëzat teke pranohen më gjerësisht nga RDBMS-të e tjera, kështu që është një ide e mirë të përdorni thonjëza të vetme në vend të thonjëzave të dyfishta.

MySQL gjithashtu pret që vlerat literale DATE dhe DATETIME të citohen si vargje, të tilla si "2001-01-01 00:00:00". Për më shumë informacion, shihni dokumentacionin e literaturës Data dhe Ora, veçanërisht alternativat për përdorimin e vizës - si ndarës segmenti në vargjet e datave.

Pra, duke përdorur shembullin tuaj, do të dyfishoja vargun PHP dhe do të përdorja thonjëza të vetme për vlerat "val1", "val2". NULL është një fjalë kyçe MySQL dhe një jo-vlerë dhe për këtë arsye nuk përdoret.

Asnjë nga këta identifikues tabelash ose kolonash nuk janë fjalë të rezervuara ose nuk përdorin karaktere që kërkojnë citim, por gjithsesi i citova me mbrapsht (më shumë për këtë më vonë...).

Funksionet e lidhura me RDBMS (siç është NOW() në MySQL) nuk duhet të citohen, megjithëse argumentet e tyre u nënshtrohen të njëjtave rregulla ose rregullave të citimit të përmendura tashmë.

Backtick(`) tabela dhe kolona ┬──── ┬──┬───────┐ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ─ ─ INSERT NË `tabela` (`id`, `col1`, `col2`, `datë`, `përditësuar`) VLERAT (NULL, "val1", "val2", "2001-01-01", TANI())"; Fjalë kyçe e pacituar ─────┴┴┴┘ │ │ │ │ │ │ │││││ │││││ me një kuotë (") vargje ─│── ─ ───┴── ┴────┘ │ │ │││││ Një cituar (") DATE ──────── ─ ───┴── Funksioni i pacituar ────────────────────── ──┴┴┴┴┘

Interpolimi i ndryshueshëm

Modelet e citimit për variablat nuk ndryshojnë, edhe pse nëse keni ndërmend të interpoloni variablat drejtpërdrejt në një varg, ai duhet të citohet dyfish në PHP. Vetëm sigurohuni që t'i shpëtoni siç duhet variablave për t'u përdorur në SQL. (Rekomandohet të përdorni një API që mbështet deklaratat e përgatitura në vend të kësaj si një mbrojtje kundër injektimit SQL.)

// E njëjta gjë me disa zëvendësime të variablave // ​​Këtu, emri i tabelës së variablës $table është cituar me kthim prapa, dhe variablat // në listën VALUES janë një cituar $query = "INSERT INTO `$tabela`("id", "col1", "col2", "data") VLERAT (NULL, "$val1", "$val2", "$date")";

Deklarata të përgatitura

Kur punoni me deklaratat e përgatitura, konsultohuni me dokumentacionin për të përcaktuar nëse duhet të përfshihen plotësuesit e deklaratave. API-të më të njohura të disponueshme në PHP, PDO dhe MySQLi përfshijnë i paautorizuar vendmbajtësit, si shumica e API-ve të udhëzimeve të përgatitura në gjuhë të tjera:

// Shembull PDO me parametra të emërtuar, të pacituar $query = "INSERT INTO `tabela` (`id`, `col1`, `col2`, `date`) VLERAT (:id, :col1, :col2, :date)" ; // Shembull MySQLi me ? parametrat, të pacituara $query = "FUT NE `tabela` (`id`, `col1`, `col2`, `datë`) VLERAT (?, ?, ?, ?)";

Simbolet që kthejnë një referencë prapa në identifikues:

Për shembull:

E njëjta gjë mund të bëhet për emrat e tabelave dhe emrat e fushave. Kjo është shumë zakon i mirë nëse lidhni ID-në tuaj të bazës së të dhënave me dritaret e pasme.

Shikoni këtë përgjigje për të mësuar më shumë rreth konkluzioneve të kundërta.

Tani në lidhje me kuotat e dyfishta dhe ato të vetme (Michael e përmendi tashmë këtë).

Por për të përcaktuar vlerën, duhet të përdorni thonjëza të vetme ose të dyfishta. Le të shohim një shembull tjetër.

INSERT INTO "emri i tabelës" ("id, "titulli") VLERAT (NULL, titulli1);

Këtu kam harruar qëllimisht ta mbështjell titullin1 me thonjëza. Serveri tani do të pranojë titullin1 si emrin e kolonës (d.m.th. identifikuesin). Pra, për të treguar se kjo është një vlerë, duhet të përdorni thonjëza të dyfishta ose të vetme.

INSERT INTO `emri i tabelës` (`id, `titulli`) VLERAT (NULL, "title1");

Tani, të kombinuara me PHP, thonjëzat e dyfishta dhe ato të vetme e bëjnë shumë më të lehtë shkrimin e pyetjeve. Le të shohim versionin e modifikuar të pyetjes në pyetjen tuaj.

$query = "FUT NË `tabela` (`id`, `col1`, `col2`) VLERAT (NULL, "$val1", "$val2"");

Tani, duke përdorur thonjëza të dyfishta në PHP, do t'i bëni variablat $val1 dhe $val2 të përdorin vlerat e tyre, duke krijuar kështu një pyetje të vlefshme. si

$val1 = "vlera ime 1"; $val2 = "vlera ime 2"; $query = "FUT NË `tabela` (`id`, `col1`, `col2`) VLERAT (NULL, "$val1", "$val2"");

INSERT NË `tabela` (`id`, `col1`, `col2`) VLERAT (NULL, "vlera ime 1", "vlera ime 2")

Këtu kishte shumë përgjigje të dobishme, të cilat në përgjithësi kulmuan në dy pika.

  1. BACKTICKS (`) përdoren rreth emrave të identifikuesve.
  2. CITAT E VETËM (") përdoren rreth vlerave.

Dhe siç tha @MichaelBerkowski

Backticket duhet të përdoren për identifikuesit e tabelave dhe kolonave, por nevojiten vetëm kur identifikuesi është një fjalë kyçe e rezervuar MySQL ose kur identifikuesi përmban karaktere hapësinore ose karaktere jashtë grupit të kufizuar (shih më poshtë). Shpesh rekomandohet të shmangni përdorimin e fjalëve kyçe të rezervuara si identifikues të kolonave ose tabelave nëse është e mundur për të shmangur problemin e kuotave.

Ka një rast kur identifikuesi nuk mund të jetë fjalë kyçe e rezervuar ose përmbajnë karaktere të hapësirës së bardhë ose personazhe jashtë grupit të kufizuar por patjetër që kërkojnë backlinks rreth tyre.

123E10 është një emër identifikues i vlefshëm, por gjithashtu një fjalë për fjalë e vlefshme INTEGER.

[Pa hyrë në detaje se si do të merrni një emër id të tillë] Le të themi se dua të krijoj një tabelë të përkohshme të quajtur 123456e6.

Asnjë GABIM në të pasmet.

DB > krijoni tabelën e përkohshme `123456e6` (`id` char (8)); Kërkesa në rregull, 0 rreshta të prekur (0,03 sek)

GABIM nëse nuk përdor kthimet e telefonatave.

DB > krijoni tabelën e përkohshme 123451e6 (`id` char (8)); ERROR 1064 (42000): Ju keni një gabim në sintaksën tuaj SQL; kontrolloni manualin që korrespondon me versionin e serverit tuaj MariaDB për sintaksën e duhur për t'u përdorur pranë "123451e6 (`id` char (8))" në rreshtin 1

Megjithatë, 123451a6 është një emër i mirë ID (pa prapambetje).

DB > krijoni tabelën e përkohshme 123451a6 (`id` char (8)); Kërkesa në rregull, 0 rreshta të prekur (0,03 sek)

Kjo është tërësisht sepse 1234156e6 është gjithashtu një numër eksponencial.

Thonjëzat e vetme duhet të përdoren për vlerat e vargut, të tilla si në listën VALUES().

Pikat e pasme zakonisht përdoren për të treguar një identifikues dhe gjithashtu mund të jenë të sigurt për shkak të përdorimit të rastësishëm të fjalëve kyçe të rezervuara.

Kur kombinohen me PHP dhe MySQL, thonjëzat e dyfishta dhe ato të vetme thjeshtojnë shumë kohën e shkrimit të pyetjeve.

Ekzistojnë dy lloje të kuotave në MySQL:

  1. " për të përfshirë fjalë për fjalë të vargut
  2. ` për të përfshirë identifikues të tillë si emrat e tabelave dhe kolonave

Dhe pastaj ka "ky është një rast i veçantë. Mund të përdoret për një nga objektivat e mësipërm në një kohë në varësi të sql_mode të serverit:

  1. E paracaktuar"karakteri" mund të përdoret për të vendosur fole literale të vargjeve "
  2. Në modalitetin ANSI_QUOTES " simboli mund të përdoret për të përfshirë identifikuesit, ANSI_QUOTES

Pyetja e mëposhtme do të prodhojë rezultate (ose gabime) të ndryshme në varësi të modalitetit SQL:

ZGJIDH "kolona" NGA tabela WHERE foo = "bar"

ANSI_QUOTES i çaktivizuar

Kërkesa do të zgjedhë vargun fjalë për fjalë "kolona" ku kolona foo është e barabartë me vargun "bar"

Aktivizuar ANSI_QUOTES

Pyetja do të zgjedhë kolonën e kolonës ku kolona foo është e barabartë me kolonën

Kur të përdoret

  • Unë ju sugjeroj të shmangni përdorimin e " në mënyrë që kodi juaj të mos varet nga mënyrat SQL
  • Gjithmonë përfshini identifikuesit pasi kjo është praktikë e mirë (mjaft pyetje në SO diskutojnë këtë)

Ka një ndryshim të qartë midis përdorimit të " " dhe " " .

Kur përdoret " ", nuk ka "transformim apo përkthim". Është shtypur ashtu siç është.

Me " ", çdo gjë që rrethon "përkthehet ose shndërrohet" në vlerën e saj.

Ajo që dua të them me përkthim/konvertim është kjo: çdo gjë që përmbahet brenda thonjëzave të vetme nuk do të "përkthehet" në vlerat e tyre. Ata do të pranohen sepse janë brenda kuotave. Shembull: a=23 , pastaj jehona "$a" do të gjenerojë $a në dalje standarde. Ndërsa jehona "$a" do të prodhojë 23 në dalje standarde.

(PHP 4 >= 4.3.0, PHP 5)

mysql_real_escape_string — Shpëton karaktere speciale në një varg për t'u përdorur në një deklaratë SQL

Përshkrim

mysql_real_escape_string (varg $unescaped_string [, burimi $link_identifier = NULL]): varg

Shpëton karakteret speciale në vargun unescaped_, duke marrë parasysh grupin aktual të karaktereve të lidhjes në mënyrë që të jetë e sigurt për ta vendosur atë në një mysql_query (). Nëse do të futen të dhëna binare, duhet të përdoret ky funksion.

mysql_real_escape_string() thërret funksionin e bibliotekës së MySQL-së mysql_real_escape_string, i cili parashtron kthesa të kundërta për karakteret e mëposhtme: \x00, \n, \r, \ , " , " dhe \x1a.

Ky funksion duhet të përdoret gjithmonë (me disa përjashtime) për t'i bërë të dhënat të sigurta përpara se të dërgoni një pyetje në MySQL.

Kujdes

Siguria: grupi i paracaktuar i karaktereve

Seti i karaktereve duhet të vendoset ose në nivel serveri, ose me funksionin API mysql_set_charset() që ajo të ndikojë mysql_real_escape_string() . Shihni seksionin e koncepteve mbi grupet e karaktereve për më shumë informacion.

Parametrat

vargu i paardhur

Vargu që duhet shpëtuar.

Identifikuesi_Lidhja

Lidhja MySQL. Nëse identifikuesi i lidhjes nuk është specifikuar, lidhja e fundit hapet nga mysql_connect () supozohet. Nëse nuk gjendet një lidhje e tillë, do të përpiqet të krijojë një të tillë sikur mysql_connect () ishte thirrur pa argumente. Nëse nuk gjendet apo vendoset asnjë lidhje, një E_PARALAJMËRIM gjenerohet gabim niveli.

Vlerat e Kthimit

Kthen vargun e arratisur, ose I RREMË në gabim.

Gabimet/Përjashtimet

Ekzekutimi i këtij funksioni pa praninë e lidhjes MySQL gjithashtu do të emetojë E_PARALAJMËRIM niveli i gabimeve PHP. Ekzekutoni këtë funksion vetëm me një lidhje të vlefshme MySQL.

Shembuj

Shembulli #1 I thjeshtë mysql_real_escape_string() shembull

//Lidhu
$link = mysql_connect("mysql_host" , "mysql_user" , "mysql_password" )
OSE die(mysql_error());

//Pyetje
$query = sprintf ( "ZGJIDH * NGA përdoruesit WHERE user="%s" AND password="%s"",
mysql_real_escape_string ($user),
mysql_real_escape_string ($password));
?>

Shembulli #2 mysql_real_escape_string() kërkon një shembull lidhjeje

Ky shembull tregon se çfarë ndodh nëse një lidhje MySQL nuk është e pranishme gjatë thirrjes së këtij funksioni.

Shembulli i mësipërm do të nxjerrë diçka të ngjashme me:

Paralajmërim: mysql_real_escape_string(): Nuk ka skedar apo direktori të tillë në /this/test/script.php në rreshtin 5 Paralajmërim: mysql_real_escape_string(): Një lidhje me serverin nuk mund të krijohej në /this/test/script.php në rreshtin 5 bool(false) string(41) "SELECT * FROM aktorët WHERE last_name = """

Shembulli #3 Një shembull i sulmit të injektimit SQL

// Ne nuk kontrolluam $_POST["password"], mund të jetë çdo gjë që përdoruesi dëshiron! Për shembull:
$_POST [ "username" ] = "aidan" ;
$_POST [ "fjalëkalim" ] = "" OSE ""="" ;

// Kërkoni bazën e të dhënave për të kontrolluar nëse ka përdorues që përputhen
$query = ( $_POST [ "username" ]) " DHE fjalëkalimi=" ( $_POST [ "fjalëkalimi" ]) "" ;
mysql_query ($query);

// Kjo do të thotë se pyetja e dërguar në MySQL do të ishte:
jehonë $query ;
?>

Pyetja e dërguar në MySQL:

Kjo do të lejonte këdo që të identifikohej pa një fjalëkalim të vlefshëm.

Shënime

Para përdorimit kërkohet një lidhje MySQL mysql_real_escape_string() përndryshe një gabim niveli E_PARALAJMËRIM gjenerohet, dhe I RREMËështë kthyer. Nëse link_identifier nuk është i përcaktuar, përdoret lidhja e fundit MySQL.

shënim: mysql_real_escape_string() nuk shpëton % dhe _ . Këto janë shkronja të egra në MySQL nëse kombinohen me LIKE, GRANT, ose REVEKOJE.

8 vjet më parë

Vetëm një funksion i vogël që imiton mysql_real_escape_string origjinal, por që nuk ka nevojë për një lidhje aktive mysql. Mund të zbatohet si një funksion statik në një klasë të bazës së të dhënave. Shpresojmë që të ndihmojë dikë.

funksioni mysql_escape_mmic ($inp) (
if (is_array ($inp))
ktheje array_map (__METHOD__ , $inp );

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

Ktheni $inp ;
}
?>

13 vjet më parë

Vini re se mysql_real_escape_string nuk parashtron kthesa të kundërta për \x00, \n, \r dhe \x1a siç përmendet në dokumentacion, por në fakt zëvendëson karakterin me një paraqitje të pranueshme MySQL për pyetje (p.sh. \n zëvendësohet me "\n n" fjalë për fjalë). (\, ", dhe " janë të arratisura siç janë dokumentuar) Kjo nuk ndryshon mënyrën se si duhet ta përdorni këtë funksion, por mendoj se është mirë ta dini.

6 vjet më parë

Asnjë diskutim i arratisjes nuk është i plotë pa u thënë të gjithëve se në thelb nuk duhet të përdorni asnjëherë hyrje të jashtme për të gjeneruar kodin e interpretuar. Kjo vlen për deklaratat SQL, ose çdo gjë që do ta quani çdo lloj funksioni "eval".

Pra, në vend që të përdorni këtë funksion tmerrësisht të prishur, përdorni deklarata të përgatitura parametrike.

Sinqerisht, përdorimi i të dhënave të ofruara nga përdoruesi për të hartuar deklarata SQL duhet të konsiderohet neglizhencë profesionale dhe ju duhet të mbani përgjegjësi nga punëdhënësi ose klienti juaj për mospërdorimin e deklaratave të përgatitura parametrike.

Cfare do te thote ajo?

Do të thotë në vend që të ndërtohet një deklaratë SQL si kjo:

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

Ju duhet të përdorni funksionin përgatit () të mysqli për të ekzekutuar një deklaratë që duket si kjo:

"FUT NË X (A) VLERA(?)"

NB: Kjo nuk do të thotë që nuk duhet të gjeneroni kurrë deklarata dinamike SQL. Ajo që do të thotë është se nuk duhet të përdorni kurrë të dhëna të ofruara nga përdoruesi për të gjeneruar ato deklarata. Çdo e dhënë e ofruar nga përdoruesi duhet të kalojë si parametra në deklaratë pasi të ketë është përgatitur.

Kështu, për shembull, nëse po ndërtoni një kornizë të vogël dhe dëshironi të futni një tabelë në bazë të URI-së së kërkesës, është në interesin tuaj më të mirë të mos merrni vlerën $_SERVER["REQUEST_URI"] (ose ndonjë pjesë e saj) dhe lidhni drejtpërdrejt atë me pyetjen tuaj. Në vend të kësaj, duhet të analizoni pjesën e vlerës së $_SERVER["REQUEST_URI"] që dëshironi dhe ta hartoni atë nëpërmjet një lloj funksioni ose grupi shoqërues në një jopërdorues Vlera e dhënë Nëse hartëzimi nuk prodhon vlerë, ju e dini se diçka nuk është në rregull me të dhënat e ofruara nga përdoruesi.

Dështimi për të ndjekur këtë ka qenë shkaku i një numri problemesh të injektimit SQL në kornizën Ruby On Rails, edhe pse përdor deklarata të përgatitura parametrike. Kështu është hakuar GitHub në një moment. Pra, asnjë gjuhë nuk është imune ndaj këtij problemi. Kjo është arsyeja pse kjo është një praktikë e përgjithshme më e mirë dhe jo diçka specifike për PHP-në dhe pse duhet ta adoptoni VËRTETË.

Gjithashtu, duhet të bëni ende një lloj vërtetimi të të dhënave të ofruara nga përdoruesit, edhe kur përdorni deklarata të përgatitura parametrike. Kjo ndodh sepse ato të dhëna të ofruara nga përdoruesi shpesh bëhen pjesë e disa HTML-ve të krijuara dhe ju dëshironi të siguroheni që të dhënat e dhëna nga përdoruesi nuk do të shkaktojnë probleme sigurie në shfletues.

9 vjet më parë

Në shembullin nr. 2, ka një gjë interesante në lidhje me injektimin SQL: AND ka përparësi ndaj OR, kështu që pyetja e injektuar ekzekutohet në të vërtetë si WHERE (user="aidan" AND password="") OSE ""="", kështu që në vend të kësaj e kthimit të një regjistrimi të bazës së të dhënave që korrespondon me një emër përdoruesi arbitrar (në këtë rast "aidan"), ai në fakt do të kthente TË GJITHA regjistrimet e bazës së të dhënave. Në asnjë mënyrë të veçantë. Pra, një sulmues mund të jetë në gjendje të identifikohet si çdo llogari, por jo domosdoshmërisht me ndonjë kontrolloni se cila llogari është.

Sigurisht që një potencial sulmi thjesht mund të modifikojë parametrat e tij për të synuar përdoruesit specifikë me interes:

//P.sh. vlerat e sulmuesit
$_POST [ "username" ] = "" ;
$_POST["fjalëkalim"] = "" OSE përdorues = "administrator" DHE "" = "";

// Pyetje e keqformuar
$query = "SELECT * FROM users WHERE user="$_POST [ emri i përdoruesit ] " DHE fjalëkalimi = " $_POST [ fjalëkalimi ] " " ;

jehonë $query ;

// Pyetja e dërguar në MySQL do të lexonte:
// SELECT * FROM users WHERE user="" AND password="" OSE user="administrator" AND ""="";
// që do të lejonte këdo që të fitonte qasje në llogarinë e quajtur "administrator"

?>

1 vit me pare

@feedr
Unë e shtjellova shënimin e tij si më poshtë:
$string = "asda\0sd\x1aas\\\\\\\\dasd\"asdasd\na\"\"sdasdad";
$array1 = grup ("\\\\\\\\", "\0", "\n", "\r", """, """, "\x1a");
$array2 = grup("\\\\\\\\\\\\\\\\\", "\\\0", "\\\n", "\\\r", "\\\ " ", "\\\"", "\\\Z");
echo ($string);
echo (PHP_EOL);
për($i=0; $i nëse ($i==0)
$p = "/(?tjetër
$p = "/(?jehonë ($i);
jehonë ($p);
echo ($array2[$i]);
$string = preg_replace($p, $array2[$i], $string);
echo ("\t");
echo ($string);
echo (PHP_EOL);
}
echo (PHP_EOL);
echo ($string);

2 vite më parë

Për të cituar Sam në Numb Safari

[ "Asnjë diskutim i arratisjes nuk është i plotë pa i thënë të gjithëve se në thelb nuk duhet të përdorni asnjëherë hyrje të jashtme për të gjeneruar kodin e interpretuar. Kjo vlen për deklaratat SQL, ose çdo gjë që do të quani çdo lloj funksioni "eval".

Pra, në vend që të përdorni këtë funksion tmerrësisht të prishur, përdorni deklarata të përgatitura parametrike.

Sinqerisht, përdorimi i të dhënave të ofruara nga përdoruesi për të hartuar deklarata SQL duhet të konsiderohet neglizhencë profesionale dhe ju duhet të mbani përgjegjësi nga punëdhënësi ose klienti juaj për mospërdorimin e deklaratave të përgatitura parametrike." ]

Sam ka te drejte.........

Megjithatë, nuk mendoj se është e arsyeshme të ndalojmë të gjitha dezinfektimet dhe thjesht t'i kalojmë detyrën deklaratave të përgatitura parametrike.

Një zhvillues i veçantë që punon në një situatë të veçantë do të dijë gjithmonë më shumë për të dhëna të vlefshme (specifik për atë kontekst).

Nëse i kërkoni një përdoruesi të kalojë një vlerë që ju i keni dhënë tashmë dhe ju e dini që të gjitha vlerat e tilla fillojnë AB****** dhe vargu duhet të jetë i gjatësisë 7 ose 11, por asnjëherë ndonjë gjatësi tjetër, atëherë ju keni baza e një para-sanitizuesi të mirë - gjatësi të ndryshme të lejueshme të një vargu mund të tregojnë të dhëna të vjetra.

Unë kurrë nuk do të doja t'i kaloja thjesht mbeturinat që një përdorues keqdashës mund të ketë kaluar përmes një formulari në deklaratat e përgatitura parametrike, unë gjithmonë do të dëshiroja të bëja kontrollet e mia të arsyeshme në fillim dhe në disa raste këto mund të gabojnë me kujdes dhe thjesht zgjidhni të anuloni plotësisht funksionin e bazës së të dhënave.

Në këtë mënyrë DB-ja ime nuk bllokohet me deklarata të pasigurta të bëra të sigurta - thjesht nuk bllokohet, gjë që është më mirë.

Siguria në shtresa - sanitizimi dhe vlefshmëria duhet të merren parasysh në çdo situatë PARA se të përdorni deklaratat e përgatitura.

Përveç kësaj, për aq sa mund të lexoj në dokumentin zyrtar
==============================================

"Ikja dhe injektimi SQL

Variablat e lidhur dërgohen në server veçmas nga pyetësori dhe kështu nuk mund të ndërhyjnë në të. Serveri i përdor këto vlera direkt në pikën e ekzekutimit, pasi të analizohet shablloni i deklaratës. Parametrat e lidhur nuk kanë nevojë të shmangen pasi ato kurrë nuk zëvendësohen drejtpërdrejt në vargun e pyetjes"

Kjo më sugjeron se rreziku shmanget në brendësi me trajtim alternativ dhe jo me anulim.

Kjo do të thotë që një projekt i madh me konvertim jo të plotë në deklarata të përgatitura, kod i trashëguar në pjesë të ndryshme të një organizate ose serverë që flasin me njëri-tjetrin, të gjithë mund të transmetojnë lajmet e këqija nga një vendndodhje ose situatë imune në një që nuk është imun.

Për sa kohë që sanitizimi kryhet me kompetencë pa pësuar rreziqe shtesë, atëherë personalisht do të qëndroja me shtresa të caktuara dezinfektimi dhe më pas do të thërrisja deklaratat e përgatitura.


Së pari, pak përse nevojiten këto prerje në përgjithësi.
Nëse zëvendësojmë ndonjë të dhënë në një pyetje, atëherë për të dalluar këto të dhëna nga komandat SQL, ato duhet të vendosen në thonjëza.
Për shembull, nëse shkruani
SELECT * NGA tabela WHERE emri = Fatura
atëherë baza e të dhënave do të vendosë që Bill është emri i një fushe tjetër, nuk do ta gjejë atë dhe do të hedhë një gabim. Prandaj, të dhënat e zëvendësuara (në këtë rast, emri Bill) duhet të mbyllen në thonjëza - atëherë baza e të dhënave do ta konsiderojë atë një varg, vlera e të cilit duhet t'i caktohet fushës së emrit:
SELECT * NGA tabela WHERE emri = "Faturë"
Megjithatë, thonjëzat mund të shfaqen edhe në vetë të dhënat. P.sh.
SELECT * NGA tabela WHERE emri = "D"Artagnan"
Këtu baza e të dhënave do të vendosë që "D" është e dhëna, dhe Artagnan është një komandë që nuk e njeh, dhe gjithashtu do të hedhë një gabim. Prandaj, është e nevojshme të gjurmohen të gjitha të dhënat për t'i shpjeguar bazës së të dhënave se thonjëzat (dhe disa karaktere të tjera të veçanta) që gjenden në to i referohen të dhënave.
Si rezultat, ne do të marrim një kërkesë të saktë që nuk do të shkaktojë gabime:
SELECT * NGA tabela WHERE emri = "D\"Artagnan"

Kështu, zbuluam se kur zëvendësojmë të dhënat e vargut në një pyetje, duhen ndjekur dy rregulla:
- të gjitha të dhënat e futura të vargut duhet të mbyllen në thonjëza (të vetme ose të dyfishta, por ato teke janë më të përshtatshme dhe përdoren më shpesh).
- personazhet speciale duhet të shmangen me prerje.

Duhet të theksohet posaçërisht: shkurtesat e shtuara NUK hyjnë në bazën e të dhënave. Ato nevojiten vetëm në kërkesë. Kur goditni bazën, prerjet hidhen poshtë. Prandaj, një gabim i zakonshëm është përdorimi i stripslashes kur merren të dhëna nga baza e të dhënave.

Të gjitha sa më sipër zbatohen për të dhënat dhe datat e vargut. Numrat mund të futen pa i lënë pas ose duke i rrethuar me thonjëza. Nëse e bëni këtë atëherë ME DOMOSDOSHME! detyroni të dhënat në llojin e dëshiruar përpara se t'i futni ato në pyetje, për shembull:
$id = intval ($id);
Megjithatë, për thjeshtësi (dhe besueshmëri), ju mund të punoni me numra si me vargjet (pasi mysql ende i konverton ato në llojin e dëshiruar). Në përputhje me rrethanat, ne do të gjurmojmë çdo të dhënë të futur në kërkesë dhe do ta mbyllim atë në thonjëza.

Gjithashtu, ekziston një rregull tjetër - opsional, por duhet ndjekur për të shmangur gabimet:
Emrat e fushave dhe tabelave duhet të mbyllen në thonjëza të vetme - "`" (tasti me këtë simbol ndodhet në një tastierë standarde në të majtë të tastit "1"). Në fund të fundit, emri i fushës mund të përkojë me mysql fjalë kyçe, por nëse përdorim një citat prapa, atëherë MySQL do të kuptojë se gjithçka është e saktë:
SELECT * FROM `table` WHERE `date` = "2006-04-04"
Ju duhet të bëni dallimin midis këtyre thonjëzave dhe të mos ngatërroni njërën me tjetrën. Ju gjithashtu duhet të mbani mend se prapambetjeve nuk i shpëtojnë prerjet.

Pra, ne kemi mësuar se si të zëvendësojmë saktë të dhënat në një kërkesë.
POR! Ndërtimi dinamik i pyetjeve nuk kufizohet në zëvendësimin e të dhënave. Shpesh ne duhet të zëvendësojmë komandat SQL dhe emrat e fushave në një pyetje. Dhe këtu kalojmë në temën e sigurisë:

SQL Injection është një metodë e sulmit të hakerëve kur të dhënat e transferuara në një skript modifikohen në atë mënyrë që pyetja e krijuar në këtë skript të fillojë të kryejë diçka krejtësisht të ndryshme nga ajo për të cilën ishte menduar.
Rregullat për mbrojtjen nga sulme të tilla mund të ndahen në dy pika:
1. Puna me të dhëna.
2. Puna me kontrollet e pyetjeve.

Pikën e parë e diskutuam në detaje më sipër. Mund të thuhet se në fakt nuk është një mbrojtje. Pajtueshmëria me rregullat për shtimin e të dhënave në një pyetje diktohet, para së gjithash, nga kërkesat e SQL SYNTAX. Dhe si efekt anësor, kemi edhe mbrojtje kundër hakimit.

Pika e dytë është shumë më e vështirë, pasi nuk ka asnjë rregull të vetëm universal sa i përket të dhënave - një prapambetje nuk do të mbrojë emrin e fushës nga modifikimi nga një haker. Nuk është e mundur të përdoren thonjëza për të mbrojtur emrat e tabelave, deklaratat SQL, parametrat e komandave LIMIT dhe deklaratat e tjera.
Prandaj, rregulli bazë kur zëvendësohen elementët e kontrollit në një pyetje është:
Nëse keni nevojë të futni në mënyrë dinamike deklaratat SQL ose emrat e fushave, bazave të të dhënave, tabelave në një pyetje, atëherë në asnjë rrethanë nuk duhet t'i futni ato drejtpërdrejt në pyetje.
Të gjitha opsionet për shtesa të tilla duhet të shkruhen paraprakisht në skriptin tuaj dhe të zgjidhen në bazë të asaj që futi përdoruesi.
Për shembull, nëse ju duhet t'i kaloni një emër fushe porosisë nga operatori, atëherë në asnjë rrethanë nuk duhet ta zëvendësoni atë drejtpërdrejt. Duhet ta kontrollojmë së pari. Për shembull, bëni një grup vlerash të vlefshme dhe zëvendësojeni atë në kërkesë vetëm nëse parametri i kaluar është i pranishëm në këtë grup:
$orders =array("emri" , "çmimi" , "sasia" );
$key = array_search ($_GET[" sort"], $orders));
$orderby = $urdhra [ $key ];
$query = "ZGJIDH * NGA `tabela` RENDOSJE NGA$orderby " ;

Kërkojmë në grupin e opsioneve të parapërshkruara për fjalën e futur nga përdoruesi dhe nëse e gjejmë, zgjedhim elementin përkatës të grupit. Nëse nuk gjendet asnjë përputhje, elementi i parë i grupit do të zgjidhet.
Kështu, ajo që zëvendësohet në kërkesë nuk është ajo që ka futur përdoruesi, por ajo që është shkruar në skriptin tonë.
E njëjta gjë duhet bërë në të gjitha rastet e tjera.
Për shembull, nëse klauzola WHERE gjenerohet në mënyrë dinamike:
if (!empty($_GET [ "çmimi" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "çmimi" ]). """ ;
$query = "ZGJEDH * NGA `tabela` WHERE $where " ;

Është e vështirë për mua të imagjinoj një rast kur një emër tabele mund të futet në një pyetje në mënyrë dinamike, por nëse kjo ndodh, atëherë emri gjithashtu duhet të futet vetëm nga një grup i paracaktuar në skript.
Parametrat e operatorit LIMIT duhet të detyrohen në një tip numër të plotë duke përdorur operacionet aritmetike ose funksionin intval().
Mos mendoni se shembujt e listuar këtu shterojnë të gjitha opsionet për ndërtimin dinamik të pyetjeve. Thjesht duhet të kuptoni parimin dhe ta zbatoni atë në të gjitha rastet e tilla.

Për shkak të natyrës së punës sime, më duhet të kryej kontrolle sigurie të kodit burimor të aplikacioneve në ueb.
Shumë aplikacione në internet dhe shumë kode...

Nuk është sekret që dobësitë e injektimit SQL janë më të zakonshmet nga të gjitha dobësitë e aplikacionit në ueb nga ana e serverit. Ka platforma dhe korniza ku gjëra të tilla janë pothuajse plotësisht të përjashtuara, për shembull ORM e kështu me radhë. Por statistikat vazhdimisht na tregojnë për mbizotërimin absolut të aplikacioneve në ueb me pyetje të thjeshta SQL të lidhura në internet. Përveç kësaj, ka raste kur ORM është përgjithësisht i zbatueshëm Nuk mund, për shembull, kur jo vetëm parametrat e shprehjeve, por edhe vetë logjika e pyetjes në nivel operatori duhet të varen nga të dhënat e përdoruesit.

Pra, le të fillojmë.

Ikja e personazhit të padobishëm
Gjetur në 83% të aplikacioneve në internet PHP të cenueshme ndaj injeksioneve SQL
Përdorimi i funksionit të ikjes për personazhe si p.sh
mysql_escape_string
mysql_real_escape_string
shtojcave
pa thonjëza. Më shpesh manifestohet në parametra numerikë (të gjitha llojet e *_id).
Shembull
$sql = "ZGJEDH përdorues NGA lista e përdoruesve WHERE userid=".mysql_real_escape_string($_GET["uid"]);

Duket se është kod i sigurt, por vetëm në sipërfaqe. Modeli më i zakonshëm i injeksioneve SQL në PHP në praktikën time u fut këtu. Për të sulmuar këtë dobësi, një sulmues thjesht duhet të shmangë përdorimin e karaktereve \x00 \r \n \x1a në vektorin e sulmit.
Për shembull:
/index.php?uid=-777 UNION SELECT fjalëkalimin NGA lista e përdoruesve

Kërkoni në kod
E ndërlikuar nga semantika e gjuhës. Për një kërkim të thjeshtë mund të përdorni egrep:
egrep -Rin "(zgjedh|përditësim|fut|fshij|zëvendësoj).*(nga|vendos|në).*(mysql_escape_string|mysql_real_escape_string|shton vrima)" . | grep -v "[\""]["\"]"

Logjika e shprehjes së kërkimit është si më poshtë: gjeni të gjitha rreshtat në të cilat nuk ka sekuencë të karaktereve të citateve ("", "", "", "") në të majtë të funksioneve të filtrimit. Metoda, natyrisht, është larg nga 100%, por është e pamundur të kërkohet një shprehje e rregullt për të kryer analizën semantike.
Për ta bërë më të lehtë shfaqjen e informacionit, mund të theksoni funksionin me ngjyra në tastierë:
egrep -Rin "(zgjedh|përditësim|fut|fshij|zëvendësoj).*(nga|vendos|në).*(mysql_escape_string|mysql_real_escape_string|shton vrima)" . | grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|slashes)"

Për t'u mbrojtur nga kjo dobësi e shkronjave të egra, është më mirë të përdorni hedhjen e tipit.
Kjo funksionon gjithmonë më shpejt dhe është më e besueshme se të gjitha llojet e filtrimit dhe shqyrtimit.
Për shembullin e mësipërm, patch-i mund të jetë si ky:
$sql = "ZGJIDH përdoruesin NGA lista e përdoruesve WHERE userid=".intval($_GET["uid"]);

Kjo përfundon esenë e shkurtër. I bëj thirrje të gjithë zhvilluesve të uebit të përpiqen të kontrollojnë burimet e tyre për dizajne të tilla. Më mirë akoma, zgjeroni skriptin e dhënë të kërkimit për njerëzit.

Pra, në thelb kam gërmuar thellë në fushat e MySQL dhe PHP... veçanërisht masat e sigurisë që duhet të marr kur merrem me bazën e të dhënave dhe hyrjet e formave. Deri më tani kam gjetur si më poshtë të rekomanduara shumë:

  1. Deklarata të përgatitura
  2. Duke përdorur _real_escape_string()
  3. MOS duke përdorur thonjëza magjike pasi ngatërron bazat e të dhënave dhe përfundon duke ju dhënë gjëra të tilla si "Nuk e quajte...".

E gjithë kjo është e mrekullueshme dhe unë po e mbaj një sy mbi të. Megjithatë, po pyesja veten nëse duhet t'i shpëtoja personazheve si shenja e dollarit [$], shenja e përqindjes [%] dhe ndoshta të tjerëve. Ndoshta pyetja mund ta interpretonte shenjën e dollarit si një variabël PHP? Po në lidhje me sintaksën LIKE që kam dëgjuar përdor simbolin % apo edhe një shkronjë të egër? Deklaratat e përgatitura duhet të kujdesen teknikisht për të gjitha këto, por unë thjesht doja të isha në anën e sigurt dhe të sigurohesha që të mbuloja gjithçka siç duhet. Në rastet kur harroj të përdor deklarata të përgatitura ose thjesht i neglizhoj ato, shpresoja që kjo linjë e dytë e mbrojtjes të më thoshte se mund të shpëtoja nga marramendja.

Ja çfarë po përdor aktualisht për të shpëtuar:

Funksioni escape($connection, $data)($new_data = trim($data); $new_data = i_real_escape_string($connection, $new_data); $new_data = addcslashes($new_data, "%_$"); $new_data = htmlspecials ($new_data, ENT_NOQUOTES); ktheni $new_data;)

Pra, a është kjo e saktë? A po bëj diçka tmerrësisht të gabuar? Ju lutemi, vini re se kur kthej të dhënat e bazës së të dhënave, do të më duhet të heq shenjat e prapme përpara karaktereve $,% dhe _.

A po bëj diçka tmerrësisht të gabuar?

Së pari në lidhje me kërkimin tuaj.

Deklarata të përgatitura - i vetmi gjë e mrekullueshme që gjete.

Megjithëse përdorimi i mysqli_real_escape_string (duke supozuar se po përdorni deklarata të përgatitura) do të ishte të padobishme dhe të dëmshme(duke krijuar një rezultat që e keni shënuar vetë: "Ti thirre nuk është...").

Dhe Kuotat Magjike janë hequr prej kohësh nga gjuha - kështu që nuk vlejnë asgjë.

Pra, edhe shumica e premisave tuaja fillestare janë qartësisht të gabuara.

Tani tek pyetja juaj.

Ndoshta pyetja mund ta interpretonte shenjën e dollarit si një variabël PHP?

Po në lidhje me sintaksën LIKE që kam dëgjuar përdor simbolin % apo edhe një shkronjë të egër?

Po, e dëgjuat mirë. Qëllimi i saktë i operatorit LIKE është të kryejë një kërkim modeli. Çaktivizimi i këtyre personazheve në LIKE nuk do të kishte as kuptimin më të vogël.

Sa herë që do të përdorni operatorin LIKE, duhet të vendosni se cilin karakter specifik të përdorni dhe cilin të mos lejoni. Ju nuk mund të përdorni një zgjidhje një herë. Për të mos përmendur që në të gjitha ndërveprimet e tjera mysql, shenja % nuk ​​ka asnjë kuptim të veçantë.

Deklaratat e përgatitura duhet teknikisht të kujdesen për të gjithë këtë

Deklaratat e përgatitura nuk kanë të bëjnë me shenjat $ ose %. Deklaratat e përgatitura i referohen injektimit SQL, por asnjë karakter nuk mund ta shkaktojë atë (nuk mund ta quani "injeksion" një operator LIKE të përdorur siç duhet, apo jo?).

Më në fund, në pjesën më të keqe.

Në rast se harroni të përdorni deklarata të përgatitura ose thjesht neglizhoni t'i ndiqni ato,

asgjë nuk do t'ju shpëtojë.

Dhe ndihma më e vogël do të ishte nga funksioni që keni zhvilluar.

Përmblidhni.

  1. Hiqni qafe këtë veçori.
  2. Përdorni mbushëse * për të përfaqësuar çdo ndryshore individuale në pyetje.
  3. Shpëtoni % dhe _ karakteret në hyrje vetëm nëse do të përdoren në operatorin LIKE dhe nuk dëshironi që ato të interpretohen.
  4. Përdorni htmlspecialchars() për dalje, jo hyrje mysql.

*lexoni deklaratat e përgatitura nëse ky term nuk është i njohur për ju.

Ju nuk duhet të shmangni shenjën e dollarit. MySQL nuk e shikon këtë karakter në mënyrë specifike, dhe PHP e njeh atë vetëm në kodin burimor, jo në vlerat e vargut (përveç nëse e quani eval në varg, por ky është një krimb tjetër i krimbave).

Do t'ju duhet vetëm t'i shpëtoni % dhe _ nëse do të përdornit hyrjen e përdoruesit si argument LIKE dhe nuk do të dëshironit që përdoruesi të ishte në gjendje të përdorte shkronja të ngurta. Kjo mund të ndodhë nëse jeni duke përpunuar një formular kërkimi. Ju nuk keni nevojë ta përdorni atë kur ruani në një bazë të dhënash.

Ju nuk keni nevojë të përdorni htmlspecialchars kur hyni në bazën e të dhënave. Kjo duhet të përdoret vetëm kur shfaqni të dhëna tek përdoruesi në një faqe HTML për të parandaluar injektimin XSS.

Varësisht se çfarë të dhënash dhe për çfarë përdoret.

Nëse zbuloni se deklaratat e paracaktuara të PHP-së janë shumë të mëdha dhe komplekse, unë sugjeroj të hidhni një sy në disa nga klasat e disponueshme në github për t'ju dhënë një ide të pyetjeve të thjeshtuara.

Një shembull i futjes së pyetjeve me këtë klasë

$data = Array ("login" => "admin", "active" => true, "firstName" => "John", "lastName" => "Doe", "password" => $db->func( "SHA1(?)",Array ("fjalëkalimi sekret+kripë")), // fjalëkalimi = SHA1("fjalëkalimi sekret+kripë") "createdAt" => $db->tani(), // krijuarAt = TANI() " skadon" => $db->tani("+1Y") // skadon = TANI() + intervali 1 vit // Intervalet e mbështetura [s]sekondë, [m]minute, [h]orë, [d]ditë, [Muaj vit); $id = $db->insert("përdoruesit", $data); nëse ($id) echo "user është krijuar. Id=" . $id; else echo "insert dështoi: " . $db->getLastError();