SQL-injektion

Från Wiki.linux.se
Hoppa till navigering Hoppa till sök

SQL Injection

SQL-injektion är en teknik där en angripare utnyttjar brister i applikationskoden som ansvarar för att bygga dynamiska SQL-frågor. Angriparen kan få tillgång till privilegierade sektioner av applikationen, hämta all information från databasen, manipulera befintlig data eller till och med utföra farliga systemkommandon på databasvärden. Sårbarheten uppstår när utvecklare sammanfogar eller interpolerar godtycklig inmatning i sina SQL-satser.

Exempel 1: Delning av resultatuppsättningen i sidor ... och skapa superanvändare (PostgreSQL)

I följande exempel interpoleras användarinmatning direkt i SQL-frågan, vilket gör att angriparen kan få ett superanvändarkonto i databasen.

<?php

$offset = $_GET['offset']; // varning, ingen inmatningsvalidering!
$query  = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result = pg_query($conn, $query);

?>

Normala användare klickar på länkarna 'nästa', 'föregående' där $offset är kodad i URL:en. Skriptet förväntar sig att det inkommande $offset är ett nummer. Men vad händer om någon försöker bryta in genom att lägga till följande till URL:en:

0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
    select 'crack', usesysid, 't','t','crack'
    from pg_shadow where usename='postgres';
--

Om detta skulle inträffa, skulle skriptet ge angriparen superanvändartillgång. Observera att 0; används för att ange en giltig offset till den ursprungliga frågan och för att avsluta den.

Notera: Det är en vanlig teknik att tvinga SQL-parsern att ignorera resten av frågan som utvecklaren har skrivit med --, vilket är kommentartecknet i SQL.

En möjlig metod att få tillgång till lösenord är att kringgå dina sökresultatsidor. Det enda angriparen behöver göra är att se om det finns några inlämnade variabler som används i SQL-satser som inte hanteras korrekt. Dessa filter kan vanligtvis ställas in i ett föregående formulär för att anpassa WHERE-, ORDER BY-, LIMIT- och OFFSET-klausuler i SELECT-satser. Om din databas stöder UNION-konstruktionen kan angriparen försöka lägga till en hel fråga till den ursprungliga för att lista lösenord från en godtycklig tabell. Det rekommenderas starkt att endast lagra säkra hashvärden av lösenord istället för lösenorden själva.

Exempel 2: Lista artiklar ... och några lösenord (vilken databasserver som helst)

<?php

$query  = "SELECT id, name, inserted, size FROM products
           WHERE size = '$size'";
$result = odbc_exec($conn, $query);

?>

Den statiska delen av frågan kan kombineras med en annan SELECT-sats som avslöjar alla lösenord:

'
union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable;
--

UPDATE- och INSERT-satser är också sårbara för sådana attacker.

Exempel 3: Från att återställa ett lösenord ... till att få fler rättigheter (vilken databasserver som helst)

<?php
$query = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>

Om en illasinnad användare skickar in värdet ' or uid like'%admin% till $uid för att ändra administratörens lösenord, eller helt enkelt ställer in $pwd till hehehe', trusted=100, admin='yes för att få fler rättigheter, så kommer frågan att förvrängas:

<?php

// $uid: ' or uid like '%admin%
$query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%';";

// $pwd: hehehe', trusted=100, admin='yes
$query = "UPDATE usertable SET pwd='hehehe', trusted=100, admin='yes' WHERE
...;";

?>

Medan det förblir uppenbart att en angripare måste ha åtminstone viss kunskap om databasarkitekturen för att genomföra en lyckad attack, är det ofta mycket enkelt att få denna information. Till exempel kan koden vara en del av en öppen källkodsprogramvara och vara offentligt tillgänglig. Denna information kan också avslöjas av sluten källkod - även om den är kodad, obfuskad eller kompilerad - och till och med av din egen kod genom att visa felmeddelanden. Andra metoder inkluderar användning av typiska tabell- och kolumnnamn. Till exempel ett inloggningsformulär som använder en 'users'-tabell med kolumnnamn som 'id', 'username' och 'password'.

Exempel 4: Attackera databasvärdens operativsystem (MSSQL Server)

Ett skrämmande exempel på hur operativsystemkommandon kan nås på vissa databasvärdar.

<?php

$query  = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);

?>

Om en angripare skickar in värdet a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- till $prod, då kommer $query att bli:

<?php

$query  = "SELECT * FROM products
           WHERE id LIKE '%a%'
           exec master..xp_cmdshell 'net user test testpass /ADD' --%'";
