Devo proteggermi dall'iniezione SQL se ho utilizzato un menu a discesa?


96

Capisco che non dovresti MAI fidarti dell'input dell'utente da un modulo, principalmente a causa della possibilità di iniezione SQL.

Tuttavia, questo si applica anche a un modulo in cui l'unico input proviene da un menu a discesa (vedi sotto)?

Sto salvando il file $_POST['size']in una sessione che viene quindi utilizzato in tutto il sito per interrogare i vari database (con una mysqliquery di selezione) e qualsiasi iniezione SQL li danneggerebbe sicuramente (possibilmente lasciandoli cadere).

Non esiste un'area per l'input dell'utente digitato per interrogare i database, solo menu a discesa.

<form action="welcome.php" method="post">
<select name="size">
  <option value="All">Select Size</option> 
  <option value="Large">Large</option>
  <option value="Medium">Medium</option>
  <option value="Small">Small</option>
</select>
<input type="submit">
</form>

112
Sì. Niente impedisce a un utente malintenzionato di inviare i valori desiderati nel tuo <select>input. Infatti anche un utente leggermente tecnico potrebbe aggiungere ulteriori opzioni utilizzando la console del browser. se mantieni una whitelist di array di valori disponibili e confronti l'input con esso, puoi mitigarlo (e dovresti perché impedisce valori indesiderati)
Michael Berkowski

13
Dovresti capire le cose di base di richiesta / risposta e la cosa che non importa come il front-end è costruito sulla richiesta, cioè in questo caso menu a discesa
Royal Bg

13
@YourCommonSense Perché è una buona domanda. Non tutti si rendono conto di quanto sia manipolabile un cliente. Ciò provocherà risposte molto preziose a questo sito.
Cruncher

8
@ Cruncher vedo. Per uno stackoverflowiano medio è una scienza missilistica di cui hanno sentito parlare prima. Anche nonostante la domanda più votata sotto il tag PHP.
Il tuo buon senso

9
"Capisco che non dovresti MAI fidarti dell'input dell'utente". Nessuna eccezione.
Prinzhorn

Risposte:


69

Potresti fare qualcosa di semplice come il seguente esempio per assicurarti che la dimensione pubblicata sia quella che ti aspetti.

$possibleOptions = array('All', 'Large', 'Medium', 'Small');

if(in_array($_POST['size'], $possibleOptions)) {
    // Expected
} else {
    // Not Expected
}

Quindi usa mysqli_ * se stai usando una versione di php> = 5.3.0 che dovresti essere, per salvare il tuo risultato. Se usato correttamente, questo aiuterà con sql injection.


è una versione abbreviata di un OliverBS "whitelist"?
Tatters

Non solo una versione molto semplice di uno, potresti aggiungere i valori in un database per controllarli per renderlo più facile e riutilizzabile, oppure creare una classe di whitelist con un metodo specifico per ogni whitelist da controllare. se non si desidera utilizzare un database, le proprietà della whitelist potrebbero essere all'interno di una proprietà dell'array nella classe whitelist.
Oliver Bayes-Shelton

9
Tuttavia, è necessario utilizzare Dichiarazioni preparate (consigliato) o mysqli_real_escape_string. Inoltre, esegui correttamente l'escape dei valori durante l'output (ad esempio, usa htmlspecialchars () in un documento HTML).
ComFreek

4
Io suggerirei di impostare il terzo parametro di in_arrayal truerigorosa confronto. Non sono sicuro di cosa potrebbe andare storto, ma il confronto libero è piuttosto bizzarro.
Brilliand

1
I valori di @OliverBS $ _POST possono anche essere array. Le stringhe numeriche vengono confrontate come numeri ("5" == "05"). Non penso che tu abbia una falla di sicurezza nel tuo esempio specifico, ma le regole sono complesse e potrebbero aprirsi delle falle per ragioni che non capisco nemmeno. Il confronto rigoroso è più facile da ragionare e quindi più facile da usare in modo sicuro.
Brilliand

195

Sì, devi proteggerti da questo.

Lascia che ti mostri perché, utilizzando la console per sviluppatori di Firefox:

Ho modificato uno dei valori nel menu a discesa per essere un'istruzione della tabella a discesa

