A najmanja pomoć bi bila od funkcije koju ste razvili. PHP: \"Citati\". Pisanje mysql upita, kose crte, izbjegavanje navodnika Pretraživanje u kodu

niz navodnika (11)

Pokušavam pronaći najbolji način za pisanje upita. Takođe razumem važnost doslednosti. Do sada sam nasumično koristio jednostruke navodnike, dvostruke navodnike i pozadine bez ikakvog razmišljanja.

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

Također, u primjeru iznad, uzmite u obzir da "tabela", "kol[n]" i "val[n]" mogu biti varijable.

Koji je standard za ovo? Šta radiš?

Čitam odgovore na slična pitanja oko 20 minuta, ali čini se da nema definitivnog odgovora na ovo pitanje.

Odgovori

Pretpostavimo sada da koristite direktnu post varijablu u MySQL upitu, a zatim je koristite ovako:

$query = "UMETNI U `tabelu` (`id`, `ime`, `e-pošta`) VRIJEDNOSTI (" ".$_POST["id"]." ", " ".$_POST["name"]." ", " ".$_POST["e-pošta"].." ")";

Ovo je najbolja praksa za korištenje PHP varijabli u MySQL-u.

Uglavnom u Mysqlu, ovi tipovi identifikatora se koriste u ` , " , " i () upitima.

    " ili " koristite za uključivanje niza kao vrijednosti "01/26/2014 00:00:00" ili "01/26/2014 00:00:00" . Ovaj identifikator se koristi samo za funkciju stringa "01/26/2014 00:00:00", kao što je now() ili sum ,max.

    ` koristite za uključivanje tablice ili tablice tablice, npr. odaberite column_name iz table_name gdje je id = "2"

    () se koriste samo za jednostavno zatvaranje dijelova upita, na primjer odabir column_name iz table_name gdje (id = "2" i rod = "male") ili ime = "rakesh.

Osim svih (dobro objašnjenih) odgovora, ni jedan nije spomenut u nastavku i često dolazim na ova pitanja i odgovore.

Ukratko; MySQL misli da želite da radite matematiku na vlastitoj tabeli/koloni i tumačite crtice kao što je "e-pošta" kao e-pošta.

Poricanje odgovornosti. Zato sam mislio da ovo dodam kao "FYI" odgovor za one koji su potpuno novi u radu sa bazama podataka i koji možda ne razumiju tehničke pojmove koji su već opisani.

(Postoje dobri odgovori iznad u vezi s SQL prirodom vašeg pitanja, ali ovo može biti relevantno i ako ste novi u PHP-u.)

Možda je važno napomenuti da PHP tretira pojedinačne i dvostruke navodnike različito...

Nizovi sa jednim navodnicima su "literali" i predstavljaju dosta WYSIWYG nizova. PHP stringove sa dvostrukim navodnicima tumači za moguću zamjenu varijabli (povratne reference u PHP-u nisu baš stringovi, one izvršavaju naredbu u ljusci i vraćaju rezultat).

$foo = "bar"; echo "postoji $foo"; // Postoji $foo eho "postoji $foo"; // Postoji eho trake `ls -l`; // ... lista direktorija

Ako su tablice i vrijednosti cols varijable, postoje dva načina:

Sa dvostrukim navodnicima "" kompletan upit je:

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

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

Sa jednostrukim navodnicima "" :

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

Koristite povratne oznake `` kada je naziv stupca/vrijednosti sličan MySQL rezerviranoj ključnoj riječi.

Bilješka. Ako navedete naziv stupca s imenom tablice, koristite povratne kvačice poput ovoga:

`ime_tablice` . `ime_kolone`<- Примечание: исключить. из задних клещей.

Za identifikatore tabela i kolona treba koristiti pozadinske oznake, ali su potrebne samo kada je identifikator rezervisana ključna reč MySQL ili kada identifikator sadrži znakove za razmak ili znakove izvan ograničenog skupa (pogledajte ispod). Često se preporučuje izbjegavanje korištenja rezerviranih ključnih riječi kao identifikatora kolone ili tablice ako je moguće kako bi se izbjegao problem navoda.

Za vrijednosti stringova treba koristiti pojedinačne navodnike, kao što je na listi VALUES(). MySQL podržava dvostruke navodnike i za vrijednosti niza, ali jednostruki navodniki su šire prihvaćeni od strane drugih RDBMS-ova, pa je dobra ideja koristiti jednostruke navodnike umjesto dvostrukih navodnika.

MySQL također očekuje da će literalne vrijednosti DATE i DATETIME biti u jednostrukim navodnicima kao stringovi, kao što je "2001-01-01 00:00:00" . Za više informacija pogledajte dokumentaciju o datumu i vremenu, posebno o alternativama korištenju crtice - kao separatora segmenta u nizovima datuma.

Dakle, koristeći vaš primjer, dvostruko bih bacio PHP string i koristio jednostruke navodnike za vrijednosti "val1", "val2". NULL je ključna riječ MySQL i nije vrijednost i stoga se ne koristi.

Nijedan od ovih identifikatora tabele ili kolone nije rezervisana reč niti koristi znakove koji zahtevaju navođenje, ali sam ih svejedno citirao unazad (više o tome kasnije...).

