A legkevesebb segítség pedig az általad kifejlesztett függvény lenne. PHP: \"Idézetek\". Mysql lekérdezések írása, perjelek, kilépő idézőjelek Keresés a kódban

idézőjelek karakterlánc (11)

Megpróbálom megtalálni a legjobb módot a lekérdezések írására. Megértem a következetesség fontosságát is. Eddig véletlenszerűen használtam szimpla idézőjeleket, dupla idézőjeleket és hátlapokat, minden komoly gondolkodás nélkül.

$query = "INSERT INTO table (id, col1, col2) ÉRTÉKEK (NULL, érték1, érték2)";

A fenti példában vegye figyelembe azt is, hogy a „tábla”, „oszlop[n]” és „érték[n]” változók lehetnek.

Mi ennek a szabványa? Mit csinálsz?

Körülbelül 20 perce olvasom a válaszokat hasonló kérdésekre, de úgy tűnik, hogy erre a kérdésre nincs végleges válasz.

Válaszok

Most tegyük fel, hogy közvetlen bejegyzés változót használ a MySQL lekérdezésben, majd használja a következőképpen:

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

Ez a legjobb gyakorlat a PHP-változók használatához a MySQL-ben.

Főleg a Mysql-ben az ilyen típusú azonosítók használatosak a ` , " , " és () lekérdezésekben.

    " vagy " használja a karakterlánc hozzáadásához a "01/26/2014 00:00:00" vagy a "01/26/2014 00:00:00" értéket. Ezt az azonosítót csak a "2014.01.26. 00:00:00" karakterlánc függvény használja, például a now() vagy a sum ,max .

    ` használja tábla vagy táblatábla felvételéhez, például válassza ki az oszlop_neve értéket a táblázat_neve közül, ahol id = "2"

    () csak a lekérdezés részeinek egyszerű bezárására szolgál, például válassza ki az oszlopnév értéket a táblanévből, ahol (id = "2" és gender = "férfi") vagy name = "rakesh".

Az összes (jól megmagyarázott) válaszon kívül egyiket sem említettük az alábbiakban, és gyakran jövök erre a kérdésre.

Dióhéjban; A MySQL úgy gondolja, hogy matematikát szeretne végezni a saját táblázatában/oszlopában, és értelmezze a kötőjeleket, mint például az „e-mail” e-mailként .

A felelősség megtagadása. Ezért úgy gondoltam, hogy "Tájékoztatásul" hozzáteszem ezt azoknak, akik teljesen újak az adatbázisokkal való munkában, és esetleg nem értik a már leírt szakkifejezéseket.

(Fentebb vannak jó válaszok a kérdés SQL-jellegére vonatkozóan, de ez akkor is releváns lehet, ha még nem ismeri a PHP-t.)

Fontos megjegyezni, hogy a PHP eltérően kezeli az egy- és a dupla idézőjeleket...

Az egy idézőjeles karakterláncok "literálok", és meglehetősen sok WYSIWYG karakterlánc. A dupla idézőjeles karakterláncokat a PHP értelmezi az esetleges változócsere érdekében (a PHP-ben a háttérhivatkozások nem pontosan karakterláncok, végrehajtják a parancsot a shellben, és visszaadják az eredményt).

$foo = "bár"; echo "van egy $foo"; // Van egy $foo echo "van egy $foo"; // Van egy bár echo `ls -l`; // ... egy könyvtár lista

Ha a cols táblák és értékek változók, akkor két módja van:

Idézőjelekkel "" a teljes lekérdezés:

$query = "INSERT INTO $table_name (id, $col1, $col2) ÉRTÉKEK (NULL, "$érték1", "$érték2")";

$query = "INSERT INTO ".$table_name." (id, ".$col1.", ".$col2.") ÉRTÉKEK (NULL, "".$érték1.", "".$érték2."" ) ";

Egyetlen idézőjelekkel "" :

$query = "INSERT INTO ".$table_name." (id, ".$col1.", ".$col2.") ÉRTÉKEK (NULL, ".$érték1.", ".$érték2.")";

Ha az oszlop/érték neve hasonló egy MySQL lefoglalt kulcsszóhoz, használjon `` visszajelző jelölést.

Jegyzet. Ha egy oszlopnevet táblázatnévvel ad meg, használja a következő visszajelzőket:

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

A backtick-eket tábla- és oszlopazonosítókhoz kell használni, de csak akkor van rájuk szükség, ha az azonosító egy MySQL lefoglalt kulcsszó, vagy ha az azonosító szóköz karaktereket vagy a korlátozott készleten kívüli karaktereket tartalmaz (lásd alább). Gyakran javasolt elkerülni a fenntartott kulcsszavak oszlop- vagy táblázatazonosítóként való használatát, ha lehetséges, hogy elkerülje az idézetproblémát.

A karakterláncértékekhez szimpla idézőjeleket kell használni, például a VALUES() listában. A MySQL támogatja a dupla idézőjeleket a karakterlánc-értékeknél is, de más RDBMS-ek szélesebb körben elfogadják az idézőjeleket, ezért ajánlatos szimpla idézőjeleket használni a dupla idézőjelek helyett.

A MySQL azt is elvárja, hogy a DATE és DATETIME literálértékek egyszeres idézőjelben legyenek karakterláncként, például "2001-01-01 00:00:00". További információkért tekintse meg a Dátum és idő szakirodalmi dokumentációját, különösen a kötőjel alternatíváit - szegmenselválasztóként a dátum karakterláncokban.

Tehát az Ön példáját használva duplán leadnám a PHP karakterláncot, és egyetlen idézőjeleket használnék a "val1", "val2" értékekhez. A NULL egy MySQL kulcsszó és nem érték, ezért nem használják.

