Hoe kan ik SQL-injectie in PHP voorkomen?

Als gebruikersinvoer zonder wijziging wordt ingevoegd in een SQL-query, wordt de applicatie kwetsbaar voor SQL-injectie, zoals in de volgend voorbeeld:

$unsafe_variable = $_POST['user_input']; 
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

Dat komt omdat de gebruiker zoiets als value'); DROP TABLE table;--, en de vraag wordt:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

Wat kan er worden gedaan om dit te voorkomen?


Antwoord 1, autoriteit 100%

De juiste manier om SQL-injectie-aanvallen te voorkomen, ongeacht welke database u gebruikt, is door de niet-vertrouwde invoer correct te coderenzodat gegevens gegevens blijven en nooit geïnterpreteerdals opdrachten door de SQL-parser. Er zijn meerdere manieren om de gegevens veilig te coderen. Als u de details niet volledigbegrijpt, moet u altijd voorbereide instructies en geparametriseerde query’s gebruiken.Dit zijn SQL-instructies die afzonderlijk worden verzonden naar en geparseerd door de databaseserver eventuele parameters. Op deze manier is het voor een aanvaller onmogelijk om kwaadaardige SQL te injecteren.

Je hebt in principe twee opties om dit te bereiken:

  1. Met behulp van BOB(voor elk ondersteund databasestuurprogramma):

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
     $stmt->execute([ 'name' => $name ]);
     foreach ($stmt as $row) {
         // Do something with $row
     }
    
  2. mysqli (voor MySQL):

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
     $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
     $stmt->execute();
     $result = $stmt->get_result();
     while ($row = $result->fetch_assoc()) {
         // Do something with $row
     }
    

Als u aansluit op een andere database dan MySQL, is er een chauffeurspecifieke tweede optie die u kunt verwijzen (bijvoorbeeld pg_prepare()en pg_execute()voor PostgreSQL). PDO is de universele optie.


correct instellen van de verbinding

Houd er rekening mee dat wanneer u PDOgebruikt om toegang te krijgen tot een MySQL-database ECHTE Voorbereide verklaringen is Niet-standaard gebruikt . Om dit te verhelpen, moet u de emulatie van bereide uitspraken uitschakelen. Een voorbeeld van het maken van een verbinding met PDO is:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

In het bovenstaande voorbeeld is de foutmodus niet strikt noodzakelijk, , maar wordt geadviseerd om het toe te voegen . Op deze manier stopt het script niet met een Fatal Errorwanneer er iets misgaat. En het geeft de ontwikkelaar de kans op catchElke fout (en) die thrown als PDOExceptionS.

Wat echter verplichtis, is de eerste regel setAttribute(), die PDO vertelt om geëmuleerde voorbereide instructies uit te schakelen en realvoorbereide verklaringen. Dit zorgt ervoor dat de instructie en de waarden niet door PHP worden geparseerd voordat ze naar de MySQL-server worden verzonden (waardoor een mogelijke aanvaller geen kans krijgt om kwaadaardige SQL te injecteren).

Hoewel je de charsetkunt instellen in de opties van de constructor, is het belangrijk op te merken dat ‘oudere’ versies van PHP (vóór 5.3.6) negeerde stilletjes de charset parameterin de DSN.


Uitleg

De SQL-instructie die u doorgeeft aan preparewordt geparseerd en gecompileerd door de databaseserver. Door parameters op te geven (ofwel een ?of een benoemde parameter zoals :namein het bovenstaande voorbeeld) vertel je de database-engine waar je op wilt filteren. Wanneer u vervolgens executeaanroept, wordt de voorbereide instructie gecombineerd met de parameterwaarden die u opgeeft.

Het belangrijkste hier is dat de parameterwaarden worden gecombineerd met de gecompileerde instructie, niet met een SQL-tekenreeks. SQL-injectie werkt door het script te misleiden om kwaadaardige tekenreeksen op te nemen wanneer het SQL maakt om naar de database te verzenden. Dus door de eigenlijke SQL apart van de parameters te verzenden, beperk je het risico dat je eindigt met iets wat je niet van plan was.

Alle parameters die u verzendt wanneer u een voorbereide instructie gebruikt, worden alleen behandeld als tekenreeksen (hoewel de database-engine enige optimalisatie kan doen, dus parameters kunnen natuurlijk ook als getallen eindigen). Als in het bovenstaande voorbeeld de variabele $name'Sarah'; DELETE FROM employeeshet resultaat zou gewoon een zoekopdracht zijn naar de tekenreeks "'Sarah'; DELETE FROM employees", en u zult niet eindigen met een lege tabel.

Een ander voordeel van het gebruik van voorbereide instructies is dat als u dezelfde instructie vele malen in dezelfde sessie uitvoert, deze slechts één keer wordt geparseerd en gecompileerd, wat u wat snelheidswinst oplevert.