Se non pulisci questi dati, il tuo database verrà distrutto. (Questa potrebbe non essere un'istruzione SQL totalmente valida, ma spero di aver capito il mio punto di vista.)

Solo perché hai limitato le opzioni disponibili nel menu a discesa non significa che hai limitato i dati che posso inviare al tuo server.

Se hai provato a limitare ulteriormente questo comportamento di utilizzo sulla tua pagina, le mie opzioni includono la disabilitazione di tale comportamento o semplicemente la scrittura di una richiesta HTTP personalizzata sul tuo server che imita comunque l'invio di questo modulo. C'è uno strumento chiamato curl usato esattamente per questo, e penso che il comando per inviare questa iniezione SQL comunque sarebbe simile a questo:

curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--"  http://www.example.com/profile/save

(Questo potrebbe non essere un comando curl totalmente valido, ma ancora una volta, spero di aver capito il mio punto di vista.)

Quindi, ribadisco:

Non fidarti MAI dell'input dell'utente. Proteggiti SEMPRE.

Non dare per scontato che l'input dell'utente sia mai sicuro. È potenzialmente pericoloso anche se arriva attraverso un mezzo diverso da un modulo. Niente di tutto ciò è mai abbastanza affidabile da rinunciare a proteggersi dall'iniezione SQL.


24
Per non parlare della creazione di un payload personalizzato con curl. SEMPRE disinfettare gli input lato server !
recursion.ninja

14
Un'immagine dice più di mille parole.
davidkonrad

4
Questa dovrebbe essere la risposta predefinita. Inoltre dovrebbe includere qualcosa in merito curl. Le persone non capiscono che puoi inviare una richiesta HTTP da qualsiasi luogo a qualsiasi luogo utilizzando qualsiasi formato e passando qualsiasi valore, spetta al server assicurarsi che la richiesta sia valida prima di elaborarla.
retrohacker

2
Che carino! Sono i piccoli Bobby Tables!
Aura

45

Poiché questa domanda è stata contrassegnata con , ecco una risposta riguardo a questo particolare tipo di attacco:

Come ti è stato detto nei commenti, devi usare istruzioni preparate per ogni singola query che coinvolge dati variabili, senza eccezioni .

Indipendentemente da qualsiasi materiale HTML!
È essenziale comprendere che le query SQL devono essere formattate correttamente indipendentemente da qualsiasi fattore esterno, sia esso input HTML o qualsiasi altra cosa.

Sebbene sia possibile utilizzare l'elenco bianco suggerito in altre risposte per lo scopo di convalida dell'input, non dovrebbe influire su alcuna azione relativa a SQL : devono rimanere le stesse, indipendentemente dal fatto che tu abbia convalidato o meno l'input HTML. Significa che devi ancora utilizzare istruzioni preparate quando aggiungi variabili alla query.

Qui puoi trovare una spiegazione approfondita, perché le dichiarazioni preparate sono un must e come usarle correttamente e dove non sono applicabili e cosa fare in tal caso: Guida per autostoppisti alla protezione SQL Injection

Inoltre, questa domanda è stata contrassegnata con . Per lo più per caso, presumo, ma in ogni caso, devo avvertirti che mysqli non è una sostituzione adeguata per le vecchie funzioni mysq_ * . Semplicemente perché se usato nel vecchio stile, non aggiungerà alcuna sicurezza. Sebbene il supporto per le istruzioni preparate sia doloroso e fastidioso, al punto che l'utente medio di PHP non è proprio in grado di provarle. Pertanto, se non è disponibile alcun ORM o una sorta di libreria di astrazione, PDO è la tua unica scelta.


5
i = casuale (0, 15); // alcune query utilizzando i. Ho ancora bisogno di una dichiarazione preparata qui?
Cruncher

2
Qual è l'implementazione di random?
Slicedpan

9
@YourCommonSense vista ristretta? Per me "fai sempre X, e non fornirò alcuna motivazione" è una visione ristretta.
Cruncher

5
@Cruncher Non riesco a pensare a nessun motivo per non usare sempre affermazioni preparate, ogni volta, per tutto, sempre. Me ne puoi dire uno?
Wesley Murch

3
@ Cruncher Con cui sono d'accordo, sembra che tu stia interpretando Devil's Advocate. La stipula nella risposta è anche "coinvolgendo eventuali dati variabili" . Ci sono alcuni casi come l'int casting di PHP forse, ma sembra meglio usare solo istruzioni preparate. Dire alle persone (inesperte) che un piccolo "incremento" delle prestazioni è più importante della sicurezza. La risposta è "incompleta" ma invia un messaggio forte che gli altri stanno ignorando. Appoggio il mio caso.
Wesley Murch

12

Sì.

Chiunque può falsificare qualsiasi cosa per i valori che vengono effettivamente inviati -

Quindi, per convalidare i menu a discesa, puoi semplicemente controllare per assicurarti che il valore con cui stai lavorando fosse nel menu a discesa - qualcosa del genere sarebbe il modo migliore (più sensatamente paranoico):

if(in_array($_POST['ddMenu'], $dropDownValues){

    $valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)];

} else {

    die("effin h4x0rs! Keep off my LAMP!!"); 

}

