security - simple - test sql injection query




Come funziona l'iniezione SQL dal fumetto XKCD "Bobby Tables"? (8)

Solo guardando:

(Fonte: https://xkcd.com/327/ )

Cosa fa questo SQL:

Robert'); DROP TABLE STUDENTS; --

Conosco entrambi ' e -- sono per i commenti, ma la parola DROP viene commentata anche perché fa parte della stessa linea?


TL; DR

-- The application accepts input, in this case 'Nancy', without attempting to
-- sanitize the input, such as by escaping special characters
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

-- SQL injection occurs when input into a database command is manipulated to
-- cause the database server to execute arbitrary SQL
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

-- The student records are now gone - it could have been even worse!
school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

Questo elimina (elimina) la tabella degli studenti.

( Tutti gli esempi di codice in questa risposta sono stati eseguiti su un server di database PostgreSQL 9.1.2. )

Per chiarire cosa sta succedendo, proviamo con una tabella semplice che contiene solo il campo del nome e aggiungi una singola riga:

school=> CREATE TABLE students (name TEXT PRIMARY KEY);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "students_pkey" for table "students"
CREATE TABLE
school=> INSERT INTO students VALUES ('John');
INSERT 0 1

Supponiamo che l'applicazione utilizzi il seguente SQL per inserire i dati nella tabella:

INSERT INTO students VALUES ('foobar');

Sostituisci il foobar con il nome effettivo dello studente. Un'operazione di inserimento normale sarebbe simile a questa:

--                            Input:   Nancy
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

Quando interrogiamo la tabella, otteniamo questo:

school=> SELECT * FROM students;
 name
-------
 John
 Nancy
(2 rows)

Cosa succede quando inseriamo il nome di Little Bobby Tables nel tavolo?

--                            Input:   Robert'); DROP TABLE students; --
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

L'iniezione SQL qui è il risultato del nome dello studente che termina la dichiarazione e include un comando DROP TABLE separato; i due trattini alla fine dell'input intendono commentare qualsiasi codice rimanente che altrimenti causerebbe un errore. L'ultima riga dell'output conferma che il server del database ha abbandonato la tabella.

È importante notare che durante l'operazione INSERT l'applicazione non sta controllando l'input per alcun carattere speciale e pertanto consente l'inserimento di un input arbitrario nel comando SQL. Ciò significa che un utente malintenzionato può inserire, in un campo normalmente destinato all'input dell'utente, simboli speciali come virgolette e codice SQL arbitrario per causare l'esecuzione del sistema del database, quindi l' SQL injection .

Il risultato?

school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

SQL injection è l'equivalente del database di una vulnerabilità di esecuzione remota di codice arbitrario in un sistema operativo o un'applicazione. Il potenziale impatto di un attacco di SQL injection di successo non può essere sottovalutato - a seconda del sistema di database e della configurazione dell'applicazione, può essere utilizzato da un utente malintenzionato per causare la perdita di dati (come in questo caso), ottenere accesso non autorizzato ai dati o persino eseguire codice arbitrario sulla macchina host stessa.

Come notato dal fumetto XKCD, un modo di proteggere dagli attacchi SQL injection è quello di disinfettare gli input del database, ad esempio evitando caratteri speciali, in modo che non possano modificare il comando SQL sottostante e quindi non possano causare l'esecuzione di codice SQL arbitrario. Se si utilizzano query con parametri, ad esempio utilizzando SqlParameter in ADO.NET, l'input verrà, come minimo, automaticamente disinfettato per evitare l'iniezione SQL.

Tuttavia, la disinfezione degli input a livello di applicazione potrebbe non arrestare tecniche di iniezione SQL più avanzate. Ad esempio, ci sono modi per aggirare la funzione PHP mysql_real_escape_string . Per una maggiore protezione, molti sistemi di database supportano istruzioni preparate . Se implementate correttamente nel back-end, le istruzioni preparate possono rendere impossibile l'iniezione SQL trattando gli input di dati come semanticamente separati dal resto del comando.


Come tutti gli altri hanno già sottolineato, il '); chiude la dichiarazione originale e segue una seconda affermazione. La maggior parte dei framework, inclusi linguaggi come PHP, dispongono di impostazioni di sicurezza predefinite che non consentono più istruzioni in una stringa SQL. In PHP, ad esempio, è possibile eseguire più istruzioni in una sola stringa SQL utilizzando la funzione mysqli_multi_query .

Tuttavia, è possibile modificare un'istruzione SQL esistente tramite SQL injection senza dover aggiungere una seconda istruzione. Supponiamo che tu abbia un sistema di login che controlla un nome utente e una password con questa semplice selezione:

$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')";
$result=mysql_query($query);

Se fornisci peter come username e secret come password, la stringa SQL risultante sarà simile a questa:

SELECT * FROM users WHERE username='peter' and (password='secret')

È tutto ok. Ora immagina di fornire questa stringa come password:

' OR '1'='1

Quindi la stringa SQL risultante sarebbe questa:

SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')

Ciò consentirebbe di accedere a qualsiasi account senza conoscere la password. Quindi non è necessario essere in grado di utilizzare due istruzioni per utilizzare SQL injection, sebbene sia possibile fare cose più distruttive se si è in grado di fornire più istruzioni.


Ecco come funziona: supponiamo che l'amministratore stia cercando i record degli studenti

Robert'); DROP TABLE STUDENTS; --

Poiché l'account amministratore ha privilegi elevati, è possibile eliminare la tabella da questo account.

Il codice per recuperare il nome utente dalla richiesta è

Ora la query dovrebbe essere qualcosa del genere (per cercare nella tabella degli studenti)

String query="Select * from student where username='"+student_name+"'";

statement.executeQuery(query); //Rest of the code follows

La query risultante diventa

Select * from student where username='Robert'); DROP TABLE STUDENTS; --

