Perché SQL Injection non si verifica su questa query all'interno di una procedura memorizzata?


18

Ho effettuato la seguente procedura memorizzata:

ALTER PROCEDURE usp_actorBirthdays (@nameString nvarchar(100), @actorgender nvarchar(100))
AS
SELECT ActorDOB, ActorName FROM tblActor
WHERE ActorName LIKE '%' + @nameString + '%'
AND ActorGender = @actorgender

Ora, ho provato a fare qualcosa del genere. Forse sto sbagliando, ma voglio essere sicuro che una tale procedura possa impedire qualsiasi iniezione SQL:

EXEC usp_actorBirthdays 'Tom', 'Male; DROP TABLE tblActor'

L'immagine seguente mostra l'SQL sopra eseguito in SSMS e i risultati vengono visualizzati correttamente invece di un errore:

inserisci qui la descrizione dell'immagine

A proposito, ho aggiunto quella parte dopo il punto e virgola dopo l'esecuzione della query. Quindi l'ho eseguito di nuovo, ma quando ho controllato per vedere se la tabella tblActor esiste o no, era ancora lì. Sto facendo qualcosa di sbagliato? O è davvero a prova di iniezione? Immagino che quello che sto cercando di chiedere qui sia che è una procedura memorizzata come questa sicura? Grazie.


Hai provato questoEXEC usp_actorBirthdays 'Tom', 'Male''; DROP TABLE tblActor'
MarmiK il

Risposte:


38

Questo codice funziona correttamente perché è:

  1. Parametrizzato e
  2. Non eseguire alcun SQL dinamico