O, en aangezien je vroeg hoe je het voor een insert moest doen, is hier een voorbeeld (met PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute([ 'column' => $unsafeValue ]);

Kunnen voorbereide instructies worden gebruikt voor dynamische zoekopdrachten?

Hoewel u nog steeds voorbereide instructies voor de queryparameters kunt gebruiken, kan de structuur van de dynamische query zelf niet worden geparametreerd en kunnen bepaalde queryfuncties niet worden geparametreerd.

Voor deze specifieke scenario’s kunt u het beste een whitelist-filter gebruiken dat de mogelijke waarden beperkt.

// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}

Antwoord 2, autoriteit 18%

Verouderde waarschuwing:
De voorbeeldcode van dit antwoord (zoals de voorbeeldcode van de vraag) gebruikt de PHP-extensie MySQL, die in PHP 5.5.0 is verouderd en in PHP 7.0.0 volledig is verwijderd.

Beveiligingswaarschuwing: dit antwoord is niet in overeenstemming met best practices op het gebied van beveiliging. Ontsnappen is onvoldoende om te voorkomen SQL-injectie, gebruik in plaats daarvan voorbereide instructies. Gebruik de onderstaande strategie op eigen risico. (Ook mysql_real_escape_string()is verwijderd in PHP 7.)

Als je een recente versie van PHP gebruikt, is de optie mysql_real_escape_stringdie hieronder wordt beschreven niet langer beschikbaar (hoewel mysqli::escape_stringeen modern equivalent is). Tegenwoordig zou de optie mysql_real_escape_stringalleen zinvol zijn voor oudere code op een oude versie van PHP.


Je hebt twee opties: de speciale tekens in je unsafe_variableomzeilen, of een geparametriseerde query gebruiken. Beide zouden u beschermen tegen SQL-injectie. De geparametriseerde query wordt als de beste methode beschouwd, maar moet worden gewijzigd naar een nieuwere MySQL-extensie in PHP voordat u deze kunt gebruiken.

We behandelen eerst de onderste impactreeks die aan één ontsnapt.

//Connect
$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
//Disconnect

Zie ook de details van de functie mysql_real_escape_string.

Om de geparametriseerde query te gebruiken, moet je MySQLigebruiken in plaats van de MySQL-functies. Om uw voorbeeld te herschrijven, hebben we iets als het volgende nodig.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");
    // TODO - Check that connection was successful.
    $unsafe_variable = $_POST["user-input"];
    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");
    // TODO check that $stmt creation succeeded
    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);
    $stmt->execute();
    $stmt->close();
    $mysqli->close();
?>

De belangrijkste functie waarover u zich wilt inlezen, is mysqli::prepare.

Ook, zoals anderen hebben gesuggereerd, vindt u het misschien handig/gemakkelijker om een abstractielaag op te voeren met iets als BOB.

Houd er rekening mee dat de zaak waar u naar vroeg een vrij eenvoudige zaak is en dat voor complexere gevallen meer complexe benaderingen nodig zijn. In het bijzonder:

  • Als je de structuur van de SQL wilt wijzigen op basis van gebruikersinvoer, zullen geparametriseerde query’s niet helpen, en de vereiste escape wordt niet gedekt door mysql_real_escape_string. In dit soort gevallen is het beter om de invoer van de gebruiker door een witte lijst te leiden om ervoor te zorgen dat alleen ‘veilige’ waarden worden doorgelaten.
  • Als u gehele getallen uit gebruikersinvoer in een voorwaarde gebruikt en de mysql_real_escape_string-benadering volgt, krijgt u te maken met het probleem dat wordt beschreven door Polynoomin de opmerkingen hieronder. Dit geval is lastiger omdat gehele getallen niet tussen aanhalingstekens staan, dus u kunt dit oplossen door te valideren dat de gebruikersinvoer alleen cijfers bevat.
  • Er zijn waarschijnlijk andere gevallen waarvan ik niet op de hoogte ben. Misschien vindt u diteen nuttige bron voor enkele van de meer subtiele problemen die u kunt tegenkomen.

Antwoord 3, autoriteit 12%

Elk antwoord hier dekt slechts een deel van het probleem.
In feite zijn er vierverschillende query-onderdelen die we dynamisch aan SQL kunnen toevoegen: –

  • een tekenreeks
  • een getal
  • een ID
  • een syntaxiszoekwoord

En voorbereide verklaringen hebben betrekking op slechts twee ervan.

Maar soms moeten we onze zoekopdracht nog dynamischer maken door ook operators of identifiers toe te voegen.
We hebben dus verschillende beveiligingstechnieken nodig.

Over het algemeen is een dergelijke beschermingsbenadering gebaseerd op whitelisting.

In dit geval moet elke dynamische parameter hardgecodeerd zijn in uw script en uit die set worden gekozen.
Om bijvoorbeeld dynamisch te bestellen:

$orders  = array("name", "price", "qty"); // Field names
$key = array_search($_GET['sort'], $orders)); // if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. 
$query = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

Om het proces te vergemakkelijken heb ik een whitelist helper-functiegeschreven die al het werk in één regel doet:

$orderby = white_list($_GET['orderby'], "name", ["name","price","qty"], "Invalid field name");
$query  = "SELECT * FROM `table` ORDER BY `$orderby`"; // sound and safe

Er is een andere manier om identifiers te beveiligen – ontsnappen, maar ik blijf liever bij whitelisting als een robuustere en explicietere benadering. Maar zolang je een identifier hebt geciteerd, kun je ontsnappen aan het aanhalingsteken om het veilig te maken. Voor mysql moet u bijvoorbeeld standaard het aanhalingsteken verdubbelen om eraan te ontsnappen . Voor andere andere DBMS zijn de escape-regels anders.

Toch is er een probleem met SQL-syntaxiszoekwoorden (zoals AND, DESCen dergelijke), maar in dit geval lijkt whitelisting de enige benadering.

Een algemene aanbeveling kan dus worden geformuleerd als

  • Elke variabele die een letterlijke SQL-data vertegenwoordigt (of, om het simpel te zeggen – een SQL-string of een getal) moet worden toegevoegd via een voorbereide instructie. Geen uitzonderingen.
  • Elk ander zoekgedeelte, zoals een SQL-sleutelwoord, een tabel- of veldnaam of een operator, moet door een witte lijst worden gefilterd.

Bijwerken

Hoewel er een algemene overeenstemming is over de beste praktijken met betrekking tot SQL-injectiebescherming, zijn er ook nog steeds veel slechte praktijken. en sommigen van hen die te diep geworteld zijn in de geest van PHP-gebruikers. Op deze pagina zijn er bijvoorbeeld (hoewel onzichtbaar voor de meeste bezoekers) meer dan 80 verwijderde antwoorden – allemaal verwijderd door de gemeenschap als gevolg van slechte kwaliteit of het bevorderen van slechte en verouderde praktijken. Erger nog, sommige van de slechte antwoorden worden niet verwijderd, maar eerder welvaren.