Poiché l'input dell'utente non è sterilizzato, la query sopra è manipolata in 2 parti

Select * from student where username='Robert'); 

DROP TABLE STUDENTS; --

Il doppio trattino (-) commenterà solo la parte rimanente della query.

Questo è pericoloso in quanto può annullare l'autenticazione della password, se presente

Il primo farà la ricerca normale.

Il secondo farà cadere lo studente della tabella se l'account ha privilegi sufficienti (in genere l'account amministratore della scuola eseguirà tale query e avrà i privilegi di cui sopra).


Il '); termina la query, non avvia un commento. Quindi rilascia la tabella studenti e commenta il resto della query che doveva essere eseguita.


In questo caso, 'non è un personaggio di commento. È usato per delimitare stringhe letterali. L'artista comico sta scommettendo sull'idea che la scuola in questione abbia una sql dinamica da qualche parte che assomiglia a qualcosa del genere:

$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";

Quindi ora il carattere termina la stringa letterale prima che il programmatore se lo aspettasse. Combinato con il; carattere per terminare la dichiarazione, un utente malintenzionato può ora aggiungere qualsiasi sql che vogliono. Il commento alla fine è quello di assicurarsi che qualsiasi sql rimanente nell'istruzione originale non impedisca la compilazione della query sul server.

FWIW, penso anche che il fumetto in questione abbia un dettaglio importante sbagliato: se stai pensando di disinfettare gli input del tuo database, come suggerisce il fumetto, lo stai ancora facendo male. Invece, dovresti pensare in termini di messa in quarantena degli input del tuo database e il modo corretto per farlo è tramite query parametrizzate.


No, ' non è un commento in SQL, ma un delimitatore.

La mamma supponeva che il programmatore di database avesse fatto una richiesta simile a:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');

(per esempio) per aggiungere il nuovo studente, dove il contenuto della variabile $xxx stato preso direttamente da un modulo HTML, senza controllare il formato e non eseguire l'escape di caratteri speciali.

Quindi se $firstName contiene Robert'); DROP TABLE students; -- Robert'); DROP TABLE students; -- Robert'); DROP TABLE students; -- il programma di database eseguirà la seguente richiesta direttamente sul DB:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('Robert'); DROP TABLE students; --', 'XKCD');

vale a dire. terminerà presto l'istruzione insert, eseguirà qualsiasi codice dannoso richiesto dal cracker, quindi commenterà qualunque resto del codice ci possa essere.

Mmm, sono troppo lento, vedo già 8 risposte prima delle mie nella banda arancione ... :-) Un argomento popolare, a quanto pare.


Supponiamo che tu abbia ingenuamente scritto un metodo di creazione degli studenti come questo:

void createStudent(String name) {
    database.execute("INSERT INTO students (name) VALUES ('" + name + "')");
}

E qualcuno inserisce il nome Robert'); DROP TABLE STUDENTS; -- Robert'); DROP TABLE STUDENTS; --

Quel che viene eseguito sul database è questa query:

INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')

Il punto e virgola termina il comando di inserimento e ne avvia un altro; il - commenta il resto della linea. Il comando DROP TABLE viene eseguito ...

Questo è il motivo per cui i parametri di bind sono una buona cosa.


Una virgoletta singola è l'inizio e la fine di una stringa. Un punto e virgola è la fine di una dichiarazione. Quindi se stavano facendo una selezione come questa:

Select *
From Students
Where (Name = '<NameGetsInsertedHere>')

L'SQL diventerebbe:

Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
--             ^-------------------------------^

Su alcuni sistemi, la select verrà eseguita per prima, seguita drop ! Il messaggio è: NON VALORIZZARE I VALORI EMBED NEL TUO SQL. Usa invece i parametri!





sql-injection