5
Questa risposta è incompleta, inoltre dovresti usare istruzioni preparate, in modo che l'iniezione non sia possibile indipendentemente da quale valore è impostato
Slicedpan

7
@ Slicedpan Davvero? Sembra che tu stia saltando sul carro senza sapere perché ... Se ho una manciata di possibili input in una query, in modo tale da poter verificare che siano tutti a posto (cosa che so, perché li ho fatti io), quindi non si accumulano ulteriori vantaggi in termini di sicurezza dall'utilizzo di una dichiarazione preparata
Cruncher

3
Tranne che l'applicazione dell'uso di istruzioni preparate come convenzione ti protegge dall'introduzione di vulnerabilità di SQL injection in futuro.
Slicedpan

1
@Cruncher Fare una promessa "tutti i valori nell'elenco a discesa saranno sempre al sicuro" è una promessa molto pericolosa da fare. I valori vengono modificati, il codice non viene necessariamente aggiornato. Chi aggiorna i valori potrebbe anche non sapere qual è il valore non sicuro! Soprattutto nel regno della programmazione web, che è piena di tutti i tipi di problemi di sicurezza, lesinare su qualcosa di simile è semplicemente irresponsabile (e altre parole meno carine).
hyde

1
@Cruncher Se gestisci correttamente le tue cose SQL, puoi accettare qualsiasi input senza interferire con il corretto funzionamento dell'SQL. La parte SQL dovrebbe essere preparata e pronta ad accettare qualsiasi cosa, indipendentemente da dove provenga. Tutto il resto è soggetto a errori.
glglgl

8

Un modo per proteggersi dagli utenti che modificano i menu a discesa utilizzando la console è utilizzare solo valori interi in essi. Quindi puoi verificare che il valore POST contenga un numero intero e utilizzare un array per convertirlo in testo quando necessario. Per esempio:

<?php
// No, you don't need to specify the numbers in the array but as we're using them I always find having them visually there helpful.
$sizes = array(0 => 'All', 1 => 'Large', 2 => 'Medium', 3 => 'Small');
$size = filter_input(INPUT_POST, "size", FILTER_VALIDATE_INT);

echo '<select name="size">';
foreach($sizes as $i => $s) {
    echo '<option value="' . $i . '"' . ($i == $size ? ' selected' : '') . '>' . $s . '</option>';
}
echo '</select>';

Quindi puoi usare $sizenella tua query con la consapevolezza che conterrà sempre FALSEo solo un numero intero.


2
@OliverBS Che cosa è filter_inputse non un controllo sul lato server? Nessuno può pubblicare nulla tranne un numero intero.
Styphon

Grazie Styphon, quindi questo sostituisce il modulo nell'OP? E questo sarebbe applicabile a moduli più grandi con più elenchi a discesa? Se aggiungessi un altro menu a discesa per dire "colore"?
Tatters

@SamuelTattersfield Sì e sì. Puoi usarlo per tutti i menu a discesa che desideri, con tutte le opzioni che desideri. Devi solo creare un nuovo array per ogni menu a discesa e inserire tutte le opzioni per il menu a discesa nell'array.
Styphon

Bello! Mi piace. Proverò e vedrò cosa ottengo nei test.
Tatters

@SamuelTattersfield Ottimo, assicurati di usare anche la filter_inputparte per la convalida, altrimenti è utile come una teiera di cioccolato per la sicurezza.
Styphon

