SQL Injection è una falla di sicurezza nelle applicazioni web in cui gli aggressori inseriscono codice SQL dannoso tramite l'input dell'utente. Ciò può consentire loro di accedere ai contenuti del database di modifica dei dati sensibili o persino di assumere il controllo del sistema. È importante conoscere SQL Injection per mantenere sicure le applicazioni Web.
SQL Injection (SQLi) è una vulnerabilità di sicurezza che si verifica quando un utente malintenzionato può manipolare le query del database di un'applicazione Web inserendo codice SQL dannoso nei campi di input dell'utente. Queste query inserite possono manipolare il database sottostante per recuperare, modificare o eliminare dati sensibili. In alcuni casi gli aggressori possono addirittura aumentare i privilegi ottenendo il pieno controllo sul database o sul server.

Esempio nel mondo reale:
Nel 2019 si è verificato il Capital One Data Breach a causa di un'applicazione web configurata in modo errato che ha consentito a un utente malintenzionato di sfruttare una vulnerabilità SQL injection. Ciò ha comportato la fuga di dati personali di oltre 100 milioni di clienti, inclusi nomi, indirizzi e punteggi di credito.
Livello di sicurezza dell'iniezione SQL
DVWA fornisce quattro livelli di sicurezza per SQL Injection per aiutare gli studenti a vedere come le diverse protezioni influenzano gli attacchi:
1. Bassa sicurezza
L'app prende il tuo input e lo inserisce direttamente nella query SQL senza filtri.
$id = $_GET['id'];$query = 'SELECT first_name last_name FROM users WHERE user_id = '$id';';- Entrando
':Interrompe la query e fa sì che il database generi un errore rivelando che è vulnerabile. - Entrando
1' OR '1'='1:Fa sì che la query sia sempre vera in modo che vengano restituiti tutti gli utenti. - Entrando
1' UNION SELECT user password FROM users--:Si unisce a un'altra query per recuperare dati nascosti come nomi utente e password.
2. Sicurezza media
L'app applica la sanificazione di base degli input utilizzando funzioni comeaddslashes()scappare'.
$id = addslashes($_GET['id']);$query = 'SELECT first_name last_name FROM users WHERE user_id = '$id';';Come può essere l'attacco:
Un semplice'injection non funzionerà più (perché diventa').
Ma gli aggressori possono comunque aggirare l’utilizzo dell’iniezione numerica (poiché i numeri non hanno bisogno di virgolette).
Esempio:
riavviare mysql ubuntu
1 OR 1=1Ciò restituisce ancora tutti i record.
3. Alta sicurezza
L'app utilizza istruzioni preparate (query parametrizzate) per gestire in modo sicuro l'input dell'utente.
$stmt = $pdo->prepare('SELECT first_name last_name FROM users WHERE user_id = ?');$stmt->execute([$id]);Attacco:
Tentativi come' OR 1=1OUNION SELECTnon lavorare più.
La query tratta tutti gli input come dati e non come codice SQL.
Tipi di SQL Injection
Esistono diversi tipi di SQL Injection
L'iniezione SQL basata su errori è un tipo di iniezione SQL in banda in cui un utente malintenzionato fa sì che il database generi intenzionalmente un messaggio di errore. L'aggressore analizza quindi questo messaggio di errore per ottenere informazioni preziose sulla struttura del database come nomi di tabelle e nomi di colonne che possono essere utilizzate per elaborare ulteriori attacchi più precisi.
Come funziona
Questo attacco prende di mira le applicazioni che rivelano errori grezzi del database invece di mostrare messaggi generici. Inserendo input dannosi che interrompono la sintassi SQL, gli aggressori innescano questi errori e ottengono preziosi indizi sulla struttura del database.
strutture dati java
- Identificare un input vulnerabile: L'aggressore trova un campo di input come una barra di ricerca o un parametro URL che interagisce direttamente con il database senza un'adeguata sanificazione dell'input.
- Iniettare un payload dannoso: L'attaccante inserisce un carattere speciale (come una virgoletta singola
') o una funzione nota per causare un errore del database. - Analizza l'errore: Il database non è in grado di elaborare la query in formato errato restituisce un messaggio di errore dettagliato. Questo messaggio può rivelare informazioni cruciali come:
- Il sistema di database (ad esempio MySQL Oracle SQL Server).
- La versione del database.
- La query SQL completa in esecuzione.
- Errori di sintassi specifici che possono essere utilizzati per comprendere i nomi di tabelle o colonne.
- Perfeziona l'attacco: Utilizzando le informazioni raccolte dal messaggio di errore, l'aggressore può perfezionare il proprio payload per estrarre più dati come nomi utente e password.
Esempio:
Passaggio 1: configura il tuo ambiente
- Avvia DVWA. In genere è possibile accedervi accedendo a un URL simile
http://localhost/dvwanel tuo browser.
- Accedi a DVWA con le credenziali predefinite:
admin/password.
- Vai alla scheda Sicurezza DVWA e imposta il livello di sicurezza su basso. Ciò garantirà che le vulnerabilità siano facili da sfruttare.
Passaggio 2: identificare la vulnerabilità
La pagina SQL Injection ha una semplice casella di input in cui è possibile inserire un ID utente. La query di backend è probabilmente qualcosa del genereSELECT * FROM users WHERE id = 'user_input'
- Inserisci un ID valido come
1nella casella di input e fare clic su "Invia". Dovresti vedere i dettagli dell'utente con ID 1.
Origine dell'iniezione SQL
PHP $id = $_REQUEST[ 'id' ]; switch ($_DVWA['SQLI_DB']) { case MYSQL: // Check database $query = 'SELECT first_name last_name FROM users WHERE user_id = '$id';'; $result = mysqli_query($GLOBALS['___mysqli_ston'] $query ) or die( ''
. ((is_object($GLOBALS['___mysqli_ston'])) ? mysqli_error($GLOBALS['___mysqli_ston']) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row['first_name']; $last = $row['last_name']; // Feedback for end user echo 'ID:
{$id}
First name: {$first}
Surname: {$last}'; } mysqli_close($GLOBALS['___mysqli_ston']); break; case SQLITE: global $sqlite_db_connection; #$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']); #$sqlite_db_connection->enableExceptions(true); $query = 'SELECT first_name last_name FROM users WHERE user_id = '$id';'; #print $query; try { $results = $sqlite_db_connection->query($query); } catch (Exception $e) { echo 'Caught exception: ' . $e->getMessage(); exit(); } if ($results) { while ($row = $results->fetchArray()) { // Get values $first = $row['first_name']; $last = $row['last_name']; // Feedback for end user echo 'ID:
{$id}
First name: {$first}
Surname: {$last}'; } } else { echo 'Error in fetch '.$sqlite_db->lastErrorMsg(); } break; } } ode ?> - Ora prova a interrompere la query. Inserisci una virgoletta singola
'nella casella di input e inviare.
La query diventa:
SELECT * FROM users WHERE id = ''';Qui il database vede una quotazione extra e non sa come completare la query.
sql seleziona da più tabelle
Invece di mostrarti i dettagli dell'utente, l'applicazione restituirà un errore SQL (qualcosa come "Hai un errore nella sintassi SQL...")
Questo si chiama SQL Injection basato sugli errori perché:
- L'aggressore invia input non validi (
') - Il database genera un errore
- Questo errore fa trapelare informazioni utili sul database (come il tipo di DB, il numero di colonne, la struttura, ecc.)
2. Iniezione SQL basata sull'unione
L'SQL Injection basato sull'unione è una tecnica in cui gli aggressori utilizzano il fileUNIONoperatore per combinare i risultati di due o piùSELECTistruzioni in un unico set di risultati. Ciò può consentire loro di estrarre informazioni da altre tabelle nel database. ILUNIONl'operatore può essere utilizzato solo se:
- Entrambe le query hanno lo stesso numero di colonne
- Le colonne hanno tipi di dati simili
- Le colonne sono nello stesso ordine
Operatore UNIONE : ILUNIONL'operatore viene utilizzato per combinare il set di risultati di due o piùSELECTdichiarazioni.
- Ogni
SELECTdichiarazione all'internoUNIONdevono avere lo stesso numero di colonne - Le colonne devono avere tipi di dati simili
- Le colonne devono essere nello stesso ordine
SELECT column_name(s) FROM table1UNIONSELECT column_name(s) FROM table2Esempio:
Passaggio 1: Per prima cosa dobbiamo trovare il numero di colonne della tabella esistente nel sito web per iniettare SQL Injection basata su UNION:
La pagina SQL Injection ha una semplice casella di input in cui è possibile inserire un ID utente. La query di backend è probabilmente qualcosa del genere
SELECT * FROM users WHERE id = 'user_input'Ora prova a interrompere la query. Inserisci una virgoletta singola'nella casella di input e inviare.
Se l'applicazione è vulnerabile riceverai un messaggio di errore dettagliato. Potrebbe assomigliare a qualcosa del tipo:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1
Passaggio 2: Usa ilUNIONParola chiave per scoprire il numero di colonne
Per utilizzare ilUNIONparola chiave (un passaggio successivo comune) è necessario conoscere il numero di colonne nella query originale. Puoi scoprirlo utilizzando il fileORDER BYclausola
'abc' è in numeri'
- Prova a ordinare i risultati per colonna
1:1 ORDER BY 1.
- Invia. Dovrebbe funzionare.
Origine dell'iniezione SQL
PHP if( isset( $_REQUEST[ 'Submit' ] ) ) { // Get input $id = $_REQUEST[ 'id' ]; switch ($_DVWA['SQLI_DB']) { case MYSQL: // Check database $query = 'SELECT first_name last_name FROM users WHERE user_id = '$id';'; $result = mysqli_query($GLOBALS['___mysqli_ston'] $query ) or die( ''
. ((is_object($GLOBALS['___mysqli_ston'])) ? mysqli_error($GLOBALS['___mysqli_ston']) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '' ); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row['first_name']; $last = $row['last_name']; // Feedback for end user echo 'ID:
{$id}
First name: {$first}
Surname: {$last}'; } mysqli_close($GLOBALS['___mysqli_ston']); break; case SQLITE: global $sqlite_db_connection; #$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']); #$sqlite_db_connection->enableExceptions(true); $query = 'SELECT first_name last_name FROM users WHERE user_id = '$id';'; #print $query; try { $results = $sqlite_db_connection->query($query); } catch (Exception $e) { echo 'Caught exception: ' . $e->getMessage(); exit(); } if ($results) { while ($row = $results->fetchArray()) { // Get values $first = $row['first_name']; $last = $row['last_name']; // Feedback for end user echo 'ID:
{$id}
First name: {$first}
Surname: {$last}'; } } else { echo 'Error in fetch '.$sqlite_db->lastErrorMsg(); } break; } } ?> - Incrementa il numero:
1 ORDER BY 2. Invia. Dovrebbe funzionare.
- Continua ad incrementare finché non ricevi un errore. Per esempio
1 ORDER BY 4potrebbe darti:Unknown column '4' in 'order clause' - Ciò significa che la query ha 3 colonne.
3. Iniezione SQL basata su cieco
Iniezione SQL cieca si verifica quando gli aggressori non riescono a vedere i risultati della query direttamente sulla pagina web. Invece deducono informazioni da sottili cambiamenti nel comportamento dell’applicazione o nei tempi di risposta. Sebbene sia più lento e noioso del classico SQLi, può essere ugualmente efficace.
Invece di recuperare i dati, l'aggressore deduce informazioni osservando il comportamento della pagina web. Questa operazione viene generalmente eseguita in due modi:
- SQLi cieco basato su booleano: L'aggressore inserisce una query SQL che restituisce a VERO O falso risultato. La risposta dell'applicazione Web cambia a seconda che la query sia vera o falsa. Ad esempio, la pagina potrebbe mostrare un messaggio diverso o visualizzare un layout diverso.
- SQLi cieco basato sul tempo: L'aggressore inserisce una query SQL che fa sì che il database esegua un'azione che richiede molto tempo (come un
SLEEP()funzione) se una condizione è soddisfatta. L'aggressore osserva il tempo necessario al caricamento della pagina per determinare se la condizione inserita era vera o falsa.
Esempio:
Immagina una pagina di accesso in cui inserisci un nome utente e una password. L'applicazione costruisce una query SQL come questa:
SELECT * FROM users WHERE username = 'user_input' AND password = 'password_input'Una blind SQL injection implicherebbe la manipolazione del fileuser_inputcampo per porre una domanda al database.
Invece di ottenere una risposta diretta, l’aggressore potrebbe provare qualcosa del genere:
user_input = 'admin' AND 1=1; --Se la pagina viene caricata normalmente, l'aggressore lo sa1=1è un VERO dichiarazione.
vikas divyakirti
user_input = 'admin' AND 1=2; --Se la pagina mostra un errore o si comporta diversamente, l'aggressore lo sa1=2è un falso dichiarazione.
Utilizzando una serie di queste domande vero/falso, un utente malintenzionato può indovinare ed estrarre sistematicamente informazioni un carattere alla volta. Il processo può essere automatizzato per indovinare qualsiasi cosa, dai nomi delle tabelle alle password degli utenti.
Impatto degli attacchi SQL Injection
- Accesso non autorizzato a dati sensibili : gli aggressori possono recuperare informazioni finanziarie personali o riservate archiviate nel database.
- Problemi di integrità dei dati : gli aggressori possono modificare, eliminare o corrompere dati critici che influiscono sulla funzionalità dell'applicazione.
- Escalation dei privilegi : gli aggressori possono aggirare i meccanismi di autenticazione e ottenere privilegi amministrativi.
- Tempi di inattività del servizio : L'iniezione SQL può sovraccaricare il server causando un degrado delle prestazioni o arresti anomali del sistema.
- Danni alla reputazione : Un attacco riuscito può danneggiare gravemente la reputazione di un'organizzazione portando alla perdita della fiducia dei clienti.
Prevenire gli attacchi SQL Injection
Esistono diverse best practice per prevenire gli attacchi SQL injection:
1. Utilizzare istruzioni preparate e query con parametri
Le istruzioni preparate e le query parametrizzate garantiscono che gli input dell'utente vengano trattati come dati anziché come parte della query SQL. Questo approccio elimina il rischio di SQL injection.
Esempio in PHP (usando MySQLi):
$stmt = $conn->prepare('SELECT * FROM users WHERE username = ? AND password = ?'); $stmt->bind_param('ss' $username $password); $stmt->execute();2. Utilizzare procedure archiviate
Le procedure memorizzate sono query SQL predefinite archiviate nel database. Queste procedure possono aiutare a prevenire l'SQL injection perché non costruiscono dinamicamente query SQL.
Esempio:
CREATE PROCEDURE GetUserByUsername (IN username VARCHAR(50)) BEGIN SELECT * FROM users WHERE username = username; END;3. Convalida dell'input della lista bianca
Assicurarsi che gli input dell'utente siano convalidati prima di essere utilizzati nelle query SQL. Consenti solo determinati caratteri e modelli come l'immissione alfanumerica per campi come nomi utente o indirizzi email.
4. Utilizzare i framework ORM
Framework Object-Relational Mapping (ORM) come Ibernazione O Struttura delle entità può aiutare a prevenire l'iniezione SQL gestendo automaticamente la generazione di query impedendo la costruzione di query dinamiche.
5. Limitare i privilegi del database
Concedere agli utenti le autorizzazioni minime richieste per il database. Assicurati che le applicazioni possano eseguire solo le azioni necessarie (ad esempio SELECT INSERT) e limitare le autorizzazioni come DROP TABLE o ALTER.
6. Gestione degli errori
Configurare il database e l'applicazione per non visualizzare messaggi di errore dettagliati all'utente. Registra invece gli errori internamente e visualizza messaggi di errore generici agli utenti finali.