Wat is SQL-injectie en hoe te voorkomen in PHP-applicaties?

Dus u denkt dat uw SQL-database goed presteert en veilig is voor onmiddellijke vernietiging? Nou, SQL Injection is het daar niet mee eens!

Ja, het is onmiddellijke vernietiging waar we het over hebben, omdat ik dit artikel niet wil openen met de gebruikelijke flauwe terminologie van “aanscherping van de beveiliging” en “het voorkomen van kwaadwillige toegang”. SQL-injectie is zo’n oude truc in het boek dat iedereen, elke ontwikkelaar, er heel goed van op de hoogte is en goed weet hoe het te voorkomen. Behalve die ene vreemde keer dat ze uitglijden, en de resultaten kunnen ronduit rampzalig zijn.

Als u al weet wat SQL-injectie is, kunt u naar de tweede helft van het artikel gaan. Maar voor degenen die net opkomen op het gebied van webontwikkeling en ervan dromen om hogere functies op zich te nemen, is enige introductie op zijn plaats.

Wat is SQL-injectie?

De sleutel tot het begrijpen van SQL Injection zit in de naam: SQL + Injection. Het woord ‘injectie’ heeft hier geen medische connotaties, maar is eerder het gebruik van het werkwoord ‘injecteren’. Samen brengen deze twee woorden het idee over om SQL in een webtoepassing te plaatsen.

SQL in een webapplicatie plaatsen. . . hmmm. . . Is dat niet wat we sowieso doen? Ja, maar we willen niet dat een aanvaller onze database aanstuurt. Laten we dat begrijpen met behulp van een voorbeeld.

Laten we zeggen dat u een typische PHP-website bouwt voor een lokale e-commerce winkel, dus u besluit een contactformulier als dit toe te voegen:

<form action="record_message.php" method="POST">
  <label>Your name</label>
  <input type="text" name="name">
  
  <label>Your message</label>
  <textarea name="message" rows="5"></textarea>
  
  <input type="submit" value="Send">
</form>

En laten we aannemen dat het bestand send_message.php alles in een database opslaat, zodat de eigenaren van de winkel later gebruikersberichten kunnen lezen. Het kan een code als deze hebben:

<?php

$name = $_POST['name'];
$message = $_POST['message'];

// check if this user already has a message
mysqli_query($conn, "SELECT * from messages where name = $name");

// Other code here

Je probeert dus eerst te kijken of deze gebruiker al een ongelezen bericht heeft. De query SELECT * from messages where name = $name lijkt eenvoudig genoeg, toch?

MIS!

In onze onschuld hebben we de deuren geopend voor de onmiddellijke vernietiging van onze database. Om dit te laten gebeuren, moet de aanvaller aan de volgende voorwaarden voldoen:

  • De applicatie draait op een SQL-database (tegenwoordig is bijna elke applicatie dat)
  • De huidige databaseverbinding heeft machtigingen voor “bewerken” en “verwijderen” voor de database
  • De namen van de belangrijke tabellen zijn te raden
  Fix Google Foto's Kan wijzigingen niet opslaan

Het derde punt betekent dat nu de aanvaller weet dat je een e-commerce winkel runt, je de bestelgegevens hoogstwaarschijnlijk opslaat in een besteltabel. Gewapend met dit alles hoeft de aanvaller dit alleen als naam op te geven:

Joe; afkappen bestellingen;? Ja meneer! Laten we eens kijken wat de query zal worden wanneer deze wordt uitgevoerd door het PHP-script:

SELECTEER * UIT berichten WHERE naam = Joe; afkappen bestellingen;

Oké, het eerste deel van de query bevat een syntaxisfout (geen aanhalingstekens rond “Joe”), maar de puntkomma dwingt de MySQL-engine om een ​​nieuwe te interpreteren: opdrachten afkappen. Zo is in één klap de hele bestelgeschiedenis weg!