A táblázat- vagy oszlopazonosítók egyike sem lefoglalt szó, és nem használ olyan karaktereket, amelyek idézést igényelnek, de mindenesetre visszafelé idéztem őket (erről később...).

Az RDBMS-hez kapcsolódó függvényeket (mint például a NOW() a MySQL-ben) nem szabad idézni, bár argumentumaikra ugyanazok a szabályok vagy idézési szabályok vonatkoznak, amelyeket már említettünk.

Backtick(`) táblázat és oszlop ┬──── ┬──┬───────┐ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ INSERT INTO `table` (`id`, `col1`, `col2`, `date`, `frissítve) ÉRTÉKEK (NULL, "érték1", "érték2", "2001-01-01", MOST())"; Idézet nélküli kulcsszó ─────┴┴┴┘ │ │ │ │ │ │ │││││ Egy idézőjeles (") karakterláncok ──────────── ┴─ ───┴── ┴────┘ │ │ │││││ Egyszeres idézőjel (") DÁTUM ──────────────────────────────────── ── ───┴── Idézet nélküli függvény ──────────────────────────────────────────── ───┴┴┴┴┘

Változó interpoláció

A változók idézési mintái nem változnak, bár ha a változókat közvetlenül egy karakterláncban kívánja interpolálni, akkor azt dupla idézőjelbe kell tenni a PHP-ben. Csak győződjön meg arról, hogy az SQL-ben használatos változókat megfelelően eltávolította. (Az SQL-befecskendezés elleni védekezés érdekében javasolt inkább olyan API-t használni, amely támogatja az előkészített utasításokat.)

// Ugyanez a helyzet néhány változó cseréjével // Itt a $table változó tábla neve visszahúzott idézőjel, és // az VALUES listában lévő változók egyszeres idézőjelek $query = "INSERT INTO `$tábla`(`id`, `col1`, `col2`, `date`) ÉRTÉKEK (NULL, "$val1", "$val2", "$date")";

Elkészített nyilatkozatok

Amikor elkészített kimutatásokkal dolgozik, tekintse át a dokumentációt annak eldöntésére, hogy a kivonatkitöltőket bele kell-e venni. A PHP-ben, PDO-ban és MySQLi-ben elérhető legnépszerűbb API-k magukban foglalják jogosulatlan helyőrzők, mint a legtöbb előkészített utasítás API más nyelveken:

// PDO példa megnevezett paraméterekkel, idézőjel nélküli $query = "INSERT INTO `table` (`id`, `col1`, `col2`, `date`) VALUES (:id, :col1, :col2, :date)" ; // MySQLi példa a ? paraméterek, idézőjelek nélkül $query = "INSERT INTO `table` (`id`, `col1`, `col2`, `date`) ÉRTÉKEK (?, ?, ?, ?)";

Szimbólumok, amelyek visszautalást adnak vissza az azonosítókban:

Például:

Ugyanezt meg lehet tenni a táblanevek és a mezőnevek esetében is. Ez nagyon jó szokás ha az adatbázis azonosítóját hátsó ablakokkal köti össze.

Tekintse meg ezt a választ, ha többet szeretne megtudni a fordított következtetésekről.

Most a kettős idézőjelekről és az egyszeri idézetekről (Michael már említette).

De az érték meghatározásához egyszeres vagy dupla idézőjeleket kell használni. Lássunk egy másik példát.

INSERT INTO `táblanév` (`id, `title`) VALUES (NULL, title1);

Itt szándékosan elfelejtettem idézőjelbe tenni a cím1-et. A szerver most elfogadja a title1-et oszlopnévként (azaz azonosítóként). Tehát annak jelzéséhez, hogy ez egy érték, dupla vagy szimpla idézőjeleket kell használnia.

INSERT INTO `táblanév` (`id, `title`) ÉRTÉKEK (NULL, "title1");

Mostantól a PHP-vel kombinálva a dupla és szimpla idézőjelek sokkal könnyebbé teszik a lekérdezések írását. Nézzük meg a kérdésben szereplő lekérdezés módosított változatát.

$query = "INSERT INTO `table` (`id`, `col1`, `col2`) ÉRTÉKEK (NULL, "$érték1", "$érték2");

Most a PHP-ben dupla idézőjelek használatával a $val1 és $val2 változók értékét használva, érvényes lekérdezést hozva létre. mint

$val1 = "értékem 1"; $val2 = "2. értékem"; $query = "INSERT INTO `table` (`id`, `col1`, `col2`) ÉRTÉKEK (NULL, "$érték1", "$érték2");

INSERT INTO `table` (`id`, `col1`, `col2`) ÉRTÉKEK (NULL, "saját érték 1", "saját érték 2")

Sok hasznos válasz volt itt, amelyek általában két pontban csúcsosodtak ki.

  1. A BACKTICKS (`) az azonosító nevek körül használatos.
  2. Az értékek körül az EGYES IDÉZETEK (") használatosak.

És ahogy @MichaelBerkowski mondta

A backtick-eket tábla- és oszlopazonosítókhoz kell használni, de csak akkor van rájuk szükség, ha az azonosító egy MySQL lefoglalt kulcsszó, vagy ha az azonosító szóköz karaktereket vagy a korlátozott készleten kívüli karaktereket tartalmaz (lásd alább). Gyakran javasolt elkerülni a fenntartott kulcsszavak oszlop- vagy táblázatazonosítóként való használatát, ha lehetséges, hogy elkerülje az idézetproblémát.

Van olyan eset, amikor az azonosító nem lehet fenntartott kulcsszó vagy tartalmazzák szóköz karakterek vagy karakterek a korlátozott készleten kívül de mindenképpen szükség van körülöttük backlinkekre.

A 123E10 érvényes azonosító név, de érvényes INTEGER literál is.

[Anélkül, hogy részletezném, hogyan kaphatna ilyen azonosító nevet] Tegyük fel, hogy szeretnék létrehozni egy 123456e6 nevű ideiglenes táblát.

Nincs HIBA a backticken.

DB > ideiglenes tábla létrehozása `123456e6` (`id` char(8)); Lekérdezés OK, 0 sor érintett (0,03 mp)

HIBA, ha nem használ visszahívást.

DB > ideiglenes tábla létrehozása 123451e6 (`id` char (8)); 1064-es HIBA (42000): Hiba van az SQL szintaxisában; ellenőrizze a MariaDB kiszolgáló verziójának megfelelő kézikönyvben a megfelelő szintaxist az "123451e6 (`id` char (8)" közelében az 1. sorban

Az 123451a6 azonban jó azonosító név (backtickek nélkül).

DB > ideiglenes tábla létrehozása 123451a6 (`id` char (8)); Lekérdezés OK, 0 sor érintett (0,03 mp)

Ez teljes egészében azért van, mert az 1234156e6 is egy exponenciális szám.

A karakterláncértékekhez szimpla idézőjeleket kell használni, például a VALUES() listában.

A backtickeket általában azonosító jelzésére használják, és biztonságosak is lehetnek a fenntartott kulcsszavak alkalmankénti használata miatt.

PHP-vel és MySQL-lel kombinálva a dupla idézőjelek és szimpla idézőjelek nagymértékben leegyszerűsítik a lekérdezések írási idejét.

A MySQL-ben kétféle idézet létezik:

  1. ", hogy belefoglalja a karakterlánc-literálokat
  2. ` azonosítók, például tábla- és oszlopnevek szerepeltetéséhez

És akkor ott van "ez egy speciális eset. Erre lehet használni egy a fenti célok közül egyszerre a kiszolgáló sql_mode-jától függően:

  1. Alapértelmezett A "karakter" karakterlánc literálok egymásba ágyazására használható "
  2. ANSI_QUOTES módban " a szimbólum használható azonosítók feltüntetésére, ANSI_QUOTES

A következő lekérdezés az SQL módtól függően eltérő eredményeket (vagy hibákat) produkál:

SELECT "oszlop" FROM táblázatból WHERE foo = "bar"

ANSI_QUOTES letiltva

A lekérdezés kiválasztja az "oszlop" karakterláncot, ahol a foo oszlop egyenlő a "bar" karakterlánccal.

ANSI_QUOTES engedélyezve

A lekérdezés azt az oszlopot választja ki, ahol a foo oszlop egyenlő az oszloppal

Mikor kell használni

  • Azt javaslom, hogy kerülje a "" használatát, hogy a kódja ne függjön az SQL-módoktól
  • Mindig tüntessen fel azonosítókat, mivel ez bevált gyakorlat (elég néhány kérdés EZért beszélje meg)

Egyértelmű különbség van a " " és a " " használata között .

Ha végig a " " szerepel, akkor nincs "transzformáció vagy fordítás". Úgy van kinyomtatva, ahogy van.

A " " karakterrel bármit is vesz körül, a rendszer "lefordítja vagy átalakítja" az értékére.

Fordítás/konverzió alatt a következőt értem: semmi, ami az idézőjelekben található, nem lesz "lefordítva" az értékükre. Elfogadják, mert idézőjelben vannak. Példa: a=23 , akkor az echo "$a" $a-t generál szabványos kimeneten. Míg az echo "$a" 23-at produkál normál kimeneten.

(PHP 4 >= 4.3.0, PHP 5)

mysql_real_escape_string — A karakterláncban lévő speciális karaktereket megszabadítja az SQL utasításban való használathoz

Leírás

mysql_real_escape_string (karakterlánc $ unnescaped_string [, erőforrás $link_identifier = NULL]): húr

Kihagyja a speciális karaktereket az unescaped_string karakterláncban, figyelembe véve a kapcsolat aktuális karakterkészletét, így biztonságosan elhelyezhető mysql_query(). Ha bináris adatot kell beilleszteni, akkor ezt a funkciót kell használni.

mysql_real_escape_string() meghívja a MySQL könyvtárfüggvényét a mysql_real_escape_string, amely fordított perjeleket ír a következő karakterekhez: \x00, \n, \r, \ , " , " és \x1a.

Ezt a funkciót mindig (néhány kivételtől eltekintve) kell használni az adatok biztonságossá tételére, mielőtt lekérdezést küldene a MySQL-nek.

Vigyázat

Biztonság: az alapértelmezett karakterkészlet

A karakterkészletet vagy szerver szinten, vagy az API függvénnyel kell beállítani mysql_set_charset() hogy az befolyásolja mysql_real_escape_string() . További információért lásd a karakterkészletekkel foglalkozó fogalmak részt.

Paraméterek

unescaped_string

A karakterlánc, amelyet meg kell szökni.

Link_identifier

A MySQL kapcsolat. Ha a hivatkozás azonosítója nincs megadva, akkor az utoljára megnyitott hivatkozás mysql_connect() feltételezik. Ha nem található ilyen hivatkozás, úgy megpróbál létrehozni egyet mysql_connect() vita nélkül hívták. Ha nem található vagy jön létre kapcsolat, egy E_WARNING szintű hiba keletkezik.

Visszatérési értékek

A kihagyott karakterláncot adja vissza, vagy HAMIS hibáról.

Hibák/Kivételek

Ha ezt a funkciót MySQL-kapcsolat nélkül hajtja végre, az is kibocsát E_WARNING szintű PHP hibák. Ezt a funkciót csak érvényes MySQL kapcsolattal hajtsa végre.

Példák

1. példa Egyszerű mysql_real_escape_string() példa

//Csatlakozás
$link = mysql_connect("mysql_host" , "mysql_user" , "mysql_password")
VAGY die(mysql_error());

//Lekérdezés
$query = sprintf ( "SELECT * FROM users WHERE user="%s" AND password="%s"",
mysql_real_escape_string($user),
mysql_real_escape_string($jelszó));
?>

2. példa mysql_real_escape_string() csatlakozási példa szükséges

Ez a példa bemutatja, mi történik, ha a függvény meghívásakor nincs MySQL-kapcsolat.

A fenti példa valami ehhez hasonlót fog kiadni:

Figyelmeztetés: mysql_real_escape_string(): Nincs ilyen fájl vagy könyvtár a /this/test/script.php fájlban az 5. sorban. Figyelmeztetés: mysql_real_escape_string(): A kiszolgálóra mutató hivatkozás nem hozható létre a /this/test/script.php fájlban az 5. sorban. bool(false) string(41) "SELECT * FROM szereplők WHERE vezetéknév = """

3. példa Egy példa az SQL Injection Attack-re

// Nem ellenőriztük a $_POST["jelszó"] értékét, bármi lehet, amit a felhasználó akart! Például:
$_POST [ "felhasználónév" ] = "aidan" ;
$_POST [ "jelszó" ] = "" VAGY ""="" ;

// Az adatbázis lekérdezése annak ellenőrzéséhez, hogy vannak-e megfelelő felhasználók
$query = ( $_POST [ "felhasználónév" ]) " ÉS jelszó=" ( $_POST [ "jelszó" ]) "" ;
mysql_query($query);

// Ez azt jelenti, hogy a MySQL-nek küldött lekérdezés a következő lenne:
echo $query ;
?>

A MySQL-nek küldött lekérdezés:

Ezzel bárki bejelentkezhet érvényes jelszó nélkül.

Megjegyzések

Használat előtt MySQL kapcsolat szükséges mysql_real_escape_string() egyébként szinthiba E_WARNING keletkezik, és HAMIS visszakerül. Ha a link_identifier nincs megadva, a rendszer az utolsó MySQL-kapcsolatot használja.

jegyzet: mysql_real_escape_string() nem menekül % és _ . Ezek helyettesítő karakterek a MySQL-ben, ha ezzel kombinálják MINT, GRANT, vagy VISSZAVONÁS.

8 évvel ezelőtt

Csak egy kis függvény, ami az eredeti mysql_real_escape_string-et utánozza, de nem igényel aktív mysql kapcsolatot. Statikus függvényként implementálható egy adatbázis osztályban. Remélem valakinek segít.

függvény mysql_escape_mimic ($inp) (
if(is_tömb($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 );
}

Vissza $inp ;
}
?>

13 évvel ezelőtt

Vegye figyelembe, hogy a mysql_real_escape_string nem ír perjelet a \x00, \n, \r és és \x1a karakterek elé, ahogy a dokumentációban is szerepel, hanem a karaktert a lekérdezéseknél elfogadható MySQL-reprezentációra cseréli (pl. a \n a "\" karakterrel van helyettesítve n" literal). (a \, " és " a dokumentáltnak megfelelően kihagyott) Ez nem változtat a függvény használatán, de azt hiszem, jó tudni.

6 évvel ezelőtt

A menekülésről szóló vita nem teljes anélkül, hogy elmondanánk mindenkinek, hogy alapvetően soha nem szabad külső bemenetet használni értelmezett kód létrehozásához. Ez vonatkozik az SQL utasításokra, vagy bármire, amelyen bármilyen "eval" függvényt meg lehet hívni.

Tehát ahelyett, hogy ezt a rettenetesen törött függvényt használná, inkább paraméteres előkészített utasításokat használjon.

Őszintén szólva, a felhasználó által megadott adatok felhasználása SQL-utasítások összeállításához szakmai hanyagságnak minősül, és Önt felelősségre kell vonnia a munkáltatója vagy az ügyfele felé, ha nem használt paraméteresen elkészített kimutatásokat.

Az mit jelent?

Ez azt jelenti, hogy ahelyett, hogy egy ilyen SQL utasítást készítenénk:

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

A mysqli előkészítése() függvényét () kell használnia egy ilyen utasítás végrehajtásához:

"INSERT INTO X (A) VALUES(?)"

Megjegyzés: Ez nem azt jelenti, hogy soha nem szabad dinamikus SQL-utasításokat generálni. Ez azt jelenti, hogy soha ne használjon felhasználó által megadott adatokat az utasítások generálásához. A felhasználó által megadott adatokat paraméterként kell átadni az utasításnak, miután elkészült. készült.

Így például, ha egy kis keretrendszert épít fel, és a kérés URI alapján szeretne beszúrni egy táblázatot, akkor az Ön érdeke, hogy ne használja a $_SERVER["REQUEST_URI"] értéket (vagy bármilyen része), és fűzze össze közvetlenül a lekérdezésével. Ehelyett elemezze ki a $_SERVER["REQUEST_URI"] érték kívánt részét, és ezt valamilyen függvényen vagy asszociatív tömbön keresztül leképezi egy nem felhasználóra. Ha a leképezés nem ad értéket, akkor tudja, hogy valami nincs rendben a felhasználó által megadott adatokkal.

Ennek be nem tartása számos SQL-befecskendezési problémát okozott a Ruby On Rails keretrendszerben, annak ellenére, hogy paraméteres előkészített utasításokat használ. Így törték fel egy ponton a GitHubot. Tehát egyetlen nyelv sem mentes a problémától. Ez az oka annak, hogy ez egy általános bevált gyakorlat, és nem valami specifikus a PHP-re, és ezért VALÓBAN érdemes átvenni.

Ezenkívül még mindig el kell végeznie a felhasználók által szolgáltatott adatok egyfajta érvényesítését, még akkor is, ha parametrikusan elkészített utasításokat használ. Ennek az az oka, hogy a felhasználó által megadott adatok gyakran valamilyen generált HTML részévé válnak, és Ön szeretné biztosítani, hogy a felhasználó által megadott adatok ne okozzanak biztonsági problémákat a böngészőben.

9 évvel ezelőtt

Van egy érdekes furcsaság a 2. példában az SQL-befecskendezéssel kapcsolatban: AND prioritást élvez a VAGY-al szemben, így a beinjektált lekérdezés valójában WHERE (user="aidan" AND password="") VAGY ""=""-ként fut le, tehát ehelyett ha egy tetszőleges felhasználónévnek megfelelő adatbázisrekordot (ebben az esetben "aidan") ad vissza, akkor az valójában MINDEN adatbázisrekordot ad vissza. Nem meghatározott sorrendben. Így a támadó bármilyen fiókkal be tud jelentkezni, de nem feltétlenül bármelyik fiókkal szabályozhatja, hogy melyik fiókról van szó.

Természetesen a potenciális támadó egyszerűen módosíthatja a paramétereit, hogy megcélozhassa az érdeklődésre számot tartó felhasználókat:

//Például. a támadó értékeit
$_POST [ "felhasználónév" ] = "" ;
$_POST["jelszó"] = "" VAGY user = "rendszergazda" ÉS "" = "";

// Rosszul formázott lekérdezés
$query = "SELECT * FROM felhasználók WHERE felhasználó="$_POST [ felhasználónév ] " ÉS jelszó=" $_POST [ jelszó ] "" ;

echo $query ;

// A MySQL-nek küldött lekérdezés a következő lenne:
// SELECT * FROM users WHERE user="" AND password="" OR user="administrator" AND ""="";
// amely lehetővé tenné, hogy bárki hozzáférjen az "adminisztrátor" nevű fiókhoz

?>

1 évvel ezelőtt

@feedr
Jegyzetét a következőképpen fogalmaztam meg:
$string = "asda\0sd\x1aas\\\\\\\\dasd\"asdasd\na\"\"sdasdad";
$tömb1 = array("\\\\\\\\", "\0", "\n", "\r", """, """, "\x1a");
$tömb2 = array("\\\\\\\\\\\\\\\\\", "\\\0", "\\\n", "\\\r", "\\\ " ", "\\\", "\\\Z");
echo($karakterlánc);
visszhang(PHP_EOL);
for($i=0; $i ha ($i==0)
$p = "/(?más
$p = "/(?visszhang($i);
echo($p);
echo($tömb2[$i]);
$karakterlánc = preg_replace($p, $tömb2[$i], $karakterlánc);
echo("\t");
echo($string);
visszhang(PHP_EOL);
}
visszhang(PHP_EOL);
echo($string);

2 évvel ezelőtt

Samet idézve a Numb Safariban

[ "A menekülésről szóló vita nem teljes anélkül, hogy elmondanánk mindenkinek, hogy alapvetően soha nem szabad külső bemenetet használni az értelmezett kód generálására. Ez vonatkozik az SQL utasításokra, vagy bármire, amelyen bármilyen "eval" függvényt hívnánk.

Tehát ahelyett, hogy ezt a rettenetesen törött függvényt használná, inkább paraméteres előkészített utasításokat használjon.

Őszintén szólva, a felhasználók által megadott adatok felhasználása SQL-utasítások összeállításához szakmai hanyagságnak minősül, és Önt felelősségre kell vonnia a munkáltatója vagy az ügyfele felé, ha nem használt paraméteresen elkészített kimutatásokat."]

Samnek igaza van........

Nem tartom azonban ésszerűnek minden fertőtlenítést abbahagyni, és egyszerűen átadni a feladatot a parametrikusan elkészített kimutatásoknak.

Egy adott helyzetben dolgozó fejlesztő mindig többet tud az érvényes bemenetről (az adott kontextusra jellemző).

Ha megkér egy felhasználót, hogy adjon át egy már megadott értéket, és tudja, hogy minden ilyen érték AB******-val kezdődik, és a karakterláncnak 7 vagy 11 hosszúságúnak kell lennie, de soha más hosszúságúnak kell lennie. egy jó előfertőtlenítő alapja – a karakterlánc eltérő megengedett hossza örökölt adatokra utalhat.

Soha nem szeretném azt a szemetet, amit egy rosszindulatú felhasználó egy űrlapon keresztül juttatott el, egyszerűen átadni a parametrikusan elkészített kijelentéseknek, először mindig a saját épelméjűségemet szeretném elvégezni, és bizonyos esetekben ezek az óvatosság és a egyszerűen válassza az Adatbázis művelet teljes megszakítását.

Így a DB-m nem tömődik el a biztonságossá tett nem biztonságos kijelentésektől – egyszerűen nem tömődik el, melyik a jobb.

Biztonság rétegekben – a fertőtlenítést és az érvényesítést továbbra is minden helyzetben mérlegelni kell, MIELŐTT előkészített nyilatkozatokat használna.

Ráadásul amennyire bele tudok olvasni a hivatalos dokiba
==============================================

"Menekülés és SQL injekció

A kötött változók a lekérdezéstől elkülönítve kerülnek a szerverre, így nem zavarhatják azt. A szerver ezeket az értékeket közvetlenül a végrehajtáskor használja, miután az utasítássablont elemezte. A kötött paramétereket nem kell kihagyni, mivel soha nem cserélik be közvetlenül a lekérdezési karakterláncba"

Ez számomra azt sugallja, hogy a belső elemekben a veszélyt alternatív kezeléssel lehet elkerülni, nem semmisítéssel.

Ez azt jelenti, hogy egy olyan nagy projekt, amely nem teljes konverziót készít előkészített nyilatkozatokká, örökölt kódot a szervezet különböző részein vagy szerverek beszélgetnek egymással, mind továbbadhatják a rossz híreket egy immunis helyről vagy helyzetről egy olyanra, amely nem immunis.

Amíg a fertőtlenítést szakszerűen, további kockázatok vállalása nélkül végzik, addig én személy szerint ragaszkodom a fertőtlenítés bizonyos rétegeihez, majd felhívom az elkészített nyilatkozatokat.


Először is egy kicsit arról, hogy általában miért van szükség ezekre a perjelekre.
Ha bármilyen adatot behelyettesítünk egy lekérdezésbe, akkor ahhoz, hogy ezeket az adatokat meg tudjuk különböztetni az SQL parancsoktól, idézőjelbe kell tenni.
Például ha írsz
SELECT * FROM táblázat WHERE név = Számla
akkor az adatbázis úgy dönt, hogy Bill egy másik mező neve, nem találja meg, és hibát dob. Ezért a behelyettesített adatot (jelen esetben a Bill nevet) idézőjelbe kell tenni - ekkor az adatbázis karakterláncnak tekinti, aminek az értékét a névmezőhöz kell rendelni:
SELECT * FROM táblázat WHERE név = "Számla"
Az idézetek azonban magukban az adatokban is megjelenhetnek. Például,
SELECT * FROM táblázat WHERE név = "D"Artagnan"
Itt az adatbázis úgy dönt, hogy "D" az adat, az Artagnan pedig egy parancs, amelyet nem ismer, és szintén hibát fog kiütni. Ezért minden adatot nyomon kell követni, hogy elmagyarázzuk az adatbázisnak, hogy a bennük található idézőjelek (és néhány más speciális karakter) az adatokra vonatkoznak.
Ennek eredményeként helyes kérést kapunk, amely nem okoz hibákat:
SELECT * FROM táblázat WHERE név = "D\"Artagnan"

Így azt találtuk, hogy amikor karakterlánc-adatokat helyettesítünk egy lekérdezésben, két szabályt kell követni:
- minden beszúrt karakterlánc adatot idézőjelek közé kell tenni (egyes vagy dupla, de az egyszeresek kényelmesebbek és gyakrabban használják).
- a speciális karaktereket perjelekkel kell megszökni.

Külön meg kell jegyezni: a hozzáadott perjelek NEM kerülnek be az adatbázisba. Csak a kérésben van rájuk szükség. Az alaphoz ütve a perjeleket el kell dobni. Ennek megfelelően gyakori hiba a perjelek használata az adatok adatbázisból való lekérésekor.

A fentiek mindegyike a karakterlánc-adatokra és dátumokra vonatkozik. A számok beszúrhatók anélkül, hogy azok után következnének, vagy idézőjelek közé helyezhetők. Ha ezt megteszed akkor SZÜKSÉGSZERŰEN! kényszerítse az adatokat a kívánt típusra, mielőtt beszúrná a lekérdezésbe, például:
$id = intval ($id);
Az egyszerűség (és a megbízhatóság) érdekében azonban a számokkal ugyanúgy dolgozhatunk, mint a karakterláncokkal (mivel a mysql továbbra is a kívánt típusra konvertálja őket). Ennek megfelelően a kérelembe beillesztett adatokat nyomon követjük és idézőjelbe helyezzük.

Van még egy szabály - nem kötelező, de be kell tartani a hibák elkerülése érdekében:
A mezők és táblák neveit idézőjelek közé kell tenni - "`" (az ezzel a szimbólummal ellátott billentyű egy szabványos billentyűzeten található az "1" billentyűtől balra). Végül is a mező neve egybeeshet a mysql-lel kulcsszavakat, de ha hátsó idézetet használunk, akkor a MySQL megérti, hogy minden helyes:
SELECT * FROM `table` WHERE `date` = "2006-04-04"
Különbséget kell tenni ezek között az idézőjelek között, és nem szabad összetéveszteni egyiket a másikkal. Ne feledje azt is, hogy a backticket nem kerüli el a perjel.

Tehát megtanultuk, hogyan kell helyesen behelyettesíteni az adatokat egy kérésbe.
DE! A dinamikus lekérdezéskészítés nem korlátozódik az adatok helyettesítésére. Gyakran SQL parancsokat és mezőneveket kell helyettesítenünk egy lekérdezésben. És itt áttérünk a biztonság témájára:

Az SQL-injekció a hackertámadás olyan módszere, amikor a szkriptbe továbbított adatokat úgy módosítják, hogy az ebben a szkriptben generált lekérdezés teljesen mást kezd végrehajtani, mint amire szánták.
Az ilyen támadások elleni védekezés szabályai két pontra oszthatók:
1. Adatokkal való munka.
2. Lekérdezésvezérlőkkel való munka.

Az első pontot fentebb részletesen tárgyaltuk. Azt lehet mondani, hogy ez valójában nem védekezés. Az adatok lekérdezéshez való hozzáadására vonatkozó szabályok betartását mindenekelőtt az SQL SYNTAX követelményei határozzák meg. Mellékhatásként pedig van védelmünk a hackelés ellen is.

A második pont sokkal nehezebb, mivel nincs egyetlen univerzális szabály az adatokra vonatkozóan - a backtick nem védi meg a mező nevét a hacker általi módosítástól. Nem használható idézőjel a táblanevek, SQL utasítások, LIMIT parancsparaméterek és egyéb utasítások védelmére.
Ezért az alapszabály a vezérlőelemek lekérdezésbe való helyettesítésekor a következő:
Ha dinamikusan kell SQL utasításokat vagy mezők, adatbázisok, táblák neveit beszúrni egy lekérdezésbe, akkor semmi esetre se illessze be közvetlenül a lekérdezésbe.
Az ilyen kiegészítések minden beállítását ELŐRE kell írni a szkriptben, és a felhasználó által megadott adatok alapján kell kiválasztani.
Például, ha egy mezőnevet kell átadnia a rendelésnek operátor által, akkor semmi esetre se cserélje ki közvetlenül. Először ellenőriznünk kell. Például hozzon létre egy érvényes értékek tömbjét, és csak akkor helyettesítse be a kérésbe, ha az átadott paraméter jelen van ebben a tömbben:
$orders =array("név" , "ár" , "mennyiség" );
$kulcs = array_search($_GET["rendezés"], $rendek));
$orderby = $rendelések [ $kulcs ];
$query = "SELECT * FROM `table` ORDER BY$orderby " ;

Az előre leírt opciók tömbjében megkeressük a felhasználó által beírt szót, és ha megtaláljuk, kiválasztjuk a tömb megfelelő elemét. Ha nem található egyezés, a tömb első eleme kerül kiválasztásra.
Így nem az kerül behelyettesítésre a kérésbe, amit a felhasználó beírt, hanem amit a szkriptünkben írtunk.
Minden más esetben ugyanezt kell tenni.
Például, ha a WHERE záradék dinamikusan jön létre:
if (!empty($_GET [ "ár" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "ár" ]). """ ;
$query = "SELECT * FROM `table` WHERE $hol " ;

Nehezen tudom elképzelni azt az esetet, amikor egy táblanevet dinamikusan be lehet illeszteni a lekérdezésbe, de ha ez megtörténik, akkor a nevet is csak a szkriptben előre meghatározott halmazból kell beszúrni.
A LIMIT operátor paramétereit egész típusúra kell kényszeríteni aritmetikai műveletek vagy az intval() függvény segítségével.
Ne gondolja, hogy az itt felsorolt ​​példák kimerítik a dinamikus lekérdezéskészítés összes lehetőségét. Csak meg kell értenie az elvet, és minden ilyen esetben alkalmaznia kell.

Munkám jellegéből adódóan a webes alkalmazások forráskódjának biztonsági auditjait kell végeznem.
Rengeteg webes alkalmazás és sok kód...

Nem titok, hogy az SQL injekciós sebezhetőségek a leggyakoribbak a szerveroldali webalkalmazások sérülékenységei közül. Vannak olyan platformok és keretrendszerek, ahol az ilyesmi szinte teljesen ki van zárva, például az ORM és így tovább. De a statisztikák kitartóan azt mondják, hogy az egyszerű összefűzött SQL lekérdezéseket használó webalkalmazások abszolút túlsúlyban vannak az interneten. Ezen kívül vannak olyan esetek, amikor az ORM általánosan alkalmazható Nem lehet például, ha nemcsak a kifejezések paramétereinek, hanem magának a lekérdezési logikának is operátori szinten kell függnie a felhasználói adatoktól.

Szóval, kezdjük.

Haszontalan karakter szökés
Az SQL-injekciókra sebezhető PHP webalkalmazások 83%-ában megtalálható
Az escape függvény használata olyan karakterekhez, mint pl
mysql_escape_string
mysql_real_escape_string
perjelek
idézőjelek nélkül. Leggyakrabban numerikus paraméterekben (mindenféle *_id) nyilvánul meg.
Példa
$sql = "FELHASZNÁLÓ KIVÁLASZTÁSA A felhasználói listából WHERE userid=".mysql_real_escape_string($_GET["uid"]);

Biztonságos kódnak tűnik, de csak a felszínen. Az SQL-injektálások leggyakoribb mintája a PHP-ben a gyakorlatomban itt kúszott be. A biztonsági rés megtámadásához a támadónak egyszerűen el kell kerülnie a " " \x00 \r \n \x1a karakterek használatát a támadási vektorban.
Például:
/index.php?uid=-777 UNION SELECT jelszó a felhasználói listából

Keresés a kódban
Bonyolítja a nyelv szemantikája. Az egyszerű kereséshez használhatja az egrep-et:
egrep -Rin "(kiválasztás|frissítés|beszúrás|törlés|csere).*(from|set|into).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]"

A keresési kifejezés logikája a következő: keresse meg az összes olyan sort, amelyben nincs idézőjelek sorozata ("", "", "", "") a szűrési függvények bal oldalán. A módszer természetesen messze nem 100%, de lehetetlen reguláris kifejezést megkövetelni a szemantikai elemzés elvégzéséhez.
Az információk megjelenítésének megkönnyítése érdekében a funkciót színesen kiemelheti a konzolon:
egrep -Rin "(kiválasztás|frissítés|beszúrás|törlés|csere).*(from|set|into).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|addslashes)"

A helyettesítő karakteres sebezhetőség elleni védelem érdekében a legjobb a típusöntvény használata.
Ez mindig gyorsabban működik és megbízhatóbb, mint mindenféle szűrés és szűrés.
A fenti példában a javítás a következő lehet:
$sql = "FELHASZNÁLÓ KIVÁLASZTÁSA A felhasználók listájából WHERE userid=".intval($_GET["uid"]);

Ezzel a rövid esszé lezárul. Arra kérek minden webfejlesztőt, hogy próbálja meg ellenőrizni az ilyen terveket a forrásaikban. Még jobb, ha kiterjeszti az adott keresési szkriptet az emberekre.

Tehát alapvetően a MySQL és a PHP területein ástam magam mélyen... konkrétan azon biztonsági intézkedésekben, amelyeket meg kell tennem az adatbázis- és űrlapbevitelek kezelésekor. Eddig a következőket találtam erősen ajánlottnak:

  1. Elkészített nyilatkozatok
  2. A _real_escape_string() használata
  3. NEM használ varázsidézeteket, mert összezavarja az adatbázisokat, és olyan dolgokat ad a végén, mint például: "Nem te hívtad...".

Ez mind nagyszerű, és folyamatosan figyelem. Azonban azon töprengtem, hogy meg kell-e hagynom az olyan karaktereket, mint a dollárjel [$], a százalékjel [%] és esetleg mások. A lekérdezés értelmezhette a dollárjelet PHP változóként? Mi a helyzet a LIKE szintaxissal, amelyről hallottam, hogy % szimbólumot vagy akár helyettesítő karaktert használ? Az elkészített kimutatásoknak technikailag gondoskodniuk kell erről az egészről, de én csak a biztonság kedvéért akartam lenni, és megbizonyosodni arról, hogy mindent megfelelően lefedtem. Azokban az esetekben, amikor elfelejtem felhasználni az előkészített nyilatkozatokat, vagy egyszerűen figyelmen kívül hagyom azokat, reméltem, hogy ez a második védelmi vonal megmondja, hogy megszabadulhatok a szédüléstől.

Jelenleg a következőket használom a menekülésre:

Escape függvény($connection, $data)( $new_data = trim($data); $new_data = i_real_escape_string($connection, $new_data); $new_data = addcslashes($new_data, "%_$"); $new_data = htmlspecialchars ($új_adatok, ENT_NOQUOTES); $új_adatok visszaadása; )

Tehát ez helyes? Valami szörnyen rosszul csinálok? Kérjük, vegye figyelembe, hogy az adatbázis adatainak visszaküldésekor el kell távolítanom a fordított perjeleket a $,% és _ karakterek előtt.

Valami szörnyen rosszul csinálok?

Először a kutatásáról.

Elkészített nyilatkozatok – az egyetlen csodálatos dolgot találtál.

Bár a mysqli_real_escape_string használata (feltételezve, hogy előkészített utasításokat használ) haszontalan és káros(olyan eredmény létrehozása, amelyet magad is megjegyeztél: "Nem hívtad...").

A Magic Quotes pedig már régen kikerült a nyelvből – így nem igazán ér semmit.

Tehát még a kezdeti feltételezések nagy része is egyértelműen téves.

Most a kérdésedre.

A lekérdezés értelmezhette a dollárjelet PHP változóként?

Mi a helyzet a LIKE szintaxissal, amelyről hallottam, hogy % szimbólumot vagy akár helyettesítő karaktert használ?

Igen, jól hallottad. A LIKE operátor pontos célja a mintakeresés végrehajtása. A karakterek LIKE-ban való letiltásának a legcsekélyebb értelme sem lenne.

Minden alkalommal, amikor a LIKE operátort használja, el kell döntenie, hogy melyik karaktert használja és melyiket tiltja le. Egyszeri megoldást nem használhat. Arról nem is beszélve, hogy az összes többi mysql interakcióban a % jelnek nincs különösebb jelentése.

Az elkészített kimutatásoknak technikailag gondoskodniuk kell erről

Az elkészített állításoknak semmi közük a $ vagy % jelekhez. Az elkészített utasítások az SQL injekcióra vonatkoznak, de egyetlen karakter sem okozhatja (az "injection"-t nem hívhattad megfelelően használt LIKE operátornak, ugye?).

Végül a legrosszabb részhez.

Abban az esetben, ha elfelejti felhasználni az elkészített nyilatkozatokat, vagy egyszerűen elhanyagolja betartásukat,

semmi sem ment meg.

A legkevesebb segítség pedig az általad kifejlesztett függvény lenne.

Összesít.

  1. Szabaduljon meg ettől a funkciótól.
  2. Használat töltőanyagok* hogy minden egyes változót reprezentáljon a lekérdezésben.
  3. Az Escape % és _ karakterek csak akkor szerepeljenek a bevitelben, ha a LIKE operátorban használatosak lesznek, és nem szeretné, hogy értelmezzék őket.
  4. Használja a htmlspecialchars()-t a kimenethez, ne a mysql bemenetet.

*olvasd el az elkészített nyilatkozatokat, ha ez a kifejezés ismeretlen számodra.

Nem kell kerülnie a dollárjelet. A MySQL nem nézi kifejezetten ezt a karaktert, és a PHP csak a forráskódban ismeri fel, a karakterláncértékekben nem (hacsak nem hívod meg az eval-t a karakterláncon, de az egy másik féreg).

Csak akkor kell kihagynia a % és _ karaktereket, ha a LIKE argumentumként felhasználói bevitelt használ, és nem szeretné, hogy a felhasználó helyettesítő karaktereket használjon. Ez akkor fordulhat elő, ha egy keresési űrlapot dolgoz fel. Adatbázisban való tároláskor nem kell használnia.

Az adatbázis elérésekor nem kell htmlspecialchars-t használni. Ezt csak akkor szabad használni, ha adatokat jelenít meg a felhasználó számára egy HTML-oldalon, hogy megakadályozza az XSS-injektálást.

Attól függően, hogy milyen adatokat és mire használják fel.

Ha úgy találja, hogy a PHP alapértelmezett készenléti utasításai túl nagyok és összetettek, javaslom, hogy tekintsen át néhány, a githubon elérhető osztályt, hogy képet kapjon az egyszerűsített lekérdezésekről.

Példa lekérdezések beszúrására ezzel az osztállyal

$data = Array ("login" => "admin", "active" => true, "firstName" => "John", "lastName" => "Doe", "password" => $db->func( "SHA1(?)",Tömb ("titkos jelszó+só")), // jelszó = SHA1("titkos jelszó+só") "createdAt" => $db->now(), // CreatedAt = MOST() " expires" => $db->now("+1Y") // expires = MOST() + intervallum 1 év // Támogatott intervallumok [s]másodperc, [m]perc, [h]óra, [d]nap, [Hónap év); $id = $db->insert("felhasználók", $adat); if ($id) echo "felhasználó jött létre. Id=" . $id; else echo "insert failed:" . $db->getLastError();