Bijvoorbeeld, daar (1) zijn (2) stil (3) Veel (4) antwoorden (5) , inclusief de Second Meest opgewerkt antwoord suggereren u handmatige string die ontsnapt is – een verouderde aanpak die is bewezen onzeker.

of er is een iets beter antwoord dat suggereert alleen een andere methode van string-formattering en heeft het zelfs op het ultieme panacea. Terwijl natuurlijk het niet is. Deze methode is niet beter dan reguliere string-opmaak, maar het houdt al zijn nadelen: het is alleen van toepassing op snaren en, zoals elke andere handmatige opmaak, is het in wezen optionele, niet-verplichte maatregel, vatbaar voor menselijke fouten van welke aard dan ook, vatbaar voor menselijke fout van welke aard dan ook.

Ik denk dat dit allemaal vanwege een heel oud bijgeloof, ondersteund door dergelijke autoriteiten als Owasp of De PHP-handleiding , die gelijkheid uitkondigt tussen wat “ontsnappen” en bescherming van SQL-injecties.

Ongeacht de PHP-handleiding die al eeuwenlang heeft gezegd, *_escape_stringin geen geval maakt gegevens veilig en zijn nooit bedoeld. Naast het nutteloos zijn voor een ander SQL-onderdeel dan string, is handmatige ontsnapping verkeerd, omdat het handmatig is als tegengesteld aan geautomatiseerd.

en Owasp maakt het nog erger, benadrukt op het ontsnappen Gebruikersinvoer , wat een totale onzin is: er zouden geen dergelijke woorden moeten zijn in de context van injectiebescherming. Elke variabele is potentieel gevaarlijk – ongeacht de bron! Of, met andere woorden – elke variabele moet correct worden geformatteerd om in een query te worden geplaatst – ongeacht de bron opnieuw. Het is de bestemming die er toe doet. Op het moment dat een ontwikkelaar het schapen van de geiten begint te scheiden (denken of een bepaalde variabele “veilig” of niet is) hij / zij neemt zijn / haar eerste stap naar ramp. Om nog maar te zwijgen van het feit dat zelfs de formulering bulk suggereert bij het instappunt, dat lijkt op de zeer magische citaten-functie – reeds veracht, verouderd en verwijderd.

Dus, in tegenstelling tot alles “ontsnappen”, is bereide verklaringen de maatregel die inderdaad beschermt tegen SQL-injectie (indien van toepassing).


4, Autoriteit 9%

Ik zou aanraden PDO (PHP-gegevensobjecten) aan te gebruiken voer parameterized SQL-query’s uit.

Dit beschermt niet alleen tegen SQL-injectie, maar versnelt ook zoekopdrachten.

En door PDO te gebruiken in plaats van mysql_, mysqli_en pgsql_functies, maakt u uw toepassing een beetje meer geabstraheerd van de database, in het zeldzame geval dat u van databaseprovider moet wisselen.


Antwoord 5, autoriteit 7%

Gebruik PDOen voorbereide zoekopdrachten.

($connis een PDO-object)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();

Antwoord 6, autoriteit 6%

Zoals je kunt zien, raden mensen je aan om maximaal voorbereide verklaringen te gebruiken. Het is niet verkeerd, maar wanneer uw zoekopdracht slechts één keerper proces wordt uitgevoerd, zou er een kleine prestatievermindering zijn.

Ik zat met dit probleem, maar ik denk dat ik het op een zeerverfijnde manier heb opgelost – de manier waarop hackers gebruiken om het gebruik van aanhalingstekens te vermijden. Ik gebruikte dit in combinatie met geëmuleerde voorbereide verklaringen. Ik gebruik het om allesoorten mogelijke SQL-injectie-aanvallen te voorkomen.

Mijn aanpak:

  • Als u verwacht dat de invoer een geheel getal is, zorg er dan voor dat het echtgeheel getal is. In een taal van het variabele type zoals PHP is dit zeerbelangrijk. U kunt bijvoorbeeld deze zeer eenvoudige maar krachtige oplossing gebruiken: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • Als je iets anders verwacht van integer hex it. Als je het hext, ontsnap je perfect aan alle invoer. In C/C++ is er een functie genaamd mysql_hex_string(), in PHP kun je bin2hex().

    Maak je geen zorgen dat de escape-tekenreeks 2x zo groot is als de oorspronkelijke lengte, want zelfs als je mysql_real_escape_stringgebruikt, moet PHP dezelfde capaciteit toewijzen ((2*input_length)+1), wat hetzelfde is.

  • Deze hex-methode wordt vaak gebruikt wanneer u binaire gegevens overdraagt, maar ik zie geen reden waarom deze niet op alle gegevens zou worden gebruikt om SQL-injectieaanvallen te voorkomen. Houd er rekening mee dat u gegevens moet toevoegen met 0xof in plaats daarvan de MySQL-functie UNHEXmoet gebruiken.

Dus bijvoorbeeld de vraag:

SELECT password FROM users WHERE name = 'root';

Wordt:

SELECT password FROM users WHERE name = 0x726f6f74;

of

SELECT password FROM users WHERE name = UNHEX('726f6f74');

Hex is de perfecte ontsnapping. Geen manier om te injecteren.

Verschil tussen UNHEX-functie en 0x-prefix

Er was enige discussie in opmerkingen, dus ik wil het eindelijk duidelijk maken. Deze twee benaderingen lijken erg op elkaar, maar verschillen in sommige opzichten een beetje:

Het voorvoegsel 0xkan alleen worden gebruikt voor gegevenskolommen zoals char, varchar, text, block, binary, enz.
Het gebruik ervan is ook een beetje ingewikkeld als u op het punt staat een lege string in te voegen. Je moet het volledig vervangen door '', anders krijg je een foutmelding.

UNHEX()werkt op elkekolom; je hoeft je geen zorgen te maken over de lege string.


Hex-methoden worden vaak gebruikt als aanvallen

Merk op dat deze hex-methode vaak wordt gebruikt als een SQL-injectie-aanval waarbij gehele getallen net als strings zijn en alleen met mysql_real_escape_stringontsnapt. Dan kunt u het gebruik van aanhalingstekens vermijden.

Als je bijvoorbeeld zoiets als dit doet:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

een aanval kan u heel gemakkelijkinjecteren. Overweeg de volgende geïnjecteerde code die door uw script wordt geretourneerd:

SELECT ... WHERE id = -1 UNION ALL SELECT table_name FROM information_schema.tables;

en nu gewoon de tabelstructuur extraheren:

SELECT ... WHERE id = -1 UNION ALL SELECT column_name FROM information_schema.column WHERE table_name = __0x61727469636c65__;

En selecteer dan gewoon de gegevens die men wil. Is het niet cool?

Maar als de codeur van een injecteerbare site het zou beheksen, zou er geen injectie mogelijk zijn omdat de zoekopdracht er als volgt uit zou zien:

SELECT ... WHERE id = UNHEX('2d312075...3635');

Antwoord 7, autoriteit 5%

Verouderde waarschuwing:
De voorbeeldcode van dit antwoord (zoals de voorbeeldcode van de vraag) gebruikt de PHP-extensie MySQL, die in PHP 5.5.0 is verouderd en in PHP 7.0.0 volledig is verwijderd.

Beveiligingswaarschuwing: dit antwoord is niet in overeenstemming met best practices op het gebied van beveiliging. Ontsnappen is onvoldoende om te voorkomen SQL-injectie, gebruik in plaats daarvan voorbereide instructies. Gebruik de onderstaande strategie op eigen risico. (Ook mysql_real_escape_string()is verwijderd in PHP 7.)

BELANGRIJK

De beste manier om SQL-injectie te voorkomen, is door Prepared Statementsin plaats van te escapente gebruiken, zoals het geaccepteerde antwoordlaat zien.

Er zijn bibliotheken zoals Aura.Sqlen EasyDBwaarmee ontwikkelaars voorbereide instructies gemakkelijker kunnen gebruiken. Voor meer informatie over waarom voorbereide verklaringen beter zijn, ga naar het stoppen van SQL-injectie, raadpleeg deze mysql_real_escape_string()bypassen onlangs opgeloste Unicode SQL Injection-kwetsbaarheden in WordPress.

Injectiepreventie – mysql_real_escape_string()

PHP heeft een speciaal gemaakte functie om deze aanvallen te voorkomen. Het enige dat u hoeft te doen, is de hele mondvol van een functie gebruiken, mysql_real_escape_string.

mysql_real_escape_stringneemt een string die gebruikt gaat worden in een MySQL-query en retourneert dezelfde string waarbij alle SQL-injectiepogingen veilig zijn ontsnapt. In principe vervangt het de lastige aanhalingstekens(‘) die een gebruiker zou kunnen invoeren met een MySQL-veilige vervanging, een aanhalingsteken met escapetekens \’.

OPMERKING:u moet verbonden zijn met de database om deze functie te gebruiken!

// Maak verbinding met MySQL

$name_bad = "' OR 1'"; 
$name_bad = mysql_real_escape_string($name_bad);
$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";
$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 
$name_evil = mysql_real_escape_string($name_evil);
$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

U kunt meer details vinden in MySQL – Preventie van SQL-injectie .


Antwoord 8, autoriteit 5%

Je zou zoiets eenvoudigs als dit kunnen doen:

$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

Dit zal niet elk probleem oplossen, maar het is een zeer goede opstap. Ik heb voor de hand liggende items weggelaten, zoals het controleren van het bestaan van de variabele, het formaat (cijfers, letters, enz.).


Antwoord 9, autoriteit 4%

Wat u ook gebruikt, zorg ervoor dat u controleert of uw invoer niet al is verminkt door magic_quotesof andere goedbedoelde onzin, en indien nodig doorloopt u stripslashesof wat dan ook om het te ontsmetten.


Antwoord 10, autoriteit 4%

Verouderde waarschuwing:
De voorbeeldcode van dit antwoord (zoals de voorbeeldcode van de vraag) gebruikt de PHP-extensie MySQL, die in PHP 5.5.0 is verouderd en in PHP 7.0.0 volledig is verwijderd.

Beveiligingswaarschuwing: dit antwoord is niet in overeenstemming met best practices op het gebied van beveiliging. Ontsnappen is onvoldoende om te voorkomen SQL-injectie, gebruik in plaats daarvan voorbereide instructies. Gebruik de onderstaande strategie op eigen risico. (Ook mysql_real_escape_string()is verwijderd in PHP 7.)

Geparametriseerde query EN invoervalidatie is de juiste keuze. Er zijn veel scenario’s waarin SQL-injectie kan plaatsvinden, ook al is mysql_real_escape_string()gebruikt.

Deze voorbeelden zijn kwetsbaar voor SQL-injectie:

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

of

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

In beide gevallen kunt u 'niet gebruiken om de inkapseling te beschermen.

Bron : De onverwachte SQL-injectie (bij het ontsnappen is dat niet Genoeg)


11, Autoriteit 3%