7

Le altre risposte coprono già ciò che devi sapere. Ma forse aiuta a chiarire un po 'di più:

Ci sono DUE COSE che devi fare:

1. Convalida i dati del modulo.

Come la risposta di Jonathan Hobbs mostra molto chiaramente, la scelta dell'elemento html per l'input del modulo non fa alcun filtro affidabile per te.

La convalida viene solitamente eseguita in un modo che non altera i dati, ma che mostra di nuovo il modulo, con i campi contrassegnati come "Correggi questo".

La maggior parte dei framework e dei CMS dispone di form builder che ti aiutano in questa attività. E non solo, aiutano anche contro CSRF (o "XSRF"), che è un'altra forma di attacco.

2. Disinfetta / Esci dalle variabili nelle istruzioni SQL ..

.. o lascia che le dichiarazioni preparate facciano il lavoro per te.

Se si crea un'istruzione (My) SQL con qualsiasi variabile, fornita dall'utente o meno, è necessario eseguire l'escape e citare queste variabili.

In genere, qualsiasi variabile di questo tipo inserita in un'istruzione MySQL dovrebbe essere una stringa o qualcosa che PHP può essere trasformato in modo affidabile in una stringa che MySQL può digerire. Come i numeri.

Per le stringhe, è quindi necessario scegliere uno dei diversi metodi per eseguire l'escape della stringa, ovvero sostituire tutti i caratteri che avrebbero effetti collaterali in MySQL.

  • Nella vecchia scuola MySQL + PHP, mysql_real_escape_string () fa il lavoro. Il problema è che è troppo facile da dimenticare, quindi dovresti assolutamente usare istruzioni preparate o costruttori di query.
  • In MySQLi, puoi utilizzare istruzioni preparate.
  • La maggior parte dei framework e dei CMS fornisce generatori di query che ti aiutano in questa attività.

Se hai a che fare con un numero, puoi omettere l'escape e le virgolette (questo è il motivo per cui le istruzioni preparate consentono di specificare un tipo).

È importante sottolineare che esegui l'escape delle variabili per l'istruzione SQL e NON per il database stesso . Il database memorizzerà la stringa originale, ma l'istruzione necessita di una versione con escape.

Cosa succede se ometti uno di questi?

Se non utilizzi la convalida del modulo , ma disinfetti l'input SQL, potresti vedere accadere tutti i tipi di cose brutte, ma non vedrai SQL injection! (*)

In primo luogo, può portare la tua domanda in uno stato che non avevi pianificato. Ad esempio, se si desidera calcolare l'età media di tutti gli utenti, ma un utente ha fornito "aljkdfaqer" per l'età, il calcolo fallirà.

In secondo luogo, possono esserci tutti i tipi di altri attacchi injection che devi considerare: ad esempio, l'input dell'utente potrebbe contenere javascript o altro.

Possono ancora esserci problemi con il database: ad esempio, se un campo (colonna della tabella del database) è limitato a 255 caratteri e la stringa è più lunga di quella. Oppure se il campo accetta solo numeri e si tenta invece di salvare una stringa non numerica. Ma questa non è "iniezione", è solo "arresto anomalo dell'applicazione".

Ma, anche se si dispone di un campo di testo libero in cui si consente qualsiasi input senza alcuna convalida, è comunque possibile salvarlo nel database proprio in questo modo, se si evita correttamente quando si passa a un'istruzione del database. Il problema nasce quando vuoi usare questa stringa da qualche parte.

(*) o questo sarebbe qualcosa di veramente esotico.

Se non esegui l'escape delle variabili per le istruzioni SQL , ma hai convalidato l'input del modulo, puoi comunque vedere accadere cose brutte.

Innanzitutto, rischi che quando salvi i dati nel database e li carichi di nuovo, non saranno più gli stessi dati, "persi nella traduzione".

In secondo luogo, può causare istruzioni SQL non valide e quindi bloccare l'applicazione. Ad esempio, se una variabile contiene virgolette o virgolette doppie, a seconda del tipo di virgolette utilizzate, si otterrà un'istruzione MySQL non valida.

In terzo luogo, può ancora causare SQL injection.