Affinché SQL Injection funzioni, è necessario creare una stringa di query (cosa che non si sta eseguendo) e non tradurre singoli apostrofi ( ') in apostrofi di escape ( '') (questi vengono salvati tramite i parametri di input).

Nel tuo tentativo di passare un valore "compromesso", la 'Male; DROP TABLE tblActor'stringa è proprio quella, una stringa semplice.

Ora, se stessi facendo qualcosa sulla falsariga di:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = '
          + @InputParam;

EXEC(@SQL);

poi che sarebbe suscettibile di SQL Injection, perché che query non è nell'attuale contesto pre-analizzata; quella query è solo un'altra stringa al momento. Quindi il valore di @InputParampotrebbe essere '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;e ciò potrebbe presentare un problema perché la query verrebbe rappresentata ed eseguita come:

SELECT fields FROM table WHERE field23 = '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;

Questo è uno (di diversi) motivi principali per utilizzare Stored Procedures: intrinsecamente più sicuro (beh, purché tu non aggiri quella sicurezza creando query come ho mostrato sopra senza convalidare i valori di tutti i parametri utilizzati). Tuttavia, se è necessario creare Dynamic SQL, il modo preferito è parametrizzare anche utilizzando sp_executesql:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT fields FROM table WHERE field23 = @SomeDate_tmp';

EXEC sp_executesql
  @SQL,
  N'SomeDate_tmp DATETIME',
  @SomeDate_tmp = @InputParam;

Utilizzando questo approccio, qualcuno che tenta di passare '2015-10-31'; SELECT * FROM PlainTextCreditCardInfo;per un DATETIMEparametro di input riceverà un errore durante l'esecuzione della Stored Procedure. O anche se la Procedura memorizzata accettasse @InputParametercome NVARCHAR(100), dovrebbe DATETIMEpassare a un per passare a quella sp_executesqlchiamata. E anche se il parametro in Dynamic SQL è un tipo di stringa, arrivando alla Stored Procedure in primo luogo ogni singolo apostrofo verrebbe automaticamente evaso in un doppio apostrofo.

Esiste un tipo di attacco meno noto in cui l'attaccante tenta di riempire il campo di input con apostrofi in modo tale che una stringa all'interno della Stored Procedure che verrebbe utilizzata per costruire l'SQL dinamico ma che è dichiarata troppo piccola non può adattarsi a tutto ed elimina l'apostrofo finale e in qualche modo finisce con il numero corretto di apostrofi in modo da non essere più "evaso" all'interno della stringa. Questo si chiama SQL Truncation ed è stato discusso in un articolo della rivista MSDN intitolato "Nuovi attacchi di troncamento SQL e come evitarli", di Bala Neerumalla, ma l'articolo non è più online. Il problema contenente questo articolo , l'edizione di novembre 2006 di MSDN Magazine , è disponibile solo come file della Guida di Windows (in .chmformato). Se lo scarichi, potrebbe non aprirsi a causa delle impostazioni di sicurezza predefinite. In tal caso, fare clic con il pulsante destro del mouse sul file MSDNMagazineNovember2006en-us.chm e selezionare "Proprietà". In una di quelle schede ci sarà un'opzione per "Fidati di questo tipo di file" (o qualcosa del genere) che deve essere selezionata / abilitata. Fare clic sul pulsante "OK", quindi provare ad aprire nuovamente il file .chm .

Un'altra variante dell'attacco di troncamento è, supponendo che una variabile locale sia utilizzata per memorizzare il valore "sicuro" fornito dall'utente in quanto aveva doppie virgolette raddoppiate in modo da sfuggire, per riempire quella variabile locale e posizionare la virgoletta singola alla fine. L'idea qui è che se la variabile locale non è dimensionata correttamente, non ci sarà abbastanza spazio alla fine per la seconda virgoletta singola, lasciare la variabile che termina con una singola virgoletta singola che poi si combina con la virgoletta singola che termina il valore letterale in SQL dinamico, trasformando quella virgoletta singola finale in una virgoletta singola con escape incorporata, e la stringa letterale in SQL dinamico termina quindi con la virgoletta singola successiva che intendeva iniziare la stringa letterale successiva. Per esempio:

-- Parameters:
DECLARE @UserID      INT = 37,
        @NewPassword NVARCHAR(15) = N'Any Value ....''',
        @OldPassword NVARCHAR(15) = N';Injected SQL--';

-- Stored Proc:
DECLARE @SQL NVARCHAR(MAX),
        @NewPassword_fixed NVARCHAR(15) = REPLACE(@NewPassword, N'''', N''''''),
        @OldPassword_fixed NVARCHAR(15) = REPLACE(@OldPassword, N'''', N'''''');

SELECT @NewPassword AS [@NewPassword],
       REPLACE(@NewPassword, N'''', N'''''') AS [REPLACE output],
       @NewPassword_fixed AS [@NewPassword_fixed];
/*
@NewPassword          REPLACE output          @NewPassword_fixed
Any Value ....'       Any Value ....''        Any Value ....'
*/

SELECT @OldPassword AS [@OldPassword],
       REPLACE(@OldPassword, N'''', N'''''') AS [REPLACE output],
       @OldPassword_fixed AS [@OldPassword_fixed];
/*
@OldPassword          REPLACE output          @OldPassword_fixed
;Injected SQL--       ;Injected SQL--         ;Injected SQL--
*/

SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
           + @NewPassword_fixed + N''' WHERE [TableNameID] = '
           + CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
           + @OldPassword_fixed + N''';';

SELECT @SQL AS [Injected];

Qui, l'SQL dinamico da eseguire è ora:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

Lo stesso SQL dinamico, in un formato più leggibile, è:

UPDATE dbo.TableName SET [Password] = N'Any Value ....'' WHERE [TableNameID] = 37 AND [Password] = N';

Injected SQL--';

Risolvere questo è facile. Basta eseguire una delle seguenti operazioni:

  1. NON UTILIZZARE SQL DINAMICO A MENO ASSOLUTAMENTE NECESSARIO! (Lo sto elencando prima perché dovrebbe davvero essere la prima cosa da considerare).
  2. Ridimensiona correttamente la variabile locale (ovvero dovrebbe essere doppia rispetto al parametro di input, nel caso in cui tutti i caratteri passati siano tra virgolette singole.
  3. Non utilizzare una variabile locale per memorizzare il valore "fisso"; basta inserire REPLACE()direttamente nella creazione di Dynamic SQL:

    SET @SQL = N'UPDATE dbo.TableName SET [Password] = N'''
               + REPLACE(@NewPassword, N'''', N'''''') + N''' WHERE [TableNameID] = '
               + CONVERT(NVARCHAR(10), @UserID) + N' AND [Password] = N'''
               + REPLACE(@OldPassword, N'''', N'''''') + N''';';
    
    SELECT @SQL AS [No SQL Injection here];

    Il Dynamic SQL non è più compromesso:

    UPDATE dbo.TableName SET [Password] = N'Any Value ....''' WHERE [TableNameID] = 37 AND [Password] = N';Injected SQL--';

Note sull'esempio di Trunction sopra:

  1. Sì, questo è un esempio molto ingegnoso. Non c'è molto che si possa fare in soli 15 caratteri da iniettare. Certo, forse un DELETE tableNameper essere distruttivo, ma meno probabilità di aggiungere un utente back-door o modificare una password di amministratore.
  2. Questo tipo di attacco probabilmente richiede la conoscenza del codice, dei nomi delle tabelle, ecc. Meno probabile che venga fatto da uno sconosciuto / script-kiddie casuale, ma ho lavorato in un posto che è stato attaccato da un ex dipendente piuttosto turbato che sapeva di una vulnerabilità in una particolare pagina Web di cui nessun altro era a conoscenza. Significato, a volte gli aggressori hanno una conoscenza intima del sistema.
  3. Certo, è probabile che venga ripristinata la reimpostazione della password di tutti , il che potrebbe avvisare l'azienda che si sta verificando un attacco, ma potrebbe comunque fornire abbastanza tempo per iniettare un utente back-door o forse ottenere alcune informazioni secondarie da utilizzare / sfruttare in seguito.
  4. Anche se questo scenario è per lo più accademico (cioè non è probabile che accada nel mondo reale), non è ancora impossibile.

Per informazioni più dettagliate relative a SQL Injection (relative a vari RDBMS e scenari), consultare quanto segue da Open Web Application Security Project (OWASP):
Test per SQL Injection

Stack Overflow correlato risposta su SQL Injection e SQL Truncation:
quanto è sicuro T-SQL dopo aver sostituito il carattere 'escape?


2
Oh, grazie mille, questa è un'ottima risposta. Ora capisco. Mi piacerebbe davvero vedere anche la tecnica che hai citato alla fine, in cui l'attaccante tenta di riempire il campo di input con apostrofi, se riesci a trovarlo. Grazie in anticipo. Lo terrò aperto, nel caso in cui non lo trovi, lo sceglierò come risposta.
Ravi,

1
@Ravi Ho trovato il link ma non arriva più all'articolo poiché sono tutti archiviati. Ma ho aggiunto alcune informazioni e link utili e sto ancora cercando di trovare l'articolo all'interno di quegli archivi.
Solomon Rutzky,

1
Grazie srutzsky, leggerò l'articolo OWASP e i test per l'iniezione. Se ricordo correttamente, 'mutillidae', l'app Web vulnerabile per i test di sicurezza, ha l'iniezione SQL che avevo eseguito al college con la stringa 'OR 1 = 1' che in mutillidae mi ha portato ad accedere all'app web come amministratore I pensare. Fu allora che fui introdotto per la prima volta all'iniezione SQL.
Ravi,

1
Non sono in grado di visualizzare anche il file .chm, ma grazie per questa risposta perfetta e per tutti i collegamenti utili tra cui quello di StackOverflow e quello di OWASP. L'ho letto oggi e ho imparato molto da questo.
Ravi,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.