Naar mijn mening, de beste manier om in het algemeen SQL-injectie in uw PHP-toepassing (of elke webtoepassing, voor die zaak) te voorkomen, is om na te denken over de architectuur van uw toepassing. Als de enige manier om te beschermen tegen SQL-injectie te onthouden om een ​​speciale methode of functie te gebruiken die het juiste ding doet telkens wanneer u met de database praat, doet u het verkeerd. Op die manier is het gewoon een kwestie van tijd totdat u vergeet om uw zoekopdracht correct op een bepaald moment in uw code te formatteren.

Het MVC-patroon aannemen en een raamwerk zoals CakePHP of CodeNigniter is waarschijnlijk de juiste manier om te gaan: gemeenschappelijke taken zoals het creëren van veilige databasequery’s zijn opgelost en centraal geïmplementeerd in dergelijke kaders. Ze helpen u bij het organiseren van uw webtoepassing op een verstandige manier en blijkt dat u meer nadenkt over het laden en opslaan van objecten dan het veilig bouwen van enkele SQL-query’s.


12, Autoriteit 3%

Ik gunst opgeslagen procedures (MySQL heeft een opgeslagen procedures ondersteuning sinds 5,0 ) vanuit een beveiligingsoogpunt – de voordelen zijn –

  1. De meeste databases (inclusief MySQL ) Gebruik de toegang van de gebruiker om te worden beperkt tot het uitvoeren van opgeslagen procedures. De fijnkorrelige veiligheidstoegangscontrole is nuttig om escalatie van aanvallen van privileges te voorkomen. Dit voorkomt gecompromitteerde applicaties die SQL rechtstreeks tegen de database kunnen uitvoeren.
  2. Ze abstracteren de RAW SQL-query van de toepassing, zodat minder informatie van de databasestructuur beschikbaar is voor de toepassing. Dit maakt het moeilijker voor mensen om de onderliggende structuur van de database te begrijpen en geschikte aanvallen te ontwerpen.
  3. accepteren ze alleen parameters, dus de voordelen van geparametriseerde query’s zijn er. Natuurlijk – IMO moet u nog steeds uw invoer ontsmetten – vooral als u dynamische SQL in de opgeslagen procedure gebruikt.

De nadelen zijn –

  1. zij (opgeslagen procedures) zijn moeilijk om te handhaven en hebben de neiging om zeer snel te vermenigvuldigen. Dit maakt het beheer van een probleem.
  2. Ze zijn niet erg geschikt voor dynamische query’s – als ze worden gebouwd om dynamische code te accepteren als parameters, dan worden veel van de voordelen teniet gedaan.

13, Autoriteit 3%

Er zijn veel manieren om SQL-injecties en andere SQL-hacks te voorkomen. U kunt het gemakkelijk vinden op internet (Google Search). Natuurlijk is PDO een van de goede oplossingen. Maar ik zou u graag enkele goede links preventie van SQL-injectie willen voorstellen.

Wat is SQL-injectie en hoe te voorkomen

PHP handleiding voor SQL-injectie

Microsoft-uitleg van SQL-injectie en preventie in PHP

En een andere als SQL-injectie voorkomen met MySQL en PHP .

Nu, Waarom u uw vraag van SQL-injectie wilt voorkomen?

Ik wil het je laten weten: waarom proberen we voor het voorkomen van SQL-injectie met een kort voorbeeld hieronder:

Query voor inlogverificatiematch:

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

Nu, als iemand (een hacker)

zet

$_POST['email']= [email protected]' OR '1=1

en wachtwoord alles ….

De query wordt alleen in het systeem geparseerd tot:

$query="select * from users where email='[email protected]' OR '1=1';

Het andere deel wordt weggegooid. Dus wat zal er gebeuren? Een niet-geautoriseerde gebruiker (hacker) kan inloggen als beheerder zonder zijn / haar wachtwoord te hebben. Nu kan hij / zij alles doen wat de beheerder / e-mail persoon kan doen. Zie, het is erg gevaarlijk als SQL-injectie niet wordt voorkomen.


14, Autoriteit 3%

Ik denk dat als iemand PHP en MySQL of een andere databaseserver wil gebruiken:

  1. Denk na over het leren van BOB(PHP Data Objects) – het is een databasetoegangslaag die een uniforme methode voor toegang tot meerdere databases.
  2. Denk na over het leren van MySQLi
  3. Gebruik native PHP-functies zoals: strip_tags, mysql_real_escape_stringof als variabele numeriek, gewoon (int)$foo. Lees hiermeer over het type variabelen in PHP. Als je bibliotheken zoals PDO of MySQLi gebruikt, gebruik dan altijd PDO::quote()en mysqli_real_escape_string().

Voorbeelden van bibliotheken:

—- BOB

—– Geen tijdelijke aanduidingen – rijp voor SQL-injectie! Het is slecht

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");

—– Niet-benoemde tijdelijke aanduidingen

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

—– Benoemde tijdelijke aanduidingen

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');
    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

PS:

PDO wint deze strijd met gemak. Met ondersteuning voor twaalf
verschillende databasestuurprogramma’s en benoemde parameters, kunnen we de
klein prestatieverlies en wennen aan de API. Van een beveiliging
standpunt, beide zijn veilig zolang de ontwikkelaar ze gebruikt
de manier waarop ze zouden moeten worden gebruikt

Maar hoewel zowel PDO als MySQLi vrij snel zijn, presteert MySQLi goed
onbeduidend sneller in benchmarks – ~ 2,5% voor niet-voorbereid
verklaringen, en ~6,5% voor voorbereide verklaringen.

En test elke zoekopdracht naar uw database – dit is een betere manier om injectie te voorkomen.


Antwoord 15, autoriteit 3%

Creëer indien mogelijk de typen van uw parameters. Maar het werkt alleen met eenvoudige typen zoals int, bool en float.

$unsafe_variable = $_POST['user_id'];
$safe_variable = (int)$unsafe_variable ;
mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

