Argomenti trattati
Gestisci la vulnerabilità della sicurezza del database
Per aumentare la sicurezza in Dreamweaver 8.0.2 e CS3 abbiamo rimosso la possibilità di utilizzare parametri SQL dinamici all'interno di recordset standard. Il codice originale ha esposto i database a SQL Injection. L'utilizzo di istruzioni preparate per i modelli di server ColdFusion ha risolto i problemi di vulnerabilità della sicurezza. Era necessario aggiungere una funzione alla pagina quando veniva applicato il primo recordset e quindi chiamare quella funzione da tutti i recordset sulla pagina per PHP_MySQL. Questa tecnica è simile alle dichiarazioni preparate di ASP_VBS e ColdFusion.
Nello sforzo di risolvere i problemi di vulnerabilità della sicurezza, abbiamo ridotto un po' di flessibilità per aumentare la sicurezza. Vogliamo offrire agli sviluppatori che hanno già o pianificano di sviluppare estensioni che dovranno gestire query SQL dinamiche e agli utenti finali che desiderano personalizzare i propri recordset per accettare parametri dinamici la possibilità di modificare questi recordset personalizzati all'interno di Dreamweaver.
SQL Injection e metodi di prevenzione
SQL Injection è una tecnica che sfrutta una vulnerabilità di sicurezza che si verifica nel livello del database di un'applicazione. La vulnerabilità è presente quando l'input dell'utente è erroneamente filtrato per caratteri di escape letterali di stringa incorporati in istruzioni SQL o l'input dell'utente non è fortemente tipizzato e quindi viene eseguito in modo imprevisto. È di fatto un'istanza di una classe più generale di vulnerabilità che può verificarsi ogni volta che un linguaggio di programmazione o di scripting è incorporato in un altro. Per ulteriori informazioni, visita Wikipedia.
Esempio di query SQL standard:
SELECT * FROM company_com WHERE name_com = '$companyName'
Nell'esempio sopra, la variabile $ companyName viene letta da un campo del modulo di input, quindi l'utente ha il controllo totale sul valore che verrà inviato. Nel migliore dei casi, quando l'utente inserisce "Adobe" (senza virgolette) nell'input corrispondente al nome dell'azienda, la query SQL fornita sarà simile a questa:
SELECT * FROM company_com WHERE name_com = 'Adobe'
Ma ci sono casi in cui gli utenti potrebbero tentare di attaccare il sito web e rubare informazioni riservate e/o distruggere il database. Considerando l'esempio precedente, sarà facile produrre un SQL Injection immettendo la seguente stringa "blabla" OR '1'='1" (senza le virgolette circostanti). Assegnando questa sequenza di caratteri, l'utente avrà accesso a tutte le aziende anziché a una singola azienda. L'SQL effettivo che viene eseguito è:
SELECT * FROM company_com WHERE name_com = 'blabla' OR '1'='1'
Inoltre, se l'utente desidera eliminare l'intera tabella company_com, può eseguire questa operazione semplicemente passando la seguente stringa "x'; DROP TABLE company_com; --" (senza le virgolette). ecco la query SQL completa:
SELECT * FROM company_com WHERE name_com = 'x'; DROP TABLE company_com; --'
Utilizza una delle seguenti misure per proteggere il sito web da eventuali SQL Injection:
- Limita l'input a un set minimo di caratteri consentiti rimuovendo automaticamente tutti gli altri caratteri che non rientrano nell'intervallo specificato.
- Esegui l'escape di tutti i caratteri che possono causare problemi se utilizzati all'interno di una query SQL (ad esempio, virgolette singole).
- Utilizza istruzioni preparate
- Utilizza diritti di accesso al database.
- Utilizza procedure archiviate.
Si consiglia di usare istruzioni preparate, come descritto in modo più dettagliato in Uso di istruzioni preparate. Dreamweaver 8.0.2 e CS3 utilizzano l'approccio delle istruzioni preparate per i modelli server ASP_VBS, ASP_JavaScript, Cold Fusion e JSP e l'escape per l'approccio di input dell'utente per il modello server PHP_MySQL. Per ulteriori informazioni sugli attacchi di SQL Injection, fare riferimento a Esempi di attacchi di SQL Injection.
Utilizza istruzioni preparate
Dreamweaver 8.0.2 e CS utilizzano istruzioni preparate, poiché questa soluzione ci garantisce che la sostituzione dei parametri SQL effettivi con valori specifici (eventualmente inviati tramite URL) venga effettuata sul server e non sulla pagina. Le istruzioni preparate garantiscono inoltre che il valore trasferito all'SQL stesso abbia il tipo di dati appropriato e utilizzi l'escape appropriato (se un parametro è detto di tipo int, l'utente non sarà in grado di inviare una lettera oppure, se un parametro è di tipo testo, l'intera stringa passata verrà sottoposta a escape e l'utente non avrà la possibilità di eseguire un attacco SQL Injection).
Istruzioni preparate per i modelli di server ASP_VBS e ASP_JS
Per ASP_VBS e ASP_JS, facciamo affidamento sul livello del database ADODB. L'esempio seguente mostra un semplice recordset ASP_VBS che utilizza un parametro SQL dinamico per filtrare i risultati:
... <% Dim Recordset1__MMColParam Recordset1__MMColParam = "1" If (Request.QueryString("id_com") <> "") Then Recordset1__MMColParam = Request.QueryString("id_com") End If %> <% Dim Recordset1 Dim Recordset1_cmd Dim Recordset1_numRows Set Recordset1_cmd = Server.CreateObject ("ADODB.Command") Recordset1_cmd.ActiveConnection = MM_connContacts_STRING Recordset1_cmd.CommandText = "SELECT * FROM company_com WHERE id_com = ?" Recordset1_cmd.Prepared = true Recordset1_cmd.Parameters.Append Recordset1_cmd.CreateParameter("param1", 5, 1, -1, Recordset1__MMColParam) ' adDouble Set Recordset1 = Recordset1_cmd.Execute Recordset1_numRows = 0 %> ...Istruzioni preparate per il modello di server ColdFusion
ColdFusion offre supporto integrato per le istruzioni preparate. L'esempio seguente mostra un semplice recordset ColdFusion che utilizza un parametro SQL dinamico per filtrare i risultati:
... <cfparam name="URL.id_com" default="1"> <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com WHERE id_com = <cfqueryparam value="#URL.id_com#" cfsqltype="cf_sql_numeric"> </cfquery> ...Istruzioni preparate simulate per il modello di server PHP_MySQL
Le istruzioni preparate per PHP sono disponibili con MySQL 4.1, ma per supportare anche le versioni precedenti di MySQL abbiamo deciso di implementare una funzione personalizzata che svolge praticamente la stessa funzione. L'esempio seguente mostra un semplice recordset PHP che utilizza un parametro SQL dinamico per filtrare i risultati:
... <?php if (!function_exists("GetSQLValueString")) { function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "") { $theValue = get_magic_quotes_gpc() ? stripslashes($theValue) : $theValue; $theValue = function_exists("mysql_real_escape_string") ? mysql_real_escape_string($theValue) : mysql_escape_string($theValue); switch ($theType) { case "text": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "long": case "int": $theValue = ($theValue != "") ? intval($theValue) : "NULL"; break; case "double": $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL"; break; case "date": $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL"; break; case "defined": $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue; break; } return $theValue; } } $colname_Recordset1 = "-1"; if (isset($_GET['id_com'])) { $colname_Recordset1 = $_GET['id_com']; } mysql_select_db($database_connContacts, $connContacts); $query_Recordset1 = sprintf("SELECT * FROM company_com WHERE id_com = %s", GetSQLValueString($colname_Recordset1, "int")); $Recordset1 = mysql_query($query_Recordset1, $connContacts) or die(mysql_error()); $row_Recordset1 = mysql_fetch_assoc($Recordset1); $totalRows_Recordset1 = mysql_num_rows($Recordset1); ?> ...Consenti parametri dinamici nei recordset standard
In che modo è possibile proteggere l'ambiente da SQL Injection, consentendo allo stesso tempo di mantenere modificabili i parametri dinamici e il codice risultante tramite il pannello Comportamenti server in Dreamweaver? La soluzione consiste nell'inserire i parametri dinamici nella query SQL stessa, anziché trasferirli come parametri SQL classici (che potrebbero essere sottoposti a escape o aggiunti all'SQL tramite istruzioni preparate).
Importante: gli utenti non possono generare tale codice utilizzando gli oggetti e i comportamenti server forniti con Dreamweaver 8.0.2 o CS3 (esempio: Inserimento di un recordset tramite il pannello Comportamenti server o la barra Inserisci). Questa soluzione richiede che l'utente modifichi il codice manualmente o che installi un'estensione di terze parti in grado di creare tale codice. La responsabilità di scrivere codice sicuro spetta all'utente o allo sviluppatore di software di terze parti.
Lo scopo di questo metodo è consentire agli sviluppatori esperti di creare query SQL dinamiche. L'uso improprio di questa soluzione dà agli hacker la possibilità di ottenere privilegi indesiderati e/o attaccare pagine web e database. Fai riferimento a SQL Injection e metodi di prevenzione per ridurre al minimo il rischio di attacchi SQL Injection.
I principali casi in cui abbiamo identificato questa soluzione sono:
- Creazione/aggiornamento manuale di una query SQL per accettare parametri dinamici.
- Crea/aggiorna estensioni esistenti per generare parametri dinamici.
Entrambi i casi di utilizzo saranno descritti nei paragrafi seguenti.
Creazione e aggiornamento manuale di una query SQL per accettare i parametri dinamici mantenendo l'oggetto Recordset modificabile in Dreamweaver
Nell'esempio seguente l'utente desidera aggiungere funzionalità di ordinamento a una tabella dinamica. Decide di ricaricare la pagina trasferendo i file e la direzione di ordinamento da utilizzare (crescente o decrescente) nell'URL. Un esempio di URL iniziale è:
http://www.mydomain.com/index.php?sortCol=name_com&sortDir=ascLa codifica manuale è necessaria per eseguire questa operazione in Dreamweaver utilizzando i comportamenti server. Gli esempi di codice seguenti mostrano le modifiche che devono essere apportate per ogni modello di server.
Importante: nessuno degli esempi seguenti ha la convalida rispetto all'attacco SQL Injection. Il loro scopo è puramente formativo e non dovrebbero essere utilizzati nella produzione così come sono. Gli esempi non garantiscono la massima sicurezza e sono stati mantenuti il più semplice possibile. È necessario proteggersi dagli attacchi SQL Injection. Per ulteriori informazioni, fai riferimento a SQL Injection e metodi di prevenzione.
ASP_VBS
Prima:
... <% Dim Recordset1 Dim Recordset1_cmd Dim Recordset1_numRows Set Recordset1_cmd = Server.CreateObject ("ADODB.Command") Recordset1_cmd.ActiveConnection = MM_connContacts_STRING Recordset1_cmd.CommandText = "SELECT * FROM company_com" Recordset1_cmd.Prepared = true Set Recordset1 = Recordset1_cmd.Execute Recordset1_numRows = 0 %> ...Dopo:
... <% Dim orderBy: orderBy = "" If (Request.QueryString("sortCol") <> "") Then orderBy = "ORDER BY " & Request.QueryString("id_com") If (Request.QueryString("sortDir") <> "") Then orderBy = orderBy & " " & Request.QueryString("sortDir") End If End If %> <% Dim Recordset1 Dim Recordset1_cmd Dim Recordset1_numRows Set Recordset1_cmd = Server.CreateObject ("ADODB.Command") Recordset1_cmd.ActiveConnection = MM_connContacts_STRING Recordset1_cmd.CommandText = "SELECT * FROM company_com " & orderBy & "" Recordset1_cmd.Prepared = true Set Recordset1 = Recordset1_cmd.Execute Recordset1_numRows = 0 %> ...ColdFusion
Prima:
... <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com </cfquery> ...Dopo:
... <cfparam name="URL.sortCol" default=""> <cfparam name="URL.sortDir" default="ASC"> <cfset orderBy=""> <cfif (#URL.sortCol# NEQ "")> <cfset orderBy="ORDER BY #URL.sortCol# #URL.sortDir#"> </cfif> <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com #orderBy# </cfquery> ...PHP MySQL
Prima:
... <?php mysql_select_db($database_connContacts, $connContacts); $query_Recordset1 = "SELECT * FROM company_com"; $Recordset1 = mysql_query($query_Recordset1, $connContacts) or die(mysql_error()); $row_Recordset1 = mysql_fetch_assoc($Recordset1); $totalRows_Recordset1 = mysql_num_rows($Recordset1); ?> ...Dopo:
... <?php $orderBy = ""; if (isset($_GET['sortCol'])) { $orderBy = "ORDER BY " . $_GET['sortCol']; if (isset($_GET['sortDir'])) { $orderBy .= " " . $_GET['sortDir']; } } ?> <?php mysql_select_db($database_connContacts, $connContacts); $query_Recordset1 = "SELECT * FROM company_com " . $orderBy . ""; $Recordset1 = mysql_query($query_Recordset1, $connContacts) or die(mysql_error()); $row_Recordset1 = mysql_fetch_assoc($Recordset1); $totalRows_Recordset1 = mysql_num_rows($Recordset1); ?> ...Crea/aggiorna estensioni esistenti per generare parametri dinamici mantenendo il recordset ancora modificabile da Dreamweaver
Consideriamo ora lo stesso scenario di cui sopra, a eccezione del fatto che l'utente desidera creare un'estensione che genererà il codice appropriato anziché fare affidamento su una combinazione di funzionalità integrata e modifica manuale. La nuova estensione dovrebbe generare il codice corretto per i modelli di server ASP_VBS, ColdFusion e PHP_MySQL.
Importante: nelle sezioni seguenti ci concentreremo solo sulla generazione di codice che utilizzerà i valori dei parametri URL per creare una query SQL dinamica. L'obiettivo principale è generare il codice in modo che sia ancora riconosciuto da Dreamweaver e sia modificabile dalle interfacce standard di Dreamweaver. Questo tutorial non gestirà la convalida dei dati di input (tramite parametri URL) né proteggerà l'SQL finale da qualsiasi tentativo di attacchi SQL Injection poiché la complessità di tale codice non rientra nell'ambito di questo tutorial. Lo sviluppatore è il solo responsabile della protezione dell'SQL finale dagli attacchi SQL Injection.
Forniamo un'estensione demo che accetta una connessione e una tabella come input e genera una tabella dinamica che visualizza tutti i record. Aggiorna inoltre l'SQL generato per contenere la variabile orderBy, come mostrato negli esempi precedenti. L'estensione è stata progettata per Dreamweaver 8.0.2 e CS3. Il codice generato non garantisce la massima sicurezza, poiché l'obiettivo è quello di creare recordset contenenti parametri dinamici che rimangono modificabili utilizzando le interfacce standard di Dreamweaver. Il codice sorgente dell'estensione si trova nella Cartella di configurazione dell'utente.
In tutti gli esempi di codice seguenti, sono state aggiunte le sezioni evidenziate per abilitare la funzionalità di ordinamento tramite i parametri URL sortCol e sortDir.
ASP_VBS
Il codice che genera la query SQL appropriata per il modello server ASP_VBS si trova nel file "[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js" nella cartella di configurazione dell'utente. La modifica principale è alla riga 130:
... 130: paramObj.encodedSQL = "SELECT * FROM " + paramObj.table + " \" & orderBy & \""; ...Il file dei partecipanti per il comportamento server MyDynamicTable ASP_VBS include un partecipante aggiuntivo che aggiunge la definizione della variabile "orderBy" alla pagina:
<group name="MyDynamicTable" version="9.0"> <groupParticipants> <groupParticipant name="connectionref_statement" /> <groupParticipant name="MyDynamicTable_orderBy" /> <groupParticipant name="recordset_main" /> <groupParticipant name="repeatedRegion_init2" /> <groupParticipant name="DynamicTable_main" /> <groupParticipant name="recordset_close" /> </groupParticipants> </group>ColdFusion
Lo stesso "[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js" nella Cartella di configurazione dell'utente contiene anche il codice per il modello server ColdFusion; la modifica pertinente in questo caso è alla riga 138:
... 138: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table + " #orderBy#"; ...Il file dei partecipanti per il comportamento server MyDynamicTable ColdFusion include un partecipante aggiuntivo che aggiunge la definizione della variabile "orderBy" alla pagina:
<group name="MyDynamicTable" version="9.0"> <groupParticipants> <groupParticipant name="MyDynamicTable_orderBy" /> <groupParticipant name="Recordset_main" /> <groupParticipant name="DynamicTable_main" /> <groupParticipant name="RepeatedRegion_pageNum" /> <groupParticipant name="RepeatedRegion_maxRows" /> <groupParticipant name="RepeatedRegion_startRow" /> <groupParticipant name="RepeatedRegion_endRow" /> <groupParticipant name="RepeatedRegion_totalPages" /> </groupParticipants> </group>PHP MySQL
L'ultimo modello server nell'estensione è PHP_MySQL. La modifica pertinente nel file "[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js" nella Cartella di configurazione dell'utente è alla riga 122:
... 122: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table+ " \" . $orderBy . \""; ...Il file dei partecipanti per il comportamento server MyDynamicTable PHP MySQL include un partecipante aggiuntivo che aggiunge la definizione della variabile "orderBy" alla pagina:
<group name="MyDynamicTable" version="9.0"> <groupParticipants> <groupParticipant name="Connection_include" /> <groupParticipant name="MyDynamicTable_orderBy" /> <groupParticipant name="EditOps_SQLValueString" /> <groupParticipant name="Recordset_main" /> <groupParticipant name="DynamicTable_main" /> <groupParticipant name="Recordset_close" /> </groupParticipants> </group>Pro
Il metodo sopra descritto presenta diversi vantaggi, tra cui:
- Gli sviluppatori più esperti possono sfruttare la vulnerabilità a SQL Injection per costruire in modo dinamico SQL più complessi.
- I recordset creati saranno comunque modificabili da Dreamweaver.
- Non è difficile eseguire l'integrazione con le estensioni Dreamweaver esistenti che generano recordset con parametri SQL dinamici.
- Il codice predefinito generato da Dreamweaver rimane invariato; gli sviluppatori che non richiedono query dinamiche (e i loro clienti) possono essere certi che il codice scritto da Dreamweaver è sicuro.
Contro
Poiché Dreamweaver non è stato progettato per supportare questo metodo per impostazione predefinita, presenta anche alcuni svantaggi, tra cui il fatto che
- il codice generato con il metodo descritto in questo documento è vulnerabile a SQL Injection e deve pertanto essere protetto in altri modi. L'elenco di tali metodi di protezione comprende, tra l'altro:
- Verifica che l'utente finale non possa utilizzare il metodo SQL Injection per includere la pagina web e/o il database;
- Esegui l'escape degli argomenti prima di raggiungere l'SQL effettivo;
- Assicurati che anche se l'utente riesce a violare il codice, non ruberà informazioni riservate e/o non comprometterà il database.
- Due problemi noti che non possono essere risolti fino al rilascio della versione successiva di Dreamweaver:
- ColdFusion: l'interfaccia utente recordset semplice rimuove gli hash attorno ai nomi delle variabili dalla query SQL quando vengono modificati e quindi riapplicati
- ColdFusion, ASP_VBS, ASP_JS: il pulsante di test dell'interfaccia utente recordset avanzata non funziona quando la query SQL contiene nomi di variabili
Individuazione della cartella di configurazione dell'utente
- Windows XP:
C:\Documents and Settings\[nome utente]\Application Data\Adobe\Dreamweaver 9\Configuration
- Windows Vista
C:\Users\[nome utente]\AppData\Roaming\Adobe\Dreamweaver 9\Configuration
- Mac OS 10.4.x
/Users/[nome utente]/Library/Application\ Support/Adobe/Dreamweaver\ 9/Configuration