Ar pasirengę SKVN teiginiai yra pakankami, kad būtų išvengta SQL injekcijos?

Tarkime, turiu šį kodą:

 $dbh = new PDO("blahblah"); $stmt = $dbh->prepare('SELECT * FROM users where username = :username'); $stmt->execute( array(':username' => $_REQUEST['username']) ); 

SKVN dokumentuose nurodyta:

Parengtų ataskaitų parametrų nereikia nurodyti; vairuotojas jį tvarko.

Ar tai tikrai viskas, ką reikia daryti, kad išvengtumėte SQL injekcijos? Ar tikrai taip paprasta?

Jūs galite prisiimti „MySQL“, jei tai svarbu. Be to, aš tikrai domiuosi, kaip naudoti paruoštus pranešimus SQL injekcijoms. Šiame kontekste nereikia XSS ar kitų galimų pažeidžiamumų.

558
25 сент. nustatė Markas Biekas 25 sep . 2008-09-25 18:43 '08, 18:43, 2008-09-25 18:43
@ 7 atsakymai

Trumpas atsakymas yra NE , o SKVN paruošimas neapsaugos nuo visų galimų SQL atakų. Kai kuriems neaiškiems ribiniams atvejams.

Aš pritaikau šį atsakymą, kad galėčiau kalbėti apie SKVN ...

Ilgas atsakymas nėra toks paprastas. Jis grindžiamas čia parodyta ataka.

Ataka

Taigi, leiskite jam pradėti rodyti ataką ...

 $pdo->query('SET NAMES gbk'); $var = "\xbf\x27 OR 1=1 /*"; $query = 'SELECT * FROM test WHERE name = ? LIMIT 1'; $stmt = $pdo->prepare($query); $stmt->execute(array($var)); 