16, Autoriteit 2%

Voor degenen die niet zeker zijn van het gebruik van PDO (afkomstig van de mysql_Functies), maakte ik een Zeer eenvoudige PDO wrapper dat is een enkel bestand. Het bestaat om te laten zien hoe gemakkelijk het is om alle gewone dingen te doen die applicaties moeten worden gedaan. Werkt met PostgreSQL, MySQL en SQLITE.

Lees het in feite terwijl u de handleiding leest om te zien hoe u de PDO-functies in het echte leven kunt gebruiken Om het eenvoudig te maken om waarden op te slaan en op te halen in het formaat You Want.

Ik wil een enkele kolom

$count = DB::column('SELECT COUNT(*) FROM `user`);

Ik wil een array (sleutel = & gt; waarde) resultaten (d.w.z. voor het maken van een selectebox)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

Ik wil een resultaat met één rij

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

Ik wil een reeks resultaten

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));

Antwoord 17, autoriteit 2%

Met deze PHP-functie mysql_escape_string()kun je op een snelle manier een goede preventie krijgen.

Bijvoorbeeld:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string— Ontsnapt aan een tekenreeks voor gebruik in een mysql_query

Voor meer preventie kunt u aan het einde toevoegen …

wHERE 1=1   or  LIMIT 1

Eindelijk krijg je:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1

Antwoord 18, autoriteit 2%

Een paar richtlijnen voor het vermijden van speciale tekens in SQL-instructies.

Gebruik MySQLniet. Deze extensie is verouderd. Gebruik MySQLiof BOBin plaats daarvan.

MySQLi

Voor het handmatig escapen van speciale tekens in een tekenreeks kunt u de gebruiken mysqli_real_escape_stringfunctie. De functie werkt niet goed tenzij de juiste tekenset is ingesteld met mysqli_set_charset.

Voorbeeld:

$mysqli = new mysqli('host', 'user', 'password', 'database');
$mysqli->set_charset('charset');
$string = $mysqli->real_escape_string($string);
$mysqli->query("INSERT INTO table (column) VALUES ('$string')");

Gebruik mysqli_preparevoor automatisch escapen van waarden met voorbereide instructies, en mysqli_stmt_bind_paramwaar typen voor de overeenkomstige bindvariabelen moeten zijn voorzien voor een passende conversie:

Voorbeeld:

$stmt = $mysqli->prepare("INSERT INTO table (column1, column2) VALUES (?,?)");
$stmt->bind_param("is", $integer, $string);
$stmt->execute();

Het maakt niet uit of je voorbereide instructies of mysqli_real_escape_stringgebruikt, je moet altijd weten met welk type invoergegevens je werkt.

Dus als u een voorbereide instructie gebruikt, moet u de typen variabelen voor de functie mysqli_stmt_bind_paramspecificeren.

en het gebruik van mysqli_real_escape_stringis voor, zoals de naam zegt, die speciale tekens in een tekenreeks ontsnappen, dus het maakt geen gehele getallen veilig. Het doel van deze functie is om het breken van de snaren in SQL-instructies te voorkomen, en de schade aan de database die het kan veroorzaken. mysqli_real_escape_stringis een handige functie wanneer correct wordt gebruikt, vooral in combinatie met sprintf.