Funkcije koje se odnose na RDBMS (kao što je NOW() u MySQL) ne treba navoditi, iako su njihovi argumenti podložni istim pravilima ili pravilima citiranja koja su već spomenuta.

Backtick(`) tabela i kolona ┬──── ┬──┬───────┐ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ $query = " UMETNI U `tabelu` (`id`, `col1`, `col2`, `datum`, `ažurirano`) VRIJEDNOSTI (NULL, "val1", "val2", "2001-01-01", SADA())"; ključna riječ bez navodnika ─────┴┴┴┘ │ │ │ │ │ │ │││││ Nizovi pod jednim navodnikom (") ───────────┴─ ───────┴─ ─── ┴────┘ │ │ │││││ Jednostruki navodnici (") DATUM ─────────────────────────────┴── Funkcija bez navoda ───────────────────────── ───────── ───────┴┴┴┴┘

Varijabilna interpolacija

Obrasci navođenja promenljivih se ne menjaju, mada ako nameravate da interpolirate varijable direktno u string, on mora biti stavljen u dvostruke navodnike u PHP-u. Samo se pobrinite da pravilno izbjegnete varijable za korištenje u SQL-u. (Preporučuje se korištenje API-ja koji podržava pripremljene izjave umjesto toga kao obranu od SQL injekcije.)

// Ista stvar sa nekim zamjenama varijabli // Ovdje, ime tablice varijable $table ima povratne navodnike, a varijable // na listi VRIJEDNOSTI su pod jednim navodnicima $query = "INSERT INTO `$table`(`id`, `col1`, `col2`, `date`) VRIJEDNOSTI (NULL, "$val1", "$val2", "$date")";

Pripremljene izjave

Kada radite sa pripremljenim izjavama, pogledajte dokumentaciju da biste utvrdili da li treba uključiti popunjavanje izjava. Najpopularniji API-ji dostupni u PHP-u, PDO-u i MySQLi-u uključuju neovlašćenočuvari mjesta, poput većine pripremljenih API-ja instrukcija na drugim jezicima:

// PDO primjer sa imenovanim parametrima, unquoted $query = "INSERT INTO `table` (`id`, `col1`, `col2`, `date`) VRIJEDNOSTI (:id, :col1, :col2, :date)" ; // Primjer MySQLi sa ? parametri, unquoted $query = "INSERT INTO `table` (`id`, `col1`, `col2`, `date`) VRIJEDNOSTI (?, ?, ?, ?)";

Simboli koji vraćaju povratnu referencu u identifikatorima:

Na primjer:

Isto se može učiniti za imena tablica i polja. Ovo je veoma dobra navika ako povežete svoju bazu podataka sa zadnjim prozorima.

Pogledajte ovaj odgovor da saznate više o obrnutim zaključcima.

Sada o dvostrukim navodnicima i jednostrukim navodnicima (Michael je ovo već spomenuo).

Ali da biste definirali vrijednost, morate koristiti jednostruke ili dvostruke navodnike. Pogledajmo još jedan primjer.

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

Ovdje sam namjerno zaboravio naslov1 staviti u navodnike. Server će sada prihvatiti title1 kao ime kolone (tj. identifikator). Dakle, da biste naznačili da je ovo vrijednost, trebate koristiti dvostruke ili jednostruke navodnike.

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

Sada, u kombinaciji sa PHP-om, dvostruki i jednostruki navodniki čine pisanje upita mnogo lakšim. Pogledajmo izmijenjenu verziju upita u vašem pitanju.

$query = "UMETNI U `tabelu` (`id`, `col1`, `col2`) VRIJEDNOSTI (NULL, "$val1", "$val2"");

Sada, koristeći dvostruke navodnike u PHP-u, učinit ćete da varijable $val1 i $val2 koriste svoje vrijednosti, stvarajući tako valjan upit. like

$val1 = "moja vrijednost 1"; $val2 = "moja vrijednost 2"; $query = "UMETNI U `tabelu` (`id`, `col1`, `col2`) VRIJEDNOSTI (NULL, "$val1", "$val2"");

UMETNI U `tabelu` (`id`, `col1`, `col2`) VRIJEDNOSTI (NULL, "moja vrijednost 1", "moja vrijednost 2")

Ovdje je bilo mnogo korisnih odgovora, koji su uglavnom kulminirali u dvije tačke.

  1. BACKTICKS (`) se koriste oko imena identifikatora.
  2. POJEDINAČNI NAVODNICI (") se koriste oko vrijednosti.

I kao što je rekao @MichaelBerkowski

Za identifikatore tabela i kolona treba koristiti pozadinske oznake, ali su potrebne samo kada je identifikator rezervisana ključna reč MySQL ili kada identifikator sadrži znakove za razmak ili znakove izvan ograničenog skupa (pogledajte ispod). Često se preporučuje izbjegavanje korištenja rezerviranih ključnih riječi kao identifikatora kolone ili tablice ako je moguće kako bi se izbjegao problem navoda.

Postoji slučaj kada identifikator ne može biti rezervirana ključna riječ ili sadrže razmaka znakova ili znakova izvan ograničenog skupa ali definitivno zahtijevaju povratne veze oko njih.

123E10 je važeće ime identifikatora, ali i važeći INTEGER literal.

[Ne ulazeći u detalje kako biste dobili takvo ID ime] Recimo da želim da kreiram privremenu tabelu pod nazivom 123456e6.

Nema GREŠKE na poleđini.

DB > kreiraj privremenu tablicu `123456e6` (`id` char (8)); Upit je u redu, zahvaćeno 0 redova (0,03 s)

GREŠKA ako ne koristite povratne pozive.

DB > kreiraj privremenu tabelu 123451e6 (`id` char (8)); GREŠKA 1064 (42000): Imate grešku u vašoj SQL sintaksi; provjerite priručnik koji odgovara verziji vašeg MariaDB servera za pravu sintaksu za korištenje u blizini "123451e6 (`id` char (8))" u redu 1

Međutim, 123451a6 je fino ID ime (bez kvačica).

DB > kreiraj privremenu tabelu 123451a6 (`id` char (8)); Upit je u redu, zahvaćeno 0 redova (0,03 sek)

To je u potpunosti zato što je 1234156e6 također eksponencijalni broj.

Za vrijednosti stringova treba koristiti pojedinačne navodnike, kao što je na listi VALUES().

Povratne oznake se obično koriste za označavanje identifikatora i mogu biti bezbedne zbog povremene upotrebe rezervisanih ključnih reči.

U kombinaciji sa PHP-om i MySQL-om, dvostruki i jednostruki navodniki uvelike pojednostavljuju vrijeme pisanja upita.

Postoje dvije vrste citata u MySQL-u:

  1. " da uključi string literale
  2. ` da biste uključili identifikatore kao što su imena tablica i stupaca

I onda postoji "ovo je poseban slučaj. Može se koristiti za jedan od gore navedenih ciljeva u isto vrijeme ovisno o serverskom sql_modeu:

  1. Default"znak" se može koristiti za ugniježđenje literala niza "
  2. U načinu ANSI_QUOTES " simbol se može koristiti za uključivanje identifikatora, ANSI_QUOTES

Sljedeći upit će proizvesti različite rezultate (ili greške) ovisno o SQL načinu rada:

SELECT "kolona" IZ tabele WHERE foo = "bar"

ANSI_QUOTES onemogućen

Upit će odabrati literal niza "kolona" gdje je stupac foo jednak nizu "bar"

Omogućeno ANSI_QUOTES

Upit će odabrati kolonu u kojoj je stupac foo jednak stupcu

Kada koristiti

  • Predlažem da izbjegavate korištenje " tako da vaš kod ne ovisi o SQL načinima
  • Uvijek uključite identifikatore jer je to dobra praksa (prilično pitanja na SO raspravljaju o tome)

Postoji jasna razlika između upotrebe " " i " " .

Kada se " " koristi svuda, nema "transformacije ili prijevoda". Štampano je kako jeste.

Sa " ", sve što okružuje je "prevedeno ili transformirano" u svoju vrijednost.

Ono što mislim pod prijevodom/konverzijom je sljedeće: sve što je sadržano u pojedinačnim navodnicima neće biti "prevedeno" na njihove vrijednosti. Oni će biti prihvaćeni jer su unutar navodnika. Primjer: a=23 , tada će eho "$a" generirati $a na standardnom izlazu. Dok će eho "$a" proizvesti 23 na standardnom izlazu.

(PHP 4 >= 4.3.0, PHP 5)

mysql_real_escape_string — Izbjegava posebne znakove u nizu za korištenje u SQL izrazu

Opis

mysql_real_escape_string (string $unescaped_string [, resurs $link_identifier = NULL]): string

Izbjegava specijalne znakove u unescaped_string, uzimajući u obzir trenutni skup znakova veze tako da ga je sigurno smjestiti u mysql_query(). Ako treba umetnuti binarni podatak, mora se koristiti ova funkcija.

mysql_real_escape_string() poziva MySQL-ovu bibliotečku funkciju mysql_real_escape_string, koja dodaje obrnute kose crte sljedećim znakovima: \x00, \n, \r, \ , " , " i \x1a.

Ova funkcija se uvijek (sa nekoliko izuzetaka) mora koristiti za sigurnost podataka prije slanja upita na MySQL.

Oprez

Sigurnost: podrazumevani skup znakova

Skup znakova mora biti postavljen ili na razini servera ili s API funkcijom mysql_set_charset() da bi to uticalo mysql_real_escape_string() . Pogledajte odjeljak o konceptima o skupovima znakova za više informacija.

Parametri

unescaped_string

Niz koji treba izbjeći.

Link_identifier

MySQL veza. Ako identifikator veze nije naveden, posljednja veza koju je otvorio mysql_connect() pretpostavlja se. Ako takva veza nije pronađena, pokušat će je stvoriti kao da mysql_connect() je bio pozvan bez argumenata. Ako veza nije pronađena ili uspostavljena, an E_UPOZORENJE generiše se greška nivoa.

Povratne vrijednosti

Vraća prikazani niz, ili FALSE na grešku.

Greške/Izuzeci

Izvršavanje ove funkcije bez prisutne MySQL veze također će emitovati E_UPOZORENJE PHP greške nivoa. Izvršite ovu funkciju samo ako postoji važeća MySQL veza.

Primjeri

Primjer #1 Jednostavan mysql_real_escape_string() primjer

//Connect
$link = mysql_connect("mysql_host" , "mysql_user" , "mysql_password" )
ILI die(mysql_error());

//Upit
$query = sprintf ( "IZABIR * OD korisnika GDJE korisnik="%s" I lozinka="%s"",
mysql_real_escape_string($user),
mysql_real_escape_string($password));
?>

Primjer #2 mysql_real_escape_string() zahtijeva primjer povezivanja

Ovaj primjer pokazuje šta se dešava ako MySQL veza nije prisutna prilikom pozivanja ove funkcije.

Gornji primjer će dati nešto slično:

Upozorenje: mysql_real_escape_string(): Nema takve datoteke ili direktorija u /this/test/script.php na liniji 5 Upozorenje: mysql_real_escape_string(): Veza sa serverom nije mogla biti uspostavljena u /this/test/script.php na liniji 5 bool(false) string(41) "SELECT * FROM actors WHERE prezime = """

Primjer #3 Primjer napada SQL injekcijom

// Nismo provjerili $_POST["password"], to bi moglo biti sve što korisnik želi! Na primjer:
$_POST [ "username" ] = "aidan" ;
$_POST [ "password" ] = "" ILI ""="" ;

// Upitajte bazu podataka da provjerite ima li podudarnih korisnika
$query = ( $_POST [ "korisničko ime" ]) " I lozinka=" ( $_POST [ "lozinka" ]) "" ;
mysql_query($query);

// To znači da bi upit poslan MySQL-u bio:
echo $query ;
?>

Upit poslat na MySQL:

Ovo bi omogućilo bilo kome da se prijavi bez važeće lozinke.

Bilješke

Prije upotrebe potrebna je MySQL veza mysql_real_escape_string() inače greška nivoa E_UPOZORENJE se generiše, i FALSE se vraća. Ako link_identifier nije definiran, koristi se posljednja MySQL veza.

Bilješka: mysql_real_escape_string() ne pobjegne % i _ . Ovo su zamjenski znakovi u MySQL ako se kombiniraju sa LIKE, GRANT, ili REVOKE.

prije 8 godina

Samo mala funkcija koja oponaša originalni mysql_real_escape_string ali kojoj nije potrebna aktivna mysql veza.Može se implementirati kao statička funkcija u klasi baze podataka.Nadam se da će nekome pomoći.

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

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

Vrati $inp ;
}
?>

prije 13 godina

Imajte na umu da mysql_real_escape_string ne dodaje obrnute kose crte na \x00, \n, \r, i i \x1a kao što je spomenuto u dokumentaciji, već zapravo zamjenjuje karakter s MySQL prihvatljivim prikazom za upite (npr. \n je zamijenjen sa "\ n" literal). (\, ", i " su označeni kao dokumentirani) Ovo ne mijenja način na koji biste trebali koristiti ovu funkciju, ali mislim da je dobro znati.

prije 6 godina

Nijedna diskusija o bježanju nije potpuna bez da se svima kaže da u osnovi nikada ne biste trebali koristiti vanjski ulaz za generiranje interpretiranog koda. Ovo vrijedi za SQL izraze, ili bilo što na čemu biste pozvali bilo koju vrstu "eval" funkcije.

Dakle, umjesto da koristite ovu strašno pokvarenu funkciju, umjesto toga koristite parametarske pripremljene izjave.

Iskreno, korištenje podataka koje je dao korisnik za sastavljanje SQL izjava treba smatrati profesionalnim nemarom i trebali biste biti odgovorni od strane vašeg poslodavca ili klijenta zbog nekorištenja parametarskih pripremljenih izjava.

Sta to znaci?

To znači umjesto izgradnje SQL naredbe ovako:

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

Trebali biste koristiti mysqli"sprepar() funkciju () da izvršite naredbu koja izgleda ovako:

"UMETNI U VRIJEDNOSTI X (A) (?)"

NB: Ovo ne znači da nikada ne biste trebali generirati dinamičke SQL izraze. Ono što znači je da nikada ne biste trebali koristiti podatke koje daje korisnik za generiranje tih izraza. Svi podaci koje je dao korisnik trebaju biti proslijeđeni kao parametri naredbi nakon što pripremljeno.

Tako, na primjer, ako gradite mali okvir i želite napraviti umetanje u tablicu na osnovu URI zahtjeva, u vašem je najboljem interesu da ne uzimate vrijednost $_SERVER["REQUEST_URI"] (ili bilo koju dio toga) i direktno spojite to sa vašim upitom. Umjesto toga, trebali biste analizirati dio vrijednosti $_SERVER["REQUEST_URI"] koji želite i mapirati ga kroz neku vrstu funkcije ili asocijativnog niza na ne-korisnika Ako mapiranje ne proizvodi vrijednost, znate da nešto nije u redu s podacima koje je dao korisnik.

Nepoštivanje ovoga je uzrok brojnih problema sa SQL injekcijom u Ruby On Rails okviru, iako koristi parametarski pripremljene izraze. Ovako je GitHub u jednom trenutku hakovan. Dakle, nijedan jezik nije imun na ovaj problem. Zato je ovo opšta najbolja praksa, a ne nešto specifično za PHP i zašto biste je ZAISTA trebali usvojiti.

Takođe, trebalo bi da uradite neku vrstu validacije podataka koje pružaju korisnici, čak i kada koristite parametarske pripremljene izraze. To je zato što će podaci koje je dao korisnik često postati dio nekog generiranog HTML-a, a vi želite da osigurate da podaci koje je dao korisnik neće uzrokovati sigurnosne probleme u pretraživaču.

prije 9 godina

Postoji zanimljiva zamisao u primjeru #2 o SQL injekciji: AND ima prioritet nad OR, tako da se ubačeni upit zapravo izvršava kao WHERE (user="aidan" AND password="") ILI ""="", pa se umjesto toga vraćanja zapisa baze podataka koji odgovara proizvoljnom korisničkom imenu (u ovom slučaju "aidan"), on bi zapravo vratio SVE zapise baze podataka. Nikakvim posebnim redoslijedom. Dakle, napadač bi se mogao prijaviti kao bilo koji račun, ali ne nužno sa bilo kojim kontrolu nad kojim računom se radi.

Naravno, potencijalni napadač može jednostavno modificirati svoje parametre kako bi ciljao određene korisnike od interesa:

//Npr. vrijednosti napadača
$_POST [ "username" ] = "" ;
$_POST["password"] = "" ILI korisnik = "administrator" I "" = "";

// Neispravan upit
$query = "SELECT * FROM users WHERE user="$_POST [ korisničko ime ] " I lozinka = " $_POST [ lozinka ] "" ;

echo $query ;

// Upit poslan MySQL bi glasio:
// SELECT * FROM users WHERE user="" AND password="" OR user="administrator" AND ""="";
// koji bi svakome omogućio pristup računu pod nazivom "administrator"

?>

prije 1 godine

@feedr
Njegovu belešku sam razradio na sledeći način:
$string = "asda\0sd\x1aas\\\\\\\\dasd\"asdasd\na\"\"sdasdad";
$array1 = array("\\\\\\\\", "\0", "\n", "\r", """, """, "\x1a");
$array2 = niz("\\\\\\\\\\\\\\\\\\", "\\\0", "\\\n", "\\\r", "\\\ " ", "\\\"", "\\\Z");
echo($string);
echo(PHP_EOL);
for($i=0; $i ako ($i==0)
$p = "/(?ostalo
$p = "/(?eho($i);
eho($p);
echo($array2[$i]);
$string = preg_replace($p, $array2[$i], $string);
echo("\t");
echo($string);
echo(PHP_EOL);
}
echo(PHP_EOL);
echo($string);

Prije 2 godine

Da citiram Sama na Numb Safariju

[ "Nijedna rasprava o bježanju nije potpuna bez da se svima kaže da u osnovi nikada ne biste trebali koristiti vanjski ulaz za generiranje interpretiranog koda. Ovo vrijedi za SQL naredbe, ili bilo što na čemu biste pozvali bilo koju vrstu "eval" funkcije.

Dakle, umjesto da koristite ovu strašno pokvarenu funkciju, umjesto toga koristite parametarske pripremljene izjave.

Iskreno, korištenje podataka koje je dao korisnik za sastavljanje SQL izjava treba smatrati profesionalnim nemarom i trebali biste biti odgovorni od strane vašeg poslodavca ili klijenta jer niste koristili parametarski pripremljene izjave." ]

Sam je u pravu........

Međutim, ne mislim da je razumno zaustaviti svako čišćenje i jednostavno prenijeti zadatak na parametarske pripremljene izjave.

Određeni programer koji radi u određenoj situaciji uvijek će znati više o valjanom unosu (specifično za taj kontekst).

Ako tražite od korisnika da unese vrijednost koju ste mu već dali i znate da sve takve vrijednosti počinju AB****** i da niz treba biti dužine 7 ili 11, ali nikada bilo koje druge dužine, onda imate osnova dobrog sredstva za dezinfekciju - različite dozvoljene dužine niza mogu ukazivati ​​na naslijeđene podatke.

Nikada ne bih želio jednostavno proslijediti smeće koje je zlonamjerni korisnik možda proslijedio kroz obrazac u parametarske pripremljene izjave, uvijek bih želio prvo obaviti vlastite provjere uračunljivosti i u nekim slučajevima one mogu pogriješiti na strani opreza i jednostavno odaberite da potpuno prekinete operaciju baze podataka.

Na taj način se moj DB ne začepi nesigurnim izjavama koje su bezbedne - jednostavno se ne začepi što je bolje.

Sigurnost u slojevima - sanaciju i validaciju i dalje treba razmotriti u svakoj situaciji PRIJE upotrebe pripremljenih izjava.

Osim toga, koliko mogu pročitati u službenom dokumentu
==============================================

"Escaping i SQL injekcija

Vezane varijable se šalju serveru odvojeno od upita i stoga ga ne mogu ometati. Server koristi ove vrijednosti direktno na mjestu izvršenja, nakon što se raščlani predložak izjave. Vezane parametre ne treba izbaciti jer se nikada ne zamjenjuju direktno u nizu upita"

To mi sugerira da se opasnost u unutrašnjosti izbjegava alternativnim rukovanjem, a ne poništavanjem.

To znači da veliki projekat s nekompletnom konverzijom u pripremljene izjave, naslijeđeni kod u različitim dijelovima organizacije ili serveri koji međusobno razgovaraju mogu prenijeti loše vijesti sa imune lokacije ili situacije na onu koja nije imuna.

Sve dok se dezinfekcija izvodi kompetentno bez dodatnih rizika, ja bih se lično držao određenih slojeva sanitacije i onda bih dao pripremljene izjave.


Prvo, malo o tome zašto su ove kose crte općenito potrebne.
Ako zamijenimo bilo koji podatak u upit, onda da bismo razlikovali ove podatke od SQL naredbi, oni moraju biti stavljeni u navodnike.
Na primjer, ako pišete
SELECT * IZ tabele WHERE naziv = Račun
tada će baza podataka odlučiti da je Bill ime drugog polja, neće ga pronaći i ispostavit će grešku. Stoga, zamijenjeni podaci (u ovom slučaju, ime Bill) moraju biti stavljeni u navodnike - tada će ga baza podataka smatrati nizom čija vrijednost mora biti dodijeljena polju imena:
SELECT * FROM table WHERE name = "Račun"
Međutim, citati se mogu pojaviti iu samim podacima. npr.
SELECT * FROM table WHERE naziv = "D"Artagnan"
Ovdje će baza podataka odlučiti da je "D" podatak, a Artagnan naredba koju ne poznaje, a također će izbaciti grešku. Stoga je potrebno pratiti sve podatke kako bi se bazi podataka objasnilo da se navodnici (i neki drugi posebni znakovi) koji se nalaze u njima odnose na podatke.
Kao rezultat toga, dobit ćemo ispravan zahtjev koji neće uzrokovati greške:
SELECT * FROM table WHERE name = "D\"Artagnan"

Tako smo otkrili da prilikom zamjene podataka niza u upitu treba poštovati dva pravila:
- svi umetnuti string podaci moraju biti stavljeni u navodnike (jednostruki ili dvostruki, ali su jednostruki prikladniji i češće se koriste).
- posebni znakovi moraju biti prikazani kosim crtama.

Posebno treba napomenuti: dodane kose crte NE ulaze u bazu podataka. Oni su potrebni samo u zahtjevu. Kada udarite u bazu, kose crte se odbacuju. U skladu s tim, uobičajena greška je korištenje trakastih crtica prilikom preuzimanja podataka iz baze podataka.

Sve gore navedeno se odnosi na nizove podataka i datuma. Brojevi se mogu umetnuti bez zatvaranja ili okruženja navodnicima. Ako to uradite onda NUŽNO! prisilite podatke na željeni tip prije nego što ih umetnete u upit, na primjer:
$id = intval ($id);
Međutim, radi jednostavnosti (i pouzdanosti), možete raditi sa brojevima kao sa stringovima (pošto ih mysql i dalje konvertuje u željeni tip). U skladu s tim, pratit ćemo sve podatke unesene u zahtjev i staviti ih pod navodnike.

Također, postoji još jedno pravilo – opciono, ali ga se treba pridržavati kako biste izbjegli greške:
Imena polja i tabela treba staviti u zadnje jednostruke navodnike - "`" (taster sa ovim simbolom nalazi se na standardnoj tastaturi levo od tastera "1"). Uostalom, naziv polja može da se poklapa sa mysql ključne riječi, ali ako koristimo zadnji citat, MySQL će shvatiti da je sve ispravno:
SELECT * FROM `table` WHERE `date` = "2006-04-04"
Trebali biste razlikovati ove navodnike i ne brkati jedan s drugim. Također treba imati na umu da se pozadinske oznake ne uklanjaju kosim crtama.

Dakle, naučili smo kako ispravno zamijeniti podatke u zahtjevu.
ALI! Dinamička konstrukcija upita nije ograničena na zamjenu podataka. Često moramo zamijeniti SQL naredbe i imena polja u upit. I tu prelazimo na temu sigurnosti:

SQL Injection je metoda hakerskog napada kada se podaci preneseni u skriptu modificiraju na takav način da upit generiran u ovoj skripti počinje izvoditi nešto potpuno drugačije od onoga za što je namijenjen.
Pravila za zaštitu od ovakvih napada mogu se podijeliti u dvije točke:
1. Rad sa podacima.
2. Rad s kontrolama upita.

O prvoj tački smo detaljno raspravljali gore. Može se reći da to, u stvari, nije odbrana. Usklađenost s pravilima za dodavanje podataka upitu diktira, prije svega, zahtjevi SQL SINTAKSE. A kao sporedni efekat imamo i zaštitu od hakovanja.

Druga stvar je mnogo teža, budući da ne postoji jedinstveno univerzalno pravilo za podatke - pozadinske oznake neće zaštititi ime polja od hakerske izmjene. Nije moguće koristiti navodnike za zaštitu imena tablica, SQL izraza, LIMIT parametara naredbe i drugih izraza.
Stoga je osnovno pravilo prilikom zamjene kontrolnih elemenata u upitu:
Ako trebate dinamički umetnuti SQL izraze ili imena polja, baza podataka, tablica u upit, ni pod kojim okolnostima ih ne biste trebali umetati direktno u upit.
Sve opcije za takve dodatke moraju biti unapred napisane u vašoj skripti i odabrane na osnovu onoga što je korisnik uneo.
Na primjer, ako trebate proslijediti ime polja narudžbi po operatoru, ni pod kojim okolnostima ga ne smijete direktno zamijeniti. Moramo to prvo provjeriti. Na primjer, napravite niz važećih vrijednosti i zamijenite ga u zahtjev samo ako je proslijeđeni parametar prisutan u ovom nizu:
$orders =array("name" , "price" , "qty");
$key = array_search($_GET["sort"], $orders));
$orderby = $orders [ $key ];
$query = "ODABIR * IZ `tabela` ORDER BY$orderby " ;