Tam tikromis aplinkybėmis tai grąžins daugiau nei 1 eilutę. Pažvelkime, kas čia vyksta:

  • Pasirinkite simbolių rinkinį

     $pdo->query('SET NAMES gbk'); 

    Kad šis išpuolis veiktų, mums reikia kodavimo, kurį serveris tikisi iš ryšio, tiek kodavimo, tiek ASCII, t. Y. 0x27 , ir gauti tam tikrą simbolį, kurio galutinis baitas yra ASCII, t.y. 0x5c . Kaip paaiškėjo, MySQL 5.6, 5 tokie kodai yra palaikomi pagal nutylėjimą: big5 , cp932 , big5 , cp932 ir sjis . Čia mes pasirenkame gbk .

    Dabar labai svarbu atkreipti dėmesį į SET NAMES naudojimą. Tai nustato simbolių rinkinį ON SERVER . Yra dar vienas būdas tai padaryti, tačiau netrukus mes ten pateksime.

  • Krovinys

    Ši apkrova, kurią mes naudosime šiai injekcijai, prasideda baitų seka 0xbf27 . gbk - negaliojantis multibito simbolis; latin1 tai yra eilutė ¿' . Atkreipkite dėmesį, kad latin1 ir gbk , 0x27 yra ' simbolis“.

    Mes pasirinkome šią naudingąją apkrovą, nes jei jį vadiname addslashes() , prieš simbolį turime įterpti ASCII, ty 0x5c . Taigi mes baigėme 0xbf5c27 , kuris 0xbf5c27 yra dviejų simbolių seka: 0xbf5c , po to - 0x27 . Kitaip tariant, galiojantis simbolis, po kurio seka ne izoliuotas. Tačiau mes nenaudojame „ addslashes() . Taigi kitas žingsnis ...

  • $ stmt-> Execute ()

    Svarbu suprasti, kad numatytasis SKVN daro NE iš tiesų parengtus pareiškimus. Jis imituoja juos („MySQL“). Todėl SKVN viduje sukuria užklausos eilutę, paskambindama mysql_real_escape_string() (MySQL C API funkcija) kiekvienai atitinkamos eilutės reikšmei.

    API C užklausa dėl mysql_real_escape_string() skiriasi nuo addslashes() , nes ji žino ryšio simbolių rinkinį. Taigi, jis gali tinkamai išeiti iš simbolių rinkinio, kurio laukia serveris. Tačiau iki šiol klientas mano, kad mes vis dar naudojame „ latin1 , kad galėtume prisijungti, nes niekada apie tai nekalbėjome. Mes pasakėme serveriui, kad mes naudojame gbk , bet klientas vis dar mano, kad jis yra latin1 .

    Todėl skambinant „ mysql_real_escape_string() įterpiamas mysql_real_escape_string() brūkšnys, o mūsų „ekranuotame“ turinyje yra laisvas kabantis simbolis! Tiesą sakant, jei pažvelgėme į $var į gbk simbolių gbk , gbk :

      OR „AR 1 = 1 / * 

    Būtent tai yra būtinas ataka.

  • Prašymas

    Ši dalis yra tik formalumas, tačiau čia pateiktas užklausa yra:

    sugrįš, kad imituotų teiginius, kuriuos MySQL negali paruošti iš pradžių: tuos, kurie gali būti pateikiami vadove , bet būkite atsargūs, pasirinkdami tinkamą serverio versiją. 

    Teisingas pleistras

    Problema ta, kad vietoj „ SET NAMES mysql_set_charset() C API mysql_set_charset() . Jei tai padarytume, būtų gerai, jei nuo 2006 m.

    Jei naudojate ankstesnę „MySQL“ versiją, „ mysql_real_escape_string() klaida reiškia, kad negaliojantys daugybiniai simboliai, pvz., Mūsų naudingosios apkrovos atveju, buvo laikomi vienu baitu, kad pabėgtų, net jei klientas buvo teisingai informuotas apie ryšio kodavimą, todėl šis ataka yra visa lygus bus sėkmingas. Klaida buvo nustatyta „MySQL“ 4.1.20 , 5.0.22 ir 5.1.11 .

    Bet blogiausia yra tai, kad PDO nerodė C API, skirta mysql_set_charset() iki 5.3.6, taigi ankstesnėse versijose jis negalėjo užkirsti kelio šiai atakai už kiekvieną galimą komandą! Dabar jis rodomas kaip parametras utf8mb4 nėra pažeidžiamas ir vis dar gali palaikyti kiekvieną Unicode simbolį: todėl galite jį naudoti, bet jis galimas tik po MySQL 5.5.3. Alternatyva yra utf8 , kuri taip pat nėra pažeidžiama ir gali paremti visą Unicode Basic daugiakalbę plokštumą .

    Arba galite įjungti NO_BACKSLASH_ESCAPES SQL režimą, kuris (be kitų dalykų) keičia mysql_real_escape_string() veikimą. Jei šis režimas įjungtas, 0x27 bus pakeistas į 0x2727 , o ne 0x5c27 , todėl atrankos procesas negalės sukurti galiojančių simbolių nė viename iš pažeidžiamų kodų, kur jų anksčiau nebuvo (ty 0xbf27 vis dar yra 0xbf27 ir tt) - taigi serveris vis tiek atmeta eilutę kaip netinkamą. Tačiau žr. @Eggyal atsakymą dėl kito pažeidžiamumo, kuris gali atsirasti naudojant šį SQL režimą (nors ne su SKVN).

    Saugūs pavyzdžiai

    Šie pavyzdžiai yra saugūs:

     mysql_query('SET NAMES utf8'); $var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1"); 

    Kadangi serveris tikisi utf8 ...

     mysql_set_charset('gbk'); $var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*"); mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1"); 

    Kadangi nustatėme, kad simbolių rinkinys yra teisingas, kad jis atitiktų klientą ir serverį.

     $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $pdo->query('SET NAMES gbk'); $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $stmt->execute(array("\xbf\x27 OR 1=1 /*")); 

    Kadangi išjungėme parengtus parengtus pareiškimus.

     $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password); $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $stmt->execute(array("\xbf\x27 OR 1=1 /*")); 

    Kadangi nustatėme simbolių rinkinį teisingai.

     $mysqli->query('SET NAMES gbk'); $stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $param = "\xbf\x27 OR 1=1 /*"; $stmt->bind_param('s', $param); $stmt->execute(); 

    Kadangi MySQLi veikia visą laiką su tikrais parengtais pareiškimais.

    Apvyniokite

    Jei:

    • Naudokite moderniąsias „MySQL“ versijas (pabaiga 5.1, visi 5.5, 5.6 ir tt) ir parametrą PDS DSN charset (PHP ≥ 5.3.6)

    Or

    • Nenaudokite pažeidžiamo simbolių rinkinio, kad užkoduotumėte ryšį (naudojate tik utf8 / latin1 / ascii / etc)

    Or

    • Įgalinti NO_BACKSLASH_ESCAPES SQL režimą

    Jūs esate 100% saugus.

    Priešingu atveju esate pažeidžiamas , nors naudojate paruoštus SKVN pranešimus ...

    Pridėti

    Lėtai dirbau su pleistru, kad pakeistumėte numatytąją vertę, kad nebūtų imituotas pasirengimas būsimai PHP versijai. Problema, su kuria susidūriau, yra ta, kad kai bandymai nutraukiami daugeliu bandymų. Viena iš problemų yra ta, kad imituoti vaistai vykdymo metu sukels tik sintaksės paklaidas, bet tikrieji paruošti produktai sukels klaidų paruošimo metu. Taigi, tai gali sukelti problemų (ir yra priežasties testo dalis).