Voorbeeld:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';
$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer);
echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5
$integer = '99999999999999999999';
$query = sprintf("SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer);
echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647

19, Autoriteit 2%

Het eenvoudige alternatief voor dit probleem kan worden opgelost door passende machtigingen in de database zelf te verlenen.
Bijvoorbeeld: als u een MySQL-database gebruikt, voert u de database in via Terminal of de verstrekte UI en volgt u deze opdracht:

GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

Hierdoor wordt de gebruiker beperkt om alleen beperkt te raken met alleen de opgegeven query. Verwijder de toestemming van de delete en dus zouden de gegevens nooit worden verwijderd van de query die is afgevuurd van de PHP-pagina.
Het tweede dat u moet doen, is de privileges wissen, zodat de MySQL de machtigingen en updates ververst.

FLUSH PRIVILEGES; 

meer informatie over spoelen.

Om de huidige privileges voor de gebruiker te zien, activeer de volgende vraag.

select * from mysql.user where User='username';

Meer informatie over GRANT.


Antwoord 20, autoriteit 2%

Met betrekking tot veel nuttige antwoorden, hoop ik wat waarde toe te voegen aan deze thread.

SQL-injectie is een aanval die kan worden uitgevoerd via gebruikersinvoer (invoer die door een gebruiker wordt ingevuld en vervolgens in query’s wordt gebruikt). De SQL-injectiepatronen zijn correcte querysyntaxis, terwijl we het kunnen noemen: slechte zoekopdrachten om slechte redenen, en we nemen aan dat er een slecht persoon is die probeert geheime informatie te krijgen (toegangscontrole omzeilen) die de drie beveiligingsprincipes (vertrouwelijkheid , integriteit en beschikbaarheid).

Ons punt is nu om beveiligingsbedreigingen zoals SQL-injectie-aanvallen te voorkomen, de vraag (hoe een SQL-injectie-aanval met PHP te voorkomen), realistischer te zijn, gegevensfiltering of het wissen van invoergegevens is het geval bij het gebruik van gebruikers- invoer van gegevens in een dergelijke query, het gebruik van PHP of een andere programmeertaal is niet het geval, of zoals aanbevolen door meer mensen om moderne technologie te gebruiken, zoals een voorbereide verklaring of andere tools die momenteel SQL-injectiepreventie ondersteunen, bedenk dan dat deze tools niet meer beschikbaar zijn ? Hoe beveilig je je applicatie?

Mijn aanpak tegen SQL-injectie is: gegevens die door de gebruiker zijn ingevoerd wissen voordat ze naar de database worden verzonden (voordat ze in een query worden gebruikt).

Gegevensfiltering voor (onveilige gegevens converteren naar veilige gegevens)

Bedenk dat BOBen MySQLizijn niet beschikbaar. Hoe kunt u uw aanvraag beveiligen? Dwing je me om ze te gebruiken? Hoe zit het met andere talen dan PHP? Ik geef er de voorkeur aan om algemene ideeën te geven, omdat het kan worden gebruikt voor bredere grenzen, niet alleen voor een specifieke taal.

  1. SQL-gebruiker (beperkt gebruikersrecht): de meest voorkomende SQL-bewerkingen zijn (SELECT, UPDATE, INSERT), waarom zou u dan het UPDATE-privilege geven aan een gebruiker die dit niet nodig heeft? Bijvoorbeeld, login en zoekpagina’sgebruiken alleen SELECT, waarom zou u dan DB-gebruikers op deze pagina’s met hoge privileges gebruiken?

REGEL: maak niet één databasegebruiker aan voor alle privileges. Voor alle SQL-bewerkingen kunt u uw schema maken zoals (deluser, selectuser, updateuser) als gebruikersnamen voor eenvoudig gebruik.

Zie principe van de minste privileges.

  1. Gegevensfiltering: voordat gebruikersinvoer voor query’s wordt gemaakt, moet deze worden gevalideerd en gefilterd. Voor programmeurs is het belangrijk om enkele eigenschappen te definiëren voor elke gebruikersinvoervariabele:
    gegevenstype, gegevenspatroon en gegevenslengte. Een veld dat een getal is tussen (x en y) moet exact worden gevalideerd met behulp van de exacte regel, en voor een veld dat een string (tekst) is: patroon is bijvoorbeeld het geval, een gebruikersnaam mag slechts enkele tekens bevatten, laten we zeg [a-zA-Z0-9_-.]. De lengte varieert tussen (x en n) waarbij x en n (gehele getallen, x <=n).
    Regel: het maken van exacte filters en validatieregels zijn voor mij best practices.

  2. Gebruik andere tools: hier ben ik het ook met u eens dat een voorbereide verklaring (geparametriseerde query) en opgeslagen procedures. De nadelen hiervan zijn dat deze manieren geavanceerde vaardigheden vereisen die voor de meeste gebruikers niet bestaan. Het basisidee hier is om onderscheid te maken tussen de SQL-query en de gegevens die erin worden gebruikt. Beide benaderingen kunnen zelfs worden gebruikt met onveilige gegevens, omdat de gegevens die de gebruiker hier invoert niets toevoegen aan de oorspronkelijke zoekopdracht, zoals (any of x=x).

Lees voor meer informatie de OWASP SQL-injectiepreventie Cheatsheet.

Als je een gevorderde gebruiker bent, begin dan deze verdediging te gebruiken zoals je wilt, maar voor beginners, als ze niet snel een opgeslagen procedure kunnen implementeren en de verklaring kunnen voorbereiden, is het beter om invoergegevens zoveel mogelijk te filteren .

Laten we er ten slotte rekening mee houden dat een gebruiker deze tekst hieronder verzendt in plaats van zijn/haar gebruikersnaam in te voeren:

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

Deze invoer kan vroeg worden gecontroleerd zonder enige voorbereide verklaring en opgeslagen procedures, maar voor de zekerheid begint het gebruik ervan na filtering en validatie van gebruikersgegevens.

Het laatste punt is het detecteren van onverwacht gedrag dat meer inspanning en complexiteit vereist; het wordt niet aanbevolen voor normale webapplicaties.

Onverwacht gedrag in de bovenstaande gebruikersinvoer is SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA en root. Zodra deze woorden zijn gedetecteerd, kunt u de invoer vermijden.

UPDATE 1:

Een gebruiker heeft gereageerd dat dit bericht nutteloos is, OK! Hier is wat oasp.org verstrekt :

Primaire verdediging:

Optie # 1: Gebruik van bereide uitspraken (geparametriseerde query’s)
Optie # 2: Gebruik van opgeslagen procedures
Optie # 3: Ontsnappen aan alle ingevoerde ingevoerde gebruiker

Aanvullende verdediging:

Ook handhaven: minst privilege
Voer ook uit: White List invoervalidatie

Zoals u misschien weet, moet het beweren van een artikel worden ondersteund door een geldig argument, tenminste door één referentie! Anders wordt het beschouwd als een aanval en een slechte claim!

Update 2:

Vanaf de PHP-handleiding, PHP: voorbereide verklaringen – handleiding :

Ontsnappen en SQL Injectie

Gebonden variabelen worden automatisch door de server ontsnapt. De
Server voegt hun ontsnapte waarden op op de juiste plaatsen in de
Verklaring Sjabloon vóór uitvoering. Een hint moet aan de
Server voor het type gebonden variabele, om een ​​passend te maken
conversie. Zie de functie MySQLI_STMT_BIND_PARAM () voor meer
informatie.

De automatische ontsnapping van waarden binnen de server is soms
beschouwd als een beveiligingsfunctie om SQL-injectie te voorkomen. Hetzelfde
Graad van beveiliging kan worden bereikt met niet-bereide uitspraken als
Invoerwaarden worden correct ontsnapt.

Update 3:

Ik heb testcases gemaakt voor het kennen van hoe PDO en MySQLI de query naar de MySQL-server verzenden bij gebruik van een voorbereide verklaring:

PDO:

$user = "''1''"; // Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));

query log:

   189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

mysqli:

$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();

query log:

   188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

Het is duidelijk dat een voorbereide verklaring ook aan de gegevens ontsnapt, niets anders.

Zoals ook vermeld in de bovenstaande verklaring,

De automatische ontsnapping van waarden binnen de server wordt soms beschouwd als een beveiligingsfunctie om SQL-injectie te voorkomen. In dezelfde mate van beveiliging kan worden bereikt met niet-voorbereide verklaringen, als de invoerwaarden correct worden ontsnapt

Daarom bewijst dit dat gegevensvalidatie zoals intval()een goed idee is voor gehele waarden voordat u een query verzendt. Bovendien is het voorkomen van schadelijke gebruikersgegevens voordat u de query verzendt een correcte en geldige aanpak .

Bekijk deze vraag voor meer informatie: PDO stuurt onbewerkte query naar MySQL terwijl Mysqli voorbereide query verzendt, beide produceren hetzelfde resultaat

Referenties:

  1. Cheatsheet voor SQL-injectie
  2. SQL-injectie
  3. Informatiebeveiliging
  4. Beveiligingsprincipes
  5. Gegevensvalidatie

Antwoord 21, autoriteit 2%

Beveiligingswaarschuwing: dit antwoord is niet in overeenstemming met best practices op het gebied van beveiliging. Ontsnappen is onvoldoende om te voorkomen SQL-injectie, gebruik in plaats daarvan voorbereide instructies. Gebruik de onderstaande strategie op eigen risico. (Ook mysql_real_escape_string()is verwijderd in PHP 7.)

Verouderde waarschuwing: de mysql-extensie is op dit moment verouderd. we raden aan om de BOB-extensie

te gebruiken

Ik gebruik drie verschillende manieren om te voorkomen dat mijn webapplicatie kwetsbaar wordt voor SQL-injectie.

  1. Gebruik van mysql_real_escape_string(), een vooraf gedefinieerde functie in PHP , en deze code voegt backslashes toe aan de volgende tekens: \x00, \n, \r, \, ', "en \x1a. Geef de invoerwaarden door als parameters om de kans op SQL-injectie te minimaliseren.
  2. De meest geavanceerde manier is om PDO’s te gebruiken.

Ik hoop dat dit je zal helpen.

Beschouw de volgende vraag:

$iId = mysql_real_escape_string("1 OR 1=1");
$sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string() zal hier niet beschermen. Als u enkele aanhalingstekens (‘ ‘) rond uw variabelen in uw query gebruikt, is dat wat u hiertegen beschermt. Hier is een oplossing hieronder voor:

$iId = (int) mysql_real_escape_string("1 OR 1=1");
$sSql = "SELECT * FROM table WHERE id = $iId";

Deze vraagbevat enkele goede antwoorden over dit.

Ik stel voor dat het gebruik van PDO de beste optie is.

Bewerken:

mysql_real_escape_string()is verouderd vanaf PHP 5.5.0. Gebruik mysqli of PDO.

Een alternatief voor mysql_real_escape_string() is

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

Voorbeeld:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

Antwoord 22, autoriteit 2%

Een eenvoudige manier zou zijn om een ​​PHP-framework te gebruiken zoals CodeNigniter of Laravel met ingebouwde functies zoals filtering en actief-record, zodat u zich geen zorgen hoeft te maken over deze nuances.


23

Een goed idee is om een object-relationele mapperte gebruiken zoals Idiorm:

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();
$user->first_name = 'Jamie';
$user->save();
$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();
foreach ($tweets as $tweet) {
    echo $tweet->text;
}

Het bespaart u niet alleen SQL-injecties, maar ook syntaxisfouten! Het ondersteunt ook verzamelingen modellen met methodeketens om acties te filteren of toe te passen op meerdere resultaten tegelijk en meerdere verbindingen.


Antwoord 24

Er zijn zoveel antwoorden voor PHP en MySQL, maar hier is code voor PHP en Oracleom SQL-injectie en regelmatig gebruik van oci8-stuurprogramma’s te voorkomen:

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);