$result = mssql_query($query);

?>

MSSQL Server utför SQL-satserna i satsen, inklusive ett kommando för att lägga till en ny användare till den lokala kontodatabasen. Om denna applikation kördes som sa och MSSQLSERVER-tjänsten kördes med tillräckliga rättigheter, skulle angriparen nu ha ett konto för att få åtkomst till den här maskinen.

Notera: Vissa exempel ovan är knutna till en specifik databasserver, men det betyder inte att en liknande attack är omöjlig mot andra produkter. Din databasserver kan vara lika sårbar på ett annat sätt.

Undvikandetekniker

Det rekommenderade sättet att undvika SQL-injektion är att binda all data via förberedda uttalanden. Att använda parameteriserade frågor räcker inte för att helt undvika SQL-injektion, men det är det enklaste och säkraste sättet att tillhandahålla indata till SQL-satser. Alla dynamiska datalitteraler i WHERE-, SET- och VALUES-klausuler måste ersättas med platshållare. Den faktiska datan kommer att bindas under exekveringen och skickas separat från SQL-kommandot.

Parameterbindning kan endast användas för data. Andra dynamiska delar av SQL-frågan måste filtreras mot en känd lista över tillåtna värden.

Exempel 5: Undvik SQL-injektion genom att använda PDO-förberedda uttalanden

<?php

// Den dynamiska SQL-delen valideras mot förväntade värden
$sortingOrder = $_GET['sortingOrder'] === 'DESC' ? 'DESC' : 'ASC';
$productId = $_GET['productId'];
// SQL förbereds med en platshållare
$stmt = $pdo->prepare("SELECT * FROM products WHERE id LIKE ? ORDER BY price {$sortingOrder}");
// Värdet tillhandahålls med LIKE-jokertecken
$stmt->execute(["%{$productId}%"]);

?>

Förberedda uttalanden tillhandahålls av PDO, av MySQLi och av andra databasbibliotek.

SQL-injektionsattacker är främst baserade på att utnyttja kod som inte skrivits med säkerhet i åtanke. Lita aldrig på någon inmatning, särskilt från klientsidan, även om det kommer från en valruta, ett dolt inmatningsfält eller en cookie. Det första exemplet visar att en så enkel fråga kan orsaka katastrofer.

En strategi för försvar-i-djup innebär flera goda kodningspraxis:

  • Anslut aldrig till databasen som superanvändare eller som databasägare. Använd alltid anpassade användare med minimala rättigheter.
  • Kontrollera om den givna inmatningen har förväntad datatyp. PHP har ett brett utbud av funktioner för inmatningsvalidering, från de enklaste som finns i Variabelfunktioner och i Teckentypfunktioner (t.ex. is_numeric(), ctype_digit() respektive) och vidare till Perl Compatible Regular Expressions support.
  • Om applikationen förväntar sig numerisk inmatning, överväg att verifiera data med ctype_digit(), tyst ändra dess typ med settype() eller använd dess numeriska representation med sprintf().
  • Om databasskiktet inte stöder bindning av variabler, citera varje icke-numeriskt användartillhandahållet värde som skickas till databasen med den databas-specifika strängflyktfunktionen (t.ex. mysql_real_escape_string(), sqlite_escape_string() osv.). Generiska funktioner som addslashes() är användbara endast i en mycket specifik miljö (t.ex. MySQL i enbyte-teckenuppsättning med inaktiverad NO_BACKSLASH_ESCAPES), så det är bättre att undvika dem.
  • Skriv inte ut någon databas-specifik information, särskilt inte om schemat, på något sätt. Se även Felrapportering och Felhantering och loggningsfunktioner.

Förutom dessa fördelar du av att logga frågor antingen inom ditt skript eller av databasen själv, om den stöder loggning. Självklart kan inte loggning förhindra några skadliga försök, men det kan vara till hjälp för att spåra tillbaka vilken applikation som har kringgåtts. Loggen är inte användbar i sig, men genom den information den innehåller. Mer detaljer är i allmänhet bättre än mindre.

Sidslut

Orginalhemsidan på Engelska :https://www.php.net/manual/en/security.database.sql-injection.php
PHP
Säkerhet
Databassäkerhet


Det här är en maskinöversättning av PHP-manualen till svenska. Om du hittar fel är vi tacksamma om du rapporterar dem via formuläret som finns på https://www.linux.se/kontaka-linux-se/

Tack till Datorhjälp som har sponsrat Linux.se med webserver.