Pretražujemo niz unaprijed opisanih opcija za riječ koju je unio korisnik, a ako je pronađemo, odabiremo odgovarajući element niza. Ako nije pronađeno podudaranje, prvi element niza će biti odabran.
Dakle, ono što se zamjenjuje u zahtjevu nije ono što je korisnik unio, već ono što je napisano u našoj skripti.
Isto se mora učiniti i u svim ostalim slučajevima.
Na primjer, ako se klauzula WHERE dinamički generira:
if (!empty($_GET [ "price" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "price" ]). """ ;
$query = "SELECT * FROM `table` WHERE $where " ;

Teško mi je zamisliti slučaj u kojem se naziv tablice može dinamički umetnuti u upit, ali ako se to dogodi, onda ime također treba umetnuti samo iz skupa unaprijed definiranog u skripti.
Parametri operatora LIMIT bi trebali biti prisiljeni na cjelobrojni tip pomoću aritmetičkih operacija ili funkcije intval().
Nemojte misliti da ovdje navedeni primjeri iscrpljuju sve opcije za dinamičku konstrukciju upita. Vi samo trebate razumjeti princip i primijeniti ga u svim takvim slučajevima.

Zbog prirode mog posla moram obavljati sigurnosne revizije izvornog koda web aplikacija.
Mnogo web aplikacija i puno koda...

Nije tajna da su ranjivosti SQL injekcije najčešća od svih ranjivosti web aplikacija na strani servera. Postoje platforme i okviri gdje su takve stvari gotovo potpuno isključene, na primjer ORM i tako dalje. Ali statistika nam uporno govori o apsolutnoj prevlasti web aplikacija s jednostavnim spojenim SQL upitima na Internetu. Osim toga, postoje slučajevi gdje je ORM općenito primjenjiv Ne može, na primjer, kada ne samo parametri izraza, već i sama logika upita na razini operatora moraju ovisiti o korisničkim podacima.

Dakle, počnimo.

Beskorisno izbjegavanje znakova
Nalazi se u 83% PHP web aplikacija ranjivih na SQL injekcije
Korištenje escape funkcije za znakove kao što su
mysql_escape_string
mysql_real_escape_string
dodava kose crte
bez navodnika. Najčešće se manifestuje u numeričkim parametrima (sve vrste *_id).
Primjer
$sql = "IZABIR korisnika SA liste korisnika GDJE userid=".mysql_real_escape_string($_GET["uid"]);

Čini se da je siguran kod, ali samo na površini. Najčešći obrazac SQL injekcija u PHP-u u mojoj praksi se uvukao ovdje. Da bi napao ovu ranjivost, napadač jednostavno treba izbjeći korištenje " " \x00 \r \n \x1a znakova u vektoru napada.
Na primjer:
/index.php?uid=-777 UNION SELECT lozinku SA korisničke liste

Traži u kodu
Komplikovano semantikom jezika. Za jednostavnu pretragu možete koristiti egrep:
egrep -Rin "(odaberite|ažuriraj|ubaci|izbriši|zamijeni).*(od|postavi|u).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]"

Logika izraza za pretraživanje je sljedeća: pronađite sve redove u kojima nema niza navodnika ("", "", "", "") lijevo od funkcija filtriranja. Metoda je, naravno, daleko od 100%, ali je nemoguće zahtijevati regularni izraz za obavljanje semantičke analize.
Da biste olakšali prikaz informacija, možete istaknuti funkciju u boji u konzoli:
egrep -Rin "(odaberite|ažuriraj|ubaci|izbriši|zamijeni).*(od|postavi|u).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|addslashes)"

Da biste se zaštitili od ove ranjivosti zamjenskih znakova, najbolje je koristiti uvođenje tipova.
Ovo uvijek radi brže i pouzdanije je od svih vrsta filtriranja i filtriranja.
Za gornji primjer, zakrpa bi mogla biti ovakva:
$sql = "IZABIR korisnika SA liste korisnika GDJE userid=".intval($_GET["uid"]);

Ovim se završava kratki esej. Pozivam sve web programere da pokušaju provjeriti svoje izvore za takve dizajne. Još bolje, proširite datu skriptu za pretraživanje za ljude.

Dakle, u osnovi sam iskopao duboko u oblasti MySQL-a i PHP-a... posebno bezbednosnih mera koje treba da preduzmem kada radim sa bazom podataka i unosima obrazaca. Do sada sam smatrao da se sljedeće preporučuje:

  1. Pripremljene izjave
  2. Korištenje _real_escape_string()
  3. NE koristite magične citate jer to zbunjuje baze podataka i na kraju vam daje stvari poput "Nisi to nazvao...".

Sve je ovo super i pazim na to. Međutim, pitao sam se da li da izbjegnem znakove poput znaka dolara [$], znaka postotka [%] i možda drugih. Da li je upit možda protumačio znak dolara kao PHP varijablu? Šta je sa LIKE sintaksom za koju sam čuo da koristi simbol % ili čak zamjenski znak? Pripremljene izjave bi trebale tehnički da se pobrinu za sve ovo, ali ja sam samo želio da budem na sigurnoj strani i da budem siguran da sam sve dobro pokrio. U slučajevima kada zaboravim da koristim pripremljene izjave ili ih jednostavno zanemarim, nadao sam se da će mi ova druga linija odbrane reći da mogu da se riješim vrtoglavice.

Evo šta trenutno koristim za bijeg:

Funkcija escape($connection, $data)( $new_data = trim($data); $new_data = i_real_escape_string($connection, $new_data); $new_data = addcslashes($new_data, "%_$"); $new_data = htmlspecialchars ($new_data, ENT_NOQUOTES); vratiti $new_data; )

Da li je ovo tačno? Da li radim nešto strašno pogrešno? Imajte na umu da ću prilikom vraćanja podataka baze podataka morati ukloniti obrnute kose crte prije znakova $,% i _.

Da li radim nešto strašno pogrešno?

Prvo o vašem istraživanju.

Pripremljene izjave – jedini divna stvar koju si našao.

Iako bi korištenje mysqli_real_escape_string (pod pretpostavkom da koristite pripremljene izjave) bilo beskorisno i štetno(stvarajući rezultat koji ste sami zabilježili: "Zvali ste ne\t...").

A Magični citati su odavno uklonjeni iz jezika - tako da zapravo ne vrijede ništa.

Dakle, čak je i većina vaših početnih premisa očigledno pogrešna.

Sada na vaše pitanje.

Da li je upit možda protumačio znak dolara kao PHP varijablu?

Šta je sa LIKE sintaksom za koju sam čuo da koristi simbol % ili čak zamjenski znak?

Da, dobro ste čuli. Tačna svrha LIKE operatora je da izvrši pretragu uzorka. Onemogućavanje ovih znakova u LIKE-u ne bi imalo ni najmanjeg smisla.

Svaki put kada ćete koristiti operator LIKE, morate odlučiti koji specifični znak ćete koristiti, a koji zabraniti. Ne možete koristiti jednokratno rješenje. Da ne spominjemo da u svim drugim mysql interakcijama znak % nema posebno značenje.

Pripremljene izjave treba tehnički da vode računa o svemu tome

Pripremljene izjave nemaju nikakve veze sa znakovima $ ili %. Pripremljeni izrazi se odnose na SQL injekciju, ali nijedan znak to ne može uzrokovati (ne biste mogli nazvati "injekciju" ispravno korištenim LIKE operatorom, zar ne?).

Konačno, do najgoreg dijela.

U slučaju da zaboravite koristiti pripremljene izjave ili jednostavno zanemarite da ih slijedite,

ništa te neće spasiti.

A najmanja pomoć bi bila od funkcije koju ste razvili.

Sažmite.

  1. Riješite se ove funkcije.
  2. Koristi punila * za predstavljanje svake pojedinačne varijable u upitu.
  3. Izbjegnite znakove % i _ u unosu samo ako će se koristiti u operatoru LIKE i ne želite da se tumače.
  4. Koristite htmlspecialchars() za izlaz, a ne mysql ulaz.

*pročitajte pripremljene izjave ako vam ovaj termin nije poznat.

Ne morate izbjegavati znak dolara. MySQL ne gleda posebno na ovaj znak, a PHP ga prepoznaje samo u izvornom kodu, a ne u vrijednostima stringova (osim ako ne pozovete eval na stringu, ali to je sasvim drugi crv crva).

Trebali biste izbjeći % i _ samo ako koristite korisnički unos kao argument LIKE i ne želite da korisnik može koristiti zamjenske znakove. Ovo se može dogoditi ako obrađujete obrazac za pretragu. Ne morate ga koristiti prilikom pohranjivanja u bazu podataka.

Ne morate koristiti htmlspecialchars kada pristupate bazi podataka. Ovo bi se trebalo koristiti samo kada se podaci prikazuju korisniku na HTML stranici kako bi se spriječilo XSS ubrizgavanje.

Ovisno o tome koji podaci i za što se koriste.

Ako ustanovite da su PHP-ovi zadani gotovi izrazi preveliki i složeni, predlažem da pogledate neke od klasa dostupnih na githubu da biste dobili ideju o pojednostavljenim upitima.

Primjer umetanja upita s ovom klasom

$data = Niz ("login" => "admin", "active" => true, "firstName" => "John", "lastName" => "Doe", "password" => $db->func( "SHA1(?)", Niz ("tajna lozinka+sol")), // lozinka = SHA1("tajna lozinka+sol") "createdAt" => $db->now(), // createdAt = SADA() " expires" => $db->now("+1Y") // istječe = SADA() + interval 1 godina // Podržani intervali [s]sekunda, [m]minuta, [h]sat, [d]dan, [M] mjesec, [G] godina); $id = $db->insert("korisnici", $podaci); if ($id) echo "korisnik je kreiran. Id=" . $id; else echo "umetanje nije uspjelo: " . $db->getLastError();