ugrás a tartalomhoz

Miért NE használj ezSQL-t - kritika egy twitt-re

prom3theus · 2011. Okt. 21. (P), 15.48
Az alábbi bejegyzésre szeretnék reagálni, amely a weblabor twitt box-ában jelent meg ma:
tikaszvince "PHP: Fast and easy SQL queries using ezSQL | http://t.co/QTw7jY0i http://t.co/iOpHK1VN #weblabor"


Szeretném a vitát nyílt vizekre vinni, mert szakmai (semmiképp nem személyes) meggyőződésem, hogy mind a hivatkozott bejegyzés, mind az onnan továbbhivatkozott oldalon található leírás olyan adatbázis kezelési, biztonsági, szakmai bakiktól hemzseg, amely ellen évek óta küzd a közösség egy jókora (és egyre növekvő) része.

Példaként említeném:
- PREPARE statement-ek támogatásának/példáinak totális hiánya, ehelyett a jóval kevésbé hatékony és biztonságtalanabb ismételten elküldött INSERT lekérések használata
- SQL injection elleni védelemnek nyoma nincs, ellenben tucatnyi példa szerepel a hivatkozott leírásban arra, hogyan lehet mindezt lehetővé tenni könnyen a külső személyek számára (a hivatkozott cikkből is hiányzik az erre való igény).

Konkrét példákat kiemelve a leírásból:
Example 10:
foreach ( $db->get_col("SHOW TABLES",0) as $table_name )
{
            $db->debug();
            $db->get_results("DESC $table_name");
}

1. SHOW TABLES? Ne már. Az INFORMATION_SCHEMA mire való?
2. Változónév a lekérésben??? Escape-elni majd a magic_quotes_gpc fog?! (legújabban deprecated...)

Example 3 – Return results as numerical array:
if ( $results = $db->(“SELECT $animal[$type] FROM $type”,ARRAY_N))
                        {
                                    foreach ( $results as $result )
                                    {
                                                echo “$result[0]<br>”;
                                    }
                        }
                        else
                        {
                                    echo “No $animal\s!”;
                        }

Vajon a szerzőnek van halovány sejtelme arra nézvést, hogy a fenti kódrészlet hányszor több opkódra fordul le annál, mint ha konkatanálást használna? (zárójeles megjegyzés: nem ellenőriztem, de gyaníthatóan még akkor is kevesebb opkód lenne a végeredmény, ha a konkatanált változókra külön-külön hívnánk valamilyen escape függvényt - és akkor még SQL injectet se lehetne csak úgy belenyomni)

$db->escape:
$db->escape – Format a string correctly in order to stop accidental mal formed queries under all PHP conditions.


Költői kérdés: ha az escape függvény formázásra való, és nem escape-elésre, akkor miért ez a neve?

Lírai válasz:
makes any string safe to use as a value in a query under all PHP conditions. [...] Note: Should not be used by itself to guard against SQL injection attacks[...]

Tehát biztonságossá teszi a lekérést, de azért nem igazán használható az SQL injection ellen. Ééééértem... o.O

A bizonyosság kedvéért ellenőriztem: az ezSQL_pdo osztály kódjában a 110-es sorban található tökéletes sületlenség - konkrétan: $str = addslashes(stripslashes($str)) - végez valamiféle escape-elést, de arra még véletlenül sem a PDO::quote() metódusát használja. Ezzel szemben az ezSQL_mysql osztály már legalább mysql_real_escape_string() hívást tartalmaz. Érthetetlen.

Egyébként ugyanebben a PDO-ra épülő implementációban találtam még egy érdekességet:
while ( $row = @$sth->fetch(PDO::FETCH_ASSOC) )
{
// Store relults as an objects within main array
$this->last_result[$num_rows] = (object) $row;
$num_rows++;
}


Befizetem egy sörre azt a kollégát, aki elmagyarázza nekem (és alátámasztja), hogy miért jobb PDO::FETCH_ASSOC-cal lekérni egy sort a resultset-ből, majd utána a kapott array-t object-té cast-olni, mint egyszerűen PDO::FETCH_OBJ-ot használni. Azon túl persze, hogy miért jó PHP-ban végigiterálni a sorokat, ahelyett, hogy a nyilván sokkal gyorsabb PDOStatement::fetchAll()-t hívná meg (akkor ráadásul nem kéne N-szer elnyeletni a hibakimenetet sem, a @ operátor pedig tudjuk, mennyire "szélsebes")...