Antwoord 25

Verouderde waarschuwing:
De voorbeeldcode van dit antwoord (zoals de voorbeeldcode van de vraag) gebruikt de PHP-extensie MySQL, die in PHP 5.5.0 is verouderd en in PHP 7.0.0 volledig is verwijderd.

Beveiligingswaarschuwing: dit antwoord is niet in overeenstemming met best practices op het gebied van beveiliging. Ontsnappen is onvoldoende om SQL-injectievoorkomen, gebruik in plaats daarvan voorbereide instructies. Gebruik de onderstaande strategie op eigen risico. (Ook mysql_real_escape_string()is verwijderd in PHP 7.)

Met behulp van BOBen MYSQLiis een goede gewoonte om SQL-injecties te voorkomen, maar als u echt met MySQL-functies en -query’s wilt werken, kunt u beter gebruik maken van

mysql_real_escape_string

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

Er zijn meer mogelijkheden om dit te voorkomen: zoals identificeren – als de invoer een string, getal, char of array is, zijn er zoveel ingebouwde functies om dit te detecteren. Het zou ook beter zijn om deze functies te gebruiken om invoergegevens te controleren.

is_string

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

is_numeric

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

En het is zoveel beter om die functies te gebruiken om invoergegevens te controleren met mysql_real_escape_string.


Antwoord 26

Ik heb deze kleine functie enkele jaren geleden geschreven:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}
function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

Hierdoor kunnen statements worden uitgevoerd in een one-liner C#-achtige String.Formaat zoals:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

Het ontsnapt gezien het type variabele. Als u tabel- en kolomnamen probeert te parametriseren, zou dit mislukken omdat het elke tekenreeks tussen aanhalingstekens plaatst, wat een ongeldige syntaxis is.

BEVEILIGINGSUPDATE: de vorige str_replace-versie stond injecties toe door {#} tokens toe te voegen aan gebruikersgegevens. Deze preg_replace_callback-versie veroorzaakt geen problemen als de vervanging deze tokens bevat.

Other episodes