693
30 авг. atsakymas pateikiamas ircmaxell 30 rug . 2012-08-30 20:22 '12, 08:22 pm 2012-08-30 20:22

Parengtos ataskaitos / parametrinės užklausos paprastai yra pakankamos, kad būtų užkirstas kelias pirmojo užsakymo šiam operatoriui įvedimui * . Jei naudojate nevaldomą dinaminį sql bet kur kitur savo paraiškoje, vis dar esate pažeidžiamas 2-osios eilės injekcijos.

Duomenys apie 2-osios eilės injekcijas yra cikliškai perduodami per duomenų bazę vieną kartą prieš įtraukiant juos į užklausą, ir daug sunkiau jį padaryti. AFAIK, jūs beveik niekada nematote realių suprojektuotų 2-osios eilės atakų, nes įsibrovėliams paprastai lengviau patekti į socialinę inžineriją, bet kartais 2-ojo užsakymo klaidos atsiranda dėl papildomų gerybinių simbolių ar pan.

Jūs galite atlikti antrosios eilės įpurškimo ataką, kai galite naudoti vertę duomenų bazėje, kuri vėliau bus naudojama kaip pažodinis. Pavyzdžiui, tarkime, kad įvesdami paskyrą tinklalapyje įvesite tokią informaciją kaip naują naudotojo vardą (darant prielaidą, kad „MySQL DB“ skirtas šiam klausimui):

 ' + (SELECT UserName + '_' + Password FROM Users LIMIT 1) + ' 
border=0

Jei naudotojo vardui nėra jokių kitų apribojimų, paruoštas operatorius vis tiek turi įsitikinti, kad įterpimo metu nebus atliktas aukščiau aprašytas užklausimas ir teisingai saugoma vertė į duomenų bazę. Tačiau įsivaizduokite, kad programa vėliau iš jūsų duomenų bazės iškelia jūsų naudotojo vardą ir naudoja eilutę, kad įtrauktų šią vertę į naują užklausą. Galite matyti kitą slaptažodį. Kadangi pirmieji keli naudotojo stalo pavadinimai paprastai yra administratoriai, jūs taip pat galėjote atsisakyti ūkio. (Taip pat atkreipkite dėmesį: tai dar viena priežastis, kodėl nereikia saugoti slaptažodžių paprastu tekstu!)