Ezen problémák nekem pont elegek voltak ahhoz, hogy SENKINEK ne ajánljam az ezSQL használatát, a kódon a fenti (hol kisebb, hol nagyobb) trehányságok, következetlenségeken túlmenően is ezernyi helyen lehetne optimalizálni.

Végkövetkeztetésem:
- Kezdőknek nem ajánlom, mert egész egyszerűen rosszra tanít. (de az egyértelmű legalábbis, hogy jóra nem)
- Haladóbbaknak nem ajánlom, mert semmiféle teljesítmény-optimalizálásnak nyomát sem találni benne, picit terheltebb oldalak APC vagy hasonló nélkül valószínűleg nyögni fogják a fícsörként emlegetett cache-t (több regex futtatás lekérésenként + tárolás + egyéb ehhez kapcsolódó mechanizmusok - hogy lehet ez gyorsabb egy normálisan konfigolt RDBMS natív, belső query cache-énél?)

Kérdésem tehát a twittelőhöz: Te kinek ajánlod ezt a lib-et?

Mégegyszer: nem a személyed, a bejegyzésed, vagy a hivatkozott blog ellen keltem ki - hanem a jelenség ellen, amit az ezSQL képvisel. Kérlek ne vedd ezt ellened irányuló offenzívának ezért.
 
1

Hibák

Poetro · 2011. Okt. 21. (P), 16.19
Hát igen, ilyen alapvető hibákat eleve nem szabadna elhelyezni példakódban, és már évek óta küzdök itt a Weblaboron, hogy ezek a hibák, esetleg berögződések eltűnjenek. Az SQL injectiont igenis komolyan kell venni, és megfelelő kódot kell írni, és JÓ PÉLDÁKAT, hogy a kezdőkben is rögzüljön, hogyan kell használni őket. Ezt főleg akkor kell komolyan venni, ha valaki egy adatbázisréteget fejleszt és/vagy mutat be.

Éppen ezért örülök a Node.js-beli adatbázis moduloknak, mivel azokat már eleve úgy fejlesztették ki, hogy könnyű legyen átadni a paramétereket. Kényelmesebb őket átadni a szabványos módon, mint mindenfajta string machinálással.

db.execute(
  'INSERT INTO entries (nid, title, created, updated, body) VALUES ($nid, $title, $created, $updated, $body)',
  {
    $nid: +attr.nid || null,
    $title: attr.title,
    $created: attr.created || Date.now() / 1000 | 0,
    $updated: Date.now() / 1000 | 0,
    $body: attr.body
  },
  function (error) { /* kezeljük az esetéleges hibákat */ }
);
db.execute(
  'SELECT * FROM entries WHERE nid = ?', [+nid],
  function (error, rows) { /* feldolgozzuk az adatokat */ }
);
És ugye JavaScript esetén a $nid nem jelent semmit, főleg stringben, mivel nincs automatikus behelyettesítés (majd lesz az ES.next-ben)
2

miért jó PHP-ban

H.Z. v2 · 2011. Okt. 21. (P), 16.33
miért jó PHP-ban végigiterálni a sorokat, ahelyett, hogy a nyilván sokkal gyorsabb PDOStatement::fetchAll()-t hívná meg


Erre van ötletem: bizonyos mennyiség fölött esetleg kifutna a rendelkezésére álló memóriából.
9

A memóriából a jelenlegi

prom3theus · 2011. Okt. 22. (Szo), 18.12
A memóriából a jelenlegi megoldással is könnyen kifuthat (hiszen felfűzi a sorokat egy tömbbe). PDO használatával ugyanez egy értékadás, egy array_merge vagy két tömb összeadása a fetchAll() kimenetével (a fetchAll-t némítva @-cal, ha ez annyira szükséges - bár ahogy néztem, felesleges, mert az osztály ráül az error handler-re, a @ operátor pedig így megszűnik hasznosnak lenni).
13

@ használatáról érdemes

Kubi · 2012. Jan. 21. (Szo), 23.01
@ használatáról érdemes leszokni, ha csak nem akarsz a debuggolás rémálmába csöppeni, ez is lehetne az sql paraméterek escepelése mellé egy alap ajánlás :)
3

A csiripek 90%-át tikaszvince