Se l'input dell'utente dai moduli è già filtrato / convalidato, l' iniezione SQl intenzionale potrebbe diventare meno probabile, SE l'input è ridotto a un elenco di opzioni codificato o è limitato a numeri. Ma qualsiasi input di testo libero può essere utilizzato per l'iniezione SQL, se non si esegue correttamente l'escape delle variabili nelle istruzioni SQL.

E anche se non hai alcun input dal modulo, potresti comunque avere stringhe da tutti i tipi di fonti: lette dal filesystem, raschiate da Internet, ecc. Nessuno può garantire che queste stringhe siano sicure.


Né i dati troppo lunghi né una stringa nel campo numerico causeranno il crash di nulla
Il tuo buon senso

hmm, ho appena provato questo ora, e in effetti mostra un avviso invece di un errore. Penso che sia PDO che trasforma questo in un errore. Sono sicuro di aver discusso in una dozzina di numeri su drupal.org in cui alcune stringhe sono troppo lunghe per un varchar.
donquixote

6

Il tuo browser web non "sa" che sta ricevendo una pagina da php, tutto quello che vede è html. E il livello http ne sa anche meno. Devi essere in grado di gestire quasi ogni tipo di input che può attraversare il livello http (fortunatamente per la maggior parte degli input php darà già un errore). Se stai cercando di evitare che richieste dannose rovinino il tuo database, allora devi presumere che il ragazzo dall'altra parte sappia cosa sta facendo e che non sia limitato a ciò che puoi vedere nel tuo browser in circostanze normali ( per non parlare di quello che puoi armeggiare con gli strumenti di sviluppo di un browser). Quindi sì, è necessario soddisfare qualsiasi input dal menu a discesa, ma per la maggior parte degli input è possibile fornire un errore.


6

Il fatto che tu abbia limitato l'utente a utilizzare solo i valori di un determinato elenco a discesa è irrilevante. Un utente tecnico può acquisire la richiesta http inviata al tuo server prima che lasci la sua rete, modificarla utilizzando uno strumento come un server proxy locale e quindi continuare a suo modo. Utilizzando la richiesta modificata, possono inviare valori di parametro che non sono quelli specificati nell'elenco a discesa. Gli sviluppatori devono avere la mentalità che le restrizioni del client sono spesso prive di significato, poiché qualsiasi cosa su un client può essere modificata. La convalida del server è richiesta in ogni singolo punto in cui entrano i dati del client. Gli aggressori fanno affidamento sull'ingenuità degli sviluppatori in questo unico aspetto.


5

È preferibile utilizzare una query parametrizzata per garantire contro l'iniezione SQL. In tal caso l'aspetto della query sarebbe questo:

SELECT * FROM table WHERE size = ?

Quando fornisci una query come quella sopra con un testo che non è stato verificato per integrità (l'input non è convalidato sul server) e contiene codice SQL injection, verrà gestito correttamente. In altre parole, la richiesta si tradurrà in qualcosa di simile nel livello del database:

SELECT * FROM table WHERE size = 'DROP table;'

Questo selezionerà semplicemente 0 risultati non appena restituiti, il che renderà la query inefficace nel causare effettivamente danni al database senza la necessità di una whitelist, un controllo di verifica o altre tecniche. Si noti che un programmatore responsabile farà la sicurezza a strati e spesso convaliderà oltre a parametrizzare le query. Tuttavia, ci sono pochi motivi per non parametrizzare le query dal punto di vista delle prestazioni e la sicurezza aggiunta da questa pratica è una buona ragione per familiarizzare con le query parametrizzate.


4

Tutto ciò che viene inviato dal tuo modulo arriva al tuo server come testo attraverso i fili. Non c'è nulla che impedisca a chiunque di creare un bot per imitare il client o digitarlo da un terminale se lo desidera. Non dare mai per scontato che, poiché hai programmato il client, si comporterà come pensi che lo farà. Questo è davvero facile da falsificare.

Esempio di cosa può e accadrà quando ti fidi del cliente.


4

Un hacker può bypassare completamente il browser, incluso il controllo dei moduli Javascript, inviando una richiesta tramite Telnet. Certo, guarderà il codice della tua pagina html per ottenere i nomi dei campi che deve usare, ma da quel momento in poi è "tutto va bene" per lui. Quindi, devi controllare tutti i valori inviati sul server come se non provenissero dalla tua pagina html.

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.