Taigi matome, kad paruošti pareiškimai yra pakankami vienai užklausai, bet jie patys nėra pakankami, kad apsaugotų nuo SQL injekcijos atakų per visą paraišką, nes jiems trūksta mechanizmo, užtikrinančio, kad visa prieiga prie duomenų bazės taikomojoje programoje yra saugi kodą Tačiau ji naudojama kaip geros taikomųjų programų dalis, kuri gali apimti tokius metodus kaip kodų peržiūra arba statinė analizė, arba naudojant ORM lygį, duomenų lygį arba paslaugų lygį, kuris riboja dinaminius SQL paruoštus operatorius, yra pagrindinė priemonė „SQL“ problemai spręsti. Įpurškimas. Jei laikotės gerų taikomųjų programų kūrimo principų, kad jūsų duomenų prieiga būtų atskirta nuo likusios programos dalies, tampa lengva užtikrinti ar patikrinti, ar kiekvienas užklausa teisingai naudoja parametrus. Šiuo atveju visiškai neįtraukiama visa injekcija (tiek pirmoji, tiek antroji tvarka).


* Pasirodo, kad MySql / PHP (paprastai buvo) tik kvailas dėl parametrų apdorojimo, kai dalyvauja plati simboliai, ir yra dar vienas retas atvejis, aprašytas kitame labai priimtame atsakyme, kuris gali leisti injekcijai paslysti per parametruojamą užklausą.

495
25 сент. Joel Coehoorn atsakymas, rugsėjo 25 d 2008-09-25 18:50 '08, 18:50, 2008-09-25 18:50

Ne, jie ne visada.

Tai priklauso nuo to, ar leisite naudotojui įvesti patį prašymą. Pavyzdžiui:

 $dbh = new PDO("blahblah"); $tableToUse = $_GET['userTable']; $stmt = $dbh->prepare('SELECT * FROM ' . $tableToUse . ' where username = :username'); $stmt->execute( array(':username' => $_REQUEST['username']) ); 

bus pažeidžiamas SQL injekcijoms, o paruoštų pareiškimų naudojimas šiame pavyzdyje neveiks, nes naudotojo įvestis naudojama kaip identifikatorius, o ne kaip duomenys. Teisingas atsakymas yra naudoti tam tikrą filtravimo / tikrinimo rūšį, pavyzdžiui:

 $dbh = new PDO("blahblah"); $tableToUse = $_GET['userTable']; $allowedTables = array('users','admins','moderators'); if (!in_array($tableToUse,$allowedTables)) $tableToUse = 'users'; $stmt = $dbh->prepare('SELECT * FROM ' . $tableToUse . ' where username = :username'); $stmt->execute( array(':username' => $_REQUEST['username']) ); 

Pastaba: negalite naudoti SKVN duomenų, kurie viršija DDL (Duomenų apibrėžimo kalba), ty neveikia:

 $stmt = $dbh->prepare('SELECT * FROM foo ORDER BY :userSuppliedData'); 

Taip nėra, nes DESC ir ASC nėra duomenys. SKVN gali ištrūkti tik dėl duomenų. Antra, jūs negalite netgi pateikti ' citatos“. Vienintelis būdas leisti individualų rūšiavimą yra rankiniu būdu filtruoti ir patikrinti, ar jis yra DESC arba ASC .

40
21 апр. Atsakymas pateikiamas bokšto 21 d. 2010-04-21 12:00 '10 12:00 val. 2010-04-21 12:00

Taip, pakanka. Kai injekcijos tipas atakuoja, kažkaip gaunamas vertėjas (duomenų bazė), kad būtų įvertintas kažkas, kas turėjo būti duomenų, tarsi jis būtų kodas. Tai įmanoma tik tuo atveju, jei toje pačioje priemonėje sumaišote kodą ir duomenis (pvz., Kurdami užklausą kaip eilutę).

Parametruoti užklausos veikia siunčiant kodą ir duomenis atskirai, todėl niekada negalėsite rasti skylės.