Hidvégi Gábor · 2011. Okt. 21. (P), 16.40
A csiripek 90%-át tikaszvince szolgáltatja, és mivel elég sok van belőlük, szerintem irreális elvárás ebben az esetben, hogy mindig minőségileg ellenőrzött, értékes linkeket küldjön. Szerintem ezt inkább ötletgyűjtőnek érdemes felfogni.
4

én ott hagytam abba, hogy...

Crystal · 2011. Okt. 21. (P), 17.27
én itt találkoztam ezzel a libbel pár napja: http://php.dzone.com/articles/php-fast-and-easy-sql-queries

Though, please note that it does not support differences in SQL syntax implementations among different databases.

na ennél a mondatnál hagytam abba az olvasást. Egy ilyen adatbázis-absztrakciós rétegnek a következőket kell tudnia (szerintem):
  • egységes API az SQL lekérdezések különböző DBMS-en való futtatásához
  • egységes API a különböző SQL dialektusok fölött (method chaininggel lehessen felépíteni a query adatszerkezetét, aztán a DBMS adapter rakja össze belőle az SQL sztringet)
  • automatikusan védjen SQL injection-től
  • ne korlátozza a fejlesztőt, azaz lehessen a wrapperen keresztül minden olyan szolgáltatását használni az adott DBMS-nek, amit a wrapper nélkül lehet. Ez minimum a prepared statement-ek és tranzakciók támogatását jelenti.

Na ebből a listából az ezSQL csak az 1. követelményt teljesíti. Teljesen felejtős a dolog, kicsit fel is húztam magam rajta, hogy minek ír valaki dbms wrapper libet, ha ennyire nem megy neki. Vagy legalább dugná el az asztalfiókba -.-
5

minek ír valaki dbms wrapper

Hidvégi Gábor · 2011. Okt. 21. (P), 18.01
minek ír valaki dbms wrapper libet, ha ennyire nem megy neki
Hogy rossz példának fel lehessen hozni, ez nyilvánvaló : )
6

Minek? Unatkozik, tanulni

H.Z. v2 · 2011. Okt. 21. (P), 18.16
Minek? Unatkozik, tanulni próbál belőle, ilyesmi.
7

Nem veszem ;)

tiku I tikaszvince · 2011. Okt. 22. (Szo), 09.57
Nem veszem támadásnak ;)

Kinek ajánlom a lib-et? Senkinek.
Kinek ajánlottam a hivatkozott postot? A weblabor közönségének.

Szinte soha nem szoktam véleményezni egy ajánlott linket. Nem véleményezek, mert a twitter 140 karakterébe nem fér bele a hivatkozás (még ha rövidített is) és a post címe mellé egy-egy komment. És nem véleményezek, mert általában tartózkodom az azonnali véleményt nyilvánítástól.

Általában ezek a linkek, amiket #weblabor címkével twittere küldök, GoogleReader-ben jönnek velem szembe. Ezeket elolvasom és ha a weblabor profiljába vág és engem érdekel akkor megy twitterre. És igen, néha becsúszik egy kevésbé minőségi dolog is.

Személy szerint a csiripek beküldésével nem az oktatás a célom, hanem a tartalom ajánlás, ezen keresztül pedig az olyan fórum témák kiprovokálása itt, a weblabor fórumán, mint ez a téma is.

De ha gondolod, akkor néha a CSI:PHP blogról küldhetek #weblabor #horror címkékkel egy-egy bejegyzést külön neked címezve ;)

--
@tikaszvince
Tikász Vince
8

Köszi a választ :)

prom3theus · 2011. Okt. 22. (Szo), 18.06
Köszi a választ :)

Ha a provokáció volt a célod a twittel, akkor most komolyan örülök neki, hogy szokásommal ellentétben "felültem" neki :)

Örülök annak is, hogy sok az azonosság a véleményekben, örömteli, hogy egyre többen figyelnek oda arra (valamint adják tovább és mintegy őrködnek fölötte), hogy körültekintően válasszunk eszközt és lehetőség szerint igényes megoldásokat alkalmazzunk.

Semmi baj az ezSQL célkitűzésével, bizonyos helyzetekben akár még indokolt is lehetne a használata - de semmi képp sem a jelenlegi állapotában, mert az egyszerűen veszélyes. Kontributálni kéne hozzá, de félő, hogy akkor alig maradna valami az eredeti kódból, annyi benne a cserélni/optimalizálni való.
10

Szerintem