Nu u weet hoe SQL-injectie werkt, is het tijd om te kijken hoe u dit kunt stoppen. De twee voorwaarden waaraan moet worden voldaan voor een succesvolle SQL-injectie zijn:

  • Het PHP-script moet machtigingen voor het wijzigen/verwijderen van de database hebben. Ik denk dat dit geldt voor alle applicaties en dat je je applicaties niet alleen-lezen kunt maken. 🙂 En raad eens, zelfs als we alle wijzigingsprivileges verwijderen, kan SQL-injectie nog steeds iemand in staat stellen SELECT-query’s uit te voeren en de hele database te bekijken, inclusief gevoelige gegevens. Met andere woorden, het verlagen van het databasetoegangsniveau werkt niet, en uw toepassing heeft het toch nodig.
  • Gebruikersinvoer wordt verwerkt. De enige manier waarop SQL-injectie kan werken, is wanneer u gegevens van gebruikers accepteert. Nogmaals, het is niet praktisch om alle invoer voor uw toepassing te stoppen alleen maar omdat u zich zorgen maakt over SQL-injectie.
  • SQL-injectie in PHP voorkomen

    Nu, gezien het feit dat databaseverbindingen, query’s en gebruikersinvoer deel uitmaken van het leven, hoe kunnen we SQL-injectie voorkomen? Gelukkig is het vrij eenvoudig, en er zijn twee manieren om het te doen: 1) gebruikersinvoer opschonen en 2) voorbereide verklaringen gebruiken.

    Sanitize gebruikersinvoer

    Als je een oudere PHP-versie gebruikt (5.5 of lager, en dit gebeurt veel op shared hosting), is het verstandig om al je gebruikersinvoer via een functie met de naam mysql_real_escape_string() te laten lopen. Kortom, wat het doet, het verwijdert alle speciale tekens in een string, zodat ze hun betekenis verliezen wanneer ze door de database worden gebruikt.

    Als u bijvoorbeeld een tekenreeks hebt zoals Ik ben een tekenreeks, kan het enkele aanhalingsteken (‘) door een aanvaller worden gebruikt om de databasequery die wordt gemaakt te manipuleren en een SQL-injectie te veroorzaken. Als je het door mysql_real_escape_string() laat lopen, krijg je I’m een ​​string, die een backslash toevoegt aan het enkele aanhalingsteken, waardoor het ontsnapt. Als gevolg hiervan wordt de hele string nu als een onschadelijke string doorgegeven aan de database, in plaats van deel te kunnen nemen aan querymanipulatie.

      9 AWS SES geïntegreerde e-mailmarketingoplossing tegen lagere kosten

    Er is één nadeel aan deze aanpak: het is een heel, heel oude techniek die samengaat met de oudere vormen van databasetoegang in PHP. Vanaf PHP 7 bestaat deze functie niet eens meer, wat ons bij onze volgende oplossing brengt.

    Gebruik voorbereide verklaringen

    Voorbereide instructies zijn een manier om databasequery’s veiliger en betrouwbaarder te maken. Het idee is dat in plaats van de onbewerkte query naar de database te sturen, we de database eerst de structuur vertellen van de query die we zullen verzenden. Dit is wat we bedoelen met het ‘opmaken’ van een verklaring. Zodra een verklaring is opgesteld, geven we de informatie door als geparametriseerde invoer zodat de database “de hiaten kan vullen” door de invoer in te pluggen in de querystructuur die we eerder hebben verzonden. Dit neemt elke speciale kracht weg die de inputs zouden kunnen hebben, waardoor ze in het hele proces als louter variabelen (of payloads, als je wilt) worden behandeld. Zo zien voorbereide verklaringen eruit:

    <?php
    $servername = "localhost";
    $username = "username";
    $password = "password";
    $dbname = "myDB";
    
    // Create connection
    $conn = new mysqli($servername, $username, $password, $dbname);
    
    // Check connection
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    }
    
    // prepare and bind
    $stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
    $stmt->bind_param("sss", $firstname, $lastname, $email);
    
    // set parameters and execute
    $firstname = "John";
    $lastname = "Doe";
    $email = "[email protected]";
    $stmt->execute();
    
    $firstname = "Mary";
    $lastname = "Moe";
    $email = "[email protected]";
    $stmt->execute();
    
    $firstname = "Julie";
    $lastname = "Dooley";
    $email = "[email protected]";
    $stmt->execute();
    
    echo "New records created successfully";
    
    $stmt->close();
    $conn->close();
    ?>

    Ik weet dat het proces onnodig ingewikkeld klinkt als voorbereide verklaringen nieuw voor je zijn, maar het concept is de moeite meer dan waard. Hier is een leuke kennismaking ermee.

    Voor degenen die al bekend zijn met de PDO-extensie van PHP en deze gebruiken om voorbereide verklaringen te maken, heb ik een klein advies.

    Waarschuwing: Wees voorzichtig bij het instellen van BOB

    Wanneer we PDO gebruiken voor toegang tot databases, kunnen we in een vals gevoel van veiligheid worden gezogen. “Ah, nou, ik gebruik BOB. Nu hoef ik nergens anders meer aan te denken” — zo gaat ons denken over het algemeen. Het is waar dat PDO (of door MySQLi voorbereide statements) voldoende is om allerlei soorten SQL-injectieaanvallen te voorkomen, maar je moet voorzichtig zijn bij het opzetten ervan. Het is normaal om code uit zelfstudies of eerdere projecten te kopiëren en te plakken en verder te gaan, maar met deze instelling kan alles ongedaan worden gemaakt:

    $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

    Wat deze instelling doet, is PDO vertellen om voorbereide verklaringen te emuleren in plaats van de eigenschap voorbereide verklaringen van de database daadwerkelijk te gebruiken. Bijgevolg stuurt PHP eenvoudige queryreeksen naar de database, zelfs als het lijkt alsof uw code voorbereide instructies maakt en parameters instelt en zo. Met andere woorden, u bent net zo kwetsbaar voor SQL-injectie als voorheen. 🙂

      Hoe Google Play Games op iPhone te krijgen

    De oplossing is simpel: zorg ervoor dat deze emulatie op false staat.

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

    Nu wordt het PHP-script gedwongen om voorbereide instructies op databaseniveau te gebruiken, waardoor allerlei soorten SQL-injectie worden voorkomen.

    WAF voorkomen

    Wist u dat u webapplicaties ook kunt beschermen tegen SQL-injectie door gebruik te maken van WAF (webapplicatie-firewall)?

    Nou, niet alleen SQL-injectie, maar vele andere laag 7-kwetsbaarheden, zoals cross-site scripting, verbroken authenticatie, cross-site vervalsing, gegevensblootstelling, enz. U kunt zelfgehoste zoals Mod Security of cloudgebaseerd gebruiken als volgt.

    SQL-injectie en moderne PHP-frameworks

    De SQL-injectie is zo gewoon, zo gemakkelijk, zo frustrerend en zo gevaarlijk dat alle moderne PHP-webframeworks zijn ingebouwd met tegenmaatregelen. In WordPress hebben we bijvoorbeeld de functie $wpdb->prepare(), terwijl als je een MVC-framework gebruikt, deze al het vuile werk voor je doet en je niet eens hoeft na te denken over het voorkomen van SQL-injectie. Het is een beetje vervelend dat je in WordPress statements expliciet moet voorbereiden, maar hey, het is WordPress waar we het over hebben. 🙂

    Hoe dan ook, mijn punt is dat moderne webontwikkelaars niet hoeven na te denken over SQL-injectie, en als gevolg daarvan zijn ze zich niet eens bewust van de mogelijkheid. Als zodanig kunnen de resultaten catastrofaal zijn, zelfs als ze één achterdeur open laten in hun applicatie (misschien is het een $_GET-queryparameter en oude gewoonten om een ​​vuile query af te vuren). Het is dus altijd beter om de tijd te nemen om dieper in de fundamenten te duiken.

    Conclusie

    SQL-injectie is een erg vervelende aanval op een webapplicatie, maar is gemakkelijk te vermijden. Zoals we in dit artikel hebben gezien, is voorzichtigheid bij het verwerken van gebruikersinvoer (trouwens, SQL-injectie is niet de enige bedreiging die het omgaan met gebruikersinvoer met zich meebrengt) en het bevragen van de database alles wat er is. Dat gezegd hebbende, we werken niet altijd in de beveiliging van een webframework, dus het is beter om op de hoogte te zijn van dit type aanval en er niet in te trappen.

    gerelateerde berichten