Tačiau vis tiek gali būti pažeidžiami kiti atakos, pvz., Injekcijos. Pvz., Jei naudojate duomenis HTML puslapyje, galite būti veikiami XSS atakų.

24
25 сент. Atsakymas yra suteiktas 25 sek . 2008-09-25 18:55 '08, 18:55, 2008-09-25 18:55

Ne, tai nepakanka (kai kuriais konkrečiais atvejais)! Numatyta, kad SKVN naudoja emuliuojamus paruoštus teiginius, kai naudojasi MySQL duomenų bazės tvarkykle. Naudodami „MySQL“ ir SKVN visada turėtumėte išjungti parengtus parengtus pareiškimus:

 $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 

Kitas dalykas, kurį visada reikia padaryti, yra nustatyti tinkamą duomenų bazės kodavimą:

 $dbh = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass'); 

Taip pat žr. Šį susijusį klausimą: Kaip išvengti SQL injekcijos PHP?

Taip pat atkreipkite dėmesį, kad tai taikoma tik duomenų bazei, kurią jums reikia peržiūrėti, kai rodote duomenis. Pavyzdžiui. naudodami htmlspecialchars() dar kartą naudodami tinkamą kodavimo ir citavimo tipą.

24
30 авг. Atsakymas pateikiamas PeeHaa 30 rug . 2012-08-30 20:00 „12, 8:00 val. 2012-08-30 20:00

Asmeniškai aš visada vykdysiu tam tikrą duomenų sanitarijos formą, nes jūs niekada negalite pasitikėti vartotojo įvedimu, bet, naudojant vietos žymenis / parametrus, įvesties duomenys siunčiami į serverį atskirai SQL operatoriuje ir tada sujungti. Svarbiausias dalykas yra tai, kad jis susieja pateiktus duomenis su konkrečiu tipu ir konkrečiu naudojimu ir pašalina bet kokią galimybę keisti SQL aprašo logiką.

9
25 сент. JimmyJ atsakė 25 rugsėjo 2008-09-25 18:50 '08, 18:50, 2008-09-25 18:50

Eaven, jei ketinate užkirsti kelią SQL injekcijų injekcijoms naudodami html ar js čekius, turėtumėte nepamiršti, kad priekiniai patikrinimai yra „apeiti“.

Galite išjungti js arba redaguoti šabloną naudodami pradinį kūrimo įrankį (pastatytas su „Firefox“ arba „Chrome“).

Taigi, norint užkirsti kelią SQL injekcijai, būtų teisinga išvalyti pirminę datų duomenų bazę valdytojo viduje.

Siūlyčiau naudoti savo PHP funkciją filter_input (), kad išvalytumėte GET ir INPUT reikšmes.

Jei norite tęsti saugumą pagrįstoms duomenų bazių užklausoms, siūlau naudoti įprastą išraišką, kad patikrintumėte duomenų formatą. Šiuo atveju „Preg_match“ () padės jums! Bet būkite atsargūs! Regex variklis nėra toks lengvas. Naudokite tik jei reikia, kitaip jūsų paraiškos bus sumažintos.

Saugumas turi kaštų, bet nenaudokite našumo!

Paprastas pavyzdys:

jei norite du kartus patikrinti, ar iš GET numerio gauta vertė yra mažesnė nei 99, jei (! preg_match ('/ [0-9] {1,2} /')) {...} yra sunkesnė

 if (isset($value)  intval($value)) <99) {...} 

Taigi galutinis atsakymas yra: „Ne! Parengtos SKVN ataskaitos neužkerta kelio visų tipų SQL injekcijoms“; Tai neužkerta kelio netikėtoms vertėms, tiesiog netikėtai susietai.

-2
04 марта '18 в 23:17 2018-03-04 23:17 atsakymas pateikiamas snipershady kovo 04-18 d. 11:17 val. 2018-03-04 23:17

Kiti klausimai apie arba Užduoti klausimą