zzrek · 2011. Okt. 22. (Szo), 20.41
Szerintem jó, hogy "felültél neki", ne hagyd abba, ha máskor is ilyesmibe akadsz, fejtsd ki a véleményed, hasznos volt, köszi!
11

Rendben, igyekszem és örülök,

prom3theus · 2011. Okt. 23. (V), 11.06
Rendben, igyekszem és örülök, hogy hasznom volt :)
12

Mit legyen helyette?

festo · 2012. Jan. 18. (Sze), 23.23
Pont nemrég találkoztam ezzel az osztállyal, és úgy gondoltam, hogy ez majd jajj de jó lesz, meg se néztem igazán, csak elmentettem a kedvencekbe ...

A cikk után így el is ment a kedvem tőle rendesen, viszont így felmerült bennem az a kérdés, hogy akkor ti mit ajánlotok? Van valami favorit wrapper vagy csak egyszerűen a PDO-t használjátok?
15

Nem szeretem

janoszen · 2012. Jan. 22. (V), 10.35
Én személy szerint nem szeretem a PDO-t mert valahogy az volt az érzésem, hogy túl okos próbál lenni. Szerintem, mindenképpen érdemes elé még egy elfedő réteget, hogy alatta bármikor ki tudd cserélni a MySQL connectort. Egyszer már írtam egy ilyet, lehet megint előkeresem.
16

Túl okos?

H.Z. v2 · 2012. Jan. 22. (V), 12.14
Hm. Google keresőjével vannak ilyen gondjaim, de a PDO?
Nekem inkább az (volt) a bajom vele, hogy megpróbál nagyon általános lenni, emiatt sok olyan funkció használatát problémássá teszi/teheti, ami nem lenne gond, ha közvetlenül az alá tolt adatbázis kezelő driverét használnád. (nem tudok konkrét példát, ahhoz túl rég játszottam vele – nem feltétlenül mysqlről beszélek)
17

Ugyanaz

janoszen · 2012. Jan. 22. (V), 18.13
Ugyanazt mondod, mint én, csak más szavakkal. :) A legtöbb alkalmazás már csak az adatbázis motor sajátosságai miatt nem portolható másra, szóval nem igazán látom a PDO gyakorlati hasznát azon kívül, hogy képes időnként igen faramuci és nehezen értelmezhető hibákat adni.
14

Adatbázis kezeléshez én a

Kubi · 2012. Jan. 21. (Szo), 23.27
Adatbázis kezeléshez én a doctrine használatát ajánlom (már 2 éve nem használok pdo-t vagy plain sql-t), van a doctrine 1.2 és a doctrine 2.0, kettő között hatalmas különbségek vannak, mindkettő jó, de az 1.2-vel gyorsabb dolgozni (és ha megrendelésre dolgozol ez egy fontos pont), sok minden benne van, amit kihagytak a 2.0 változatból (behaviorok). Doctrine 2.0 -t csak akkor ajánlom, ha symfony2 -vel akarsz foglalkozni.

Több adatbázis motort is támogat, elméletileg lehet köztük váltani menet közben is megkötésekkel. Biztonság szempontjából a maximumot nyújtja, nehéz benne úgy átadni paramétert, hogy az ne legyen escepelve, szószerint akarni kell. Háttérben statementeket készít és van benne cahce is (apc, memcache).

Doctrine

Behaviors részben: nested set, soft delete, i18n, csak hogy egy párat említsek, plusz ott vannak benne az eventek, a pager stb.

Doctrine-en kívül még egy másik jó orm a propel, de minőségben szerintem veri a doctrine (használom mindkettőt).
18

Köszönöm a tippet. A Doctrine

festo · 2012. Jan. 22. (V), 19.05
Köszönöm a tippet.
A Doctrine nevet láttam már leírva, de nem ismerem. Most akkor majd egy kicsit elmélyülök benne.
Ahogy így elnézegetem a példákat meg a beleolvastam a doksiba látom, hogy leginkább MVC-re van ez kitalálva, amivel jelenleg még hadilábon állok, de időtől és feladattól függően megpróbálom a kettőt összehozni.
19

Nincsmit, ha doctrinben

Kubi · 2012. Jan. 22. (V), 22.34
Nincsmit, ha doctrinben elakadsz kérdezz nyugodtan, használhatod a doctrine-t sima egyszerü oldalak esetében is és majd idővel rászoksz valami framework (symonfy, zend) használatára.