Vad som finns i avsnittet
Åtgärda sårbarhet i databasens säkerhet
För att öka säkerheten i Dreamweaver 8.0.2 och CS3 har vi tagit bort möjligheten att använda dynamiska SQL-parametrar i standardpostuppsättningar. Den ursprungliga koden exponerade databaser för SQL-injektioner. Sårbarheten i säkerheten har åtgärdats genom användning av förberedda instruktioner för ASP_VBS- och ColdFusion-servermodeller. Det var nödvändigt att lägga till en funktion på sidan när den första postuppsättningen tillämpas och sedan anropa den funktionen från alla postuppsättningar på sidan för PHP_MySQL. Tekniken liknar de förberedda instruktionerna från ASP_VBS och ColdFusion.
I försöken att täppa till sårbarheten i säkerheten fick vi kompromissa med flexibiliteten för att öka säkerheten. Vi vill ge utvecklare som redan har eller planerar att utveckla tillägg som kommer att behöva hantera dynamiska SQL-frågor, såväl som slutanvändare som vill anpassa sina postuppsättningar för att acceptera dynamiska parametrar, möjligheten att redigera de anpassade postuppsättningarna från Dreamweaver.
SQL-injektioner och förebyggande metoder
En SQL-injektion är en teknik som utnyttjar en säkerhetssårbarhet som förekommer i databasskiktet i ett program. Sårbarheten finns när användarinmatningen antingen är felaktigt filtrerad för strängkonstanta avbrottstecken som är inbäddade i SQL-instruktioner eller om användarinmatningen inte är starkt typifierad och därmed oväntat exekveras. Det är i själva verket ett exempel på en mer allmän klass av sårbarheter som kan uppstå när ett programmerings- eller skriptspråk är inbäddat i ett annat. Gå till Wikipedia för ytterligare information.
Exempel på en standardmässig SQL-fråga:
SELECT * FROM company_com WHERE name_com = '$companyName'
I exemplet ovan läses variabeln $companyName in från ett inmatningsfält i formuläret, så användaren har fullständig kontroll över vilket värde som skickas. I bästa fall kommer SQL-frågan att se ut så här när användaren skriver ”Adobe” (utan citattecken) i de indata som motsvarar företagsnamnet:
SELECT * FROM company_com WHERE name_com = 'Adobe'
Men det finns fall då användare kanske vill knäcka webbplatsen och stjäla känslig information och/eller förstöra databasen. Med beaktande av exemplet ovan är en SQL-injektion lätt att uppnå genom att man anger följande sträng ”blabla’ OR '1’=’1” (utan de omgivande citattecknen). Genom att uppge den här teckensekvensen får användaren tillgång till alla företag i stället för ett enstaka företag. Den faktiska SQL som körs är:
SELECT * FROM company_com WHERE name_com = 'blabla' OR '1'='1'
Dessutom, om användaren vill ta bort hela company_com-tabellen, kan han/hon göra det genom att helt enkelt överföra följande sträng ”x'; DROP TABLE company_com; --” (utan de omgivande citattecknen). Här är den fullständiga SQL-frågan:
SELECT * FROM company_com WHERE name_com = 'x'; DROP TABLE company_com; --'
Använd en av följande åtgärder för att skydda webbplatsens från SQL-injektioner:
- Begränsa inmatningen till en minsta uppsättning tillåtna tecken genom att automatiskt ta bort alla tecken som faller utanför det givna intervallet.
- Undanta alla tecken som kan orsaka problem när de används i en SQL-fråga (till exempel enkla citattecken).
- Använd förberedda instruktioner.
- Använd åtkomsträttigheter till databasen.
- Använd lagrade procedurer.
Vi rekommenderar förberedda instruktioner som beskrivs mer detaljerat i Använda förberedda instruktioner. Dreamweaver 8.0.2 och CS3 använder förberedda instruktioner för ASP_VBS-, ASP_JavaScript-, Cold Fusion- och JSP-servermodellerna, och undantar användarens inmatningsmetod för PHP_MySQL-servermodellen. Se SQL-injektionsangrepp med exempel för ytterligare information om SQL-injektionsangrepp.
Använda förberedda instruktioner
Dreamweaver 8.0.2 och CS använder förberedda instruktioner eftersom den här lösningen garanterar oss att utbytandet av de faktiska SQL-parametrarna med givna värden (eventuellt skickade via URL) kommer att göras på servern och inte på sidan. Förberedda instruktioner garanterar också att själva värdet som skickas till SQL har rätt datatyp och använder lämpliga undantagstecken (om en parameter sägs vara av typen int, kommer användaren inte att kunna skicka en bokstav, eller om en parameter är av typen text kommer hela den överförda strängen att undantas och användaren har inte chans att utföra en SQL-injektion).
Förberedda instruktioner för ASP_VBS- och ASP_JS-servermodellerna
För ASP_VBS och ASP_JS förlitar vi oss på ADODB-databasskiktet. Följande exempel visar en enkel ASP_VBS-postuppsättning som använder en dynamisk SQL-parameter för att filtrera resultat:
... <% 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 %> ...Förberedda instruktioner för ColdFusion-servermodellerna
ColdFusion erbjuder inbyggt stöd för förberedda instruktioner. Följande exempel visar en enkel ColdFusion-postuppsättning som använder en dynamisk SQL-parameter för att filtrera resultat:
... <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> ...Förberedda instruktioner för ColdFusion-servermodellen
Förberedda instruktioner för PHP blev tillgängliga med MySQL 4.1, men eftersom vi även ville stödja tidigare versioner av MySQL beslutade vi oss för att implementera en anpassad funktion som gör praktiskt taget samma sak. Följande exempel visar en enkel PHP-postuppsättning som använder en dynamisk SQL-parameter för att filtrera resultat:
... <?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); ?> ...Tillåt dynamiska parametrar i standardpostuppsättningar
Hur kan vi ge skydd mot SQL-injektioner när vi fortfarande tillåter dynamiska parametrar och håller den resulterande koden redigerbar via panelen Serverbeteende i Dreamweaver? Lösningen är att infoga de dynamiska parametrarna i själva SQL-frågan, snarare än att överföra dem som klassiska SQL-parametrar (som antingen skulle undantas eller läggas till SQL via förberedda instruktioner).
Viktigt: Användare kan inte generera en sådan kod med hjälp av Objekt och Serverbeteenden som levereras med Dreamweaver 8.0.2 eller CS3 (exempel: Infoga en postuppsättning via panelen Serverbeteenden eller Infoga fält). Lösningen kräver att användaren redigerar koden manuellt eller installerar ett tredjepartstillägg som skapar en sådan kod. Det åligger användaren eller utvecklaren av tredjepartsprogramvaran att skriva säker kod.
Syftet med metoden är att låta erfarna utvecklare skapa dynamiska SQL-frågor. Missbruk av lösningen ger en hackare möjlighet att få oönskade privilegier och/eller knäcka webbsidorna och databasen. Se SQL-injektioner och förebyggande metoder för att minska risken för SQL-injektioner.
De huvudsakliga fall vi har identifierat där lösningen skulle användas är:
- Manuellt skapande/manuell uppdatering av en SQL-fråga så att den accepterar dynamiska parametrar.
- Skapande/uppgradering av befintliga tillägg för att generera dynamiska parametrar.
Båda användningsfallen beskrivs ingående i följande stycken.
Manuellt skapande/manuell uppdatering av en SQL-fråga så att den accepterar dynamiska parametrar samtidigt som postuppsättningen fortfarande är redigerbar i Dreamweaver
I följande exempel vill användaren lägga till sorteringsfunktioner i en dynamisk tabell. Han bestämmer sig för att ladda om sidan medan han skickar filerna och sorteringsriktningen som ska användas (stigande eller fallande) i URL:en. Ett första exempel på URL är:
http://www.mydomain.com/index.php?sortCol=name_com&sortDir=ascHandkodning krävs för att utföra uppgiften i Dreamweaver med hjälp av Serverbeteenden. Följande kodexempel visar de ändringar som måste göras för var och en av servermodellerna.
Viktigt: Observera att inget av exemplen nedan har validering mot SQL-injektioner. Deras roll är rent pedagogisk och de ska inte användas i produktion som de är. Exemplen är inte hundraprocentigt säkra eftersom vi vill hålla dem så enkla som möjligt. Du måste skydda mot SQL-injektioner. Se SQL-injektioner och förebyggande metoder för ytterligare information.
ASP_VBS
Före:
... <% 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 %> ...Efter:
... <% 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
Före:
... <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com </cfquery> ...Efter:
... <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
Före:
... <?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); ?> ...Efter:
... <?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); ?> ...Skapande/uppgradering av befintliga tillägg för att generera dynamiska parametrar samtidigt som postuppsättningen fortfarande är redigerbar i Dreamweaver
Tänk dig nu att det är samma scenario som ovan, förutom att användaren vill skapa ett tillägg som kommer att generera lämplig kod snarare än att förlita sig på en kombination av inbyggd funktionalitet och handredigering. Det nya tillägget bör generera rätt kod för ASP_VBS-, ColdFusion- och PHP_MySQL-servermodellerna.
Viktigt: I följande avsnitt fokuserar vi endast på att generera kod som använder värdena från URL-parametrarna för att skapa en dynamisk SQL-fråga. Huvudfokus är att generera koden så att den fortfarande blir igenkänd av Dreamweaver och kan redigeras inom Dreamweavers standardgränssnitt. Självstudierna tar inte upp valideringen av inmatningsdata (via URL-parametrar) och skyddar inte den slutliga SQL:en mot försök med SQL-injektioner eftersom komplexiteten i sådan kod faller utanför ramen för självstudierna. Utvecklaren är ensam ansvarig för att skydda den slutliga SQL:en mot SQL-injektioner.
Vi tillhandahåller ett demonstationstillägg som tar en anslutning och en tabell som inmatning och genererar en dynamisk tabell som visar alla poster. Det uppdaterar också den genererade SQL:en så att den innehåller variabeln orderBy som visas i exemplen ovan. Tillägget designades för Dreamweaver 8.0.2 och CS3. Den genererade koden är inte hundraprocentigt säker eftersom målet är att skapa postuppsättningar som innehåller dynamiska parametrar som förblir redigerbara med Dreamweavers standardgränssnitt. Källkoden för tillägget finns i Användarens konfigurationsmapp.
I samtliga kodexempel nedan lades de markerade avsnitten till för att aktivera sorteringsfunktionaliteten via URL-parametrarna sortCol och sortDir.
ASP_VBS
Koden som genererar lämplig SQL-fråga för ASP_VBS-servermodellen finns i filen ”[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js” i Användarkonfigurationsmapp. Den märkbara förändringen finns på rad 130:
... 130: paramObj.encodedSQL = "SELECT * FROM " + paramObj.table + " \" & orderBy & \""; ...Deltagarfilen för MyDynamicTable ASP_VBS-serverns beteende innehåller en extra deltagare som lägger till variabeldefinitionen ”orderBy” på sidan:
<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
Samma ”[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js” i Användarkonfigurationsmapp innehåller även koden för ColdFusion-servermodell; den relevanta förändringen i detta fall finns på rad 138:
... 138: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table + " #orderBy#"; ...Deltagarfilen för MyDynamicTable ColdFusion-serverns beteende innehåller en extra deltagare som lägger till variabeldefinitionen ”orderBy” på sidan:
<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
Den senaste servermodellen i tillägget är PHP_MySQL. Den relevanta förändringen i ”[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js” i Användarkonfigurationsmapp finns på rad 122:
... 122: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table+ " \" . $orderBy . \""; ...Deltagarfilen för MyDynamicTable PHP_MySQL-serverns beteende innehåller en extra deltagare som lägger till variabeldefinitionen ”orderBy” på sidan:
<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>Fördelar
Metoden som beskrivs ovan har flera fördelar, inklusive:
- Att de mer avancerade utvecklarna kan dra fördel av SQL-injektionssårbarheten för att konstruera mer komplexa SQL på ett dynamiskt sätt.
- Att de skapade postuppsättningarna fortfarande kan redigeras från Dreamweaver.
- Det är inte svårt att integrera i befintliga Dreamweaver-tillägg som genererar postuppsättningar med dynamiska SQL-parametrar.
- Standardkoden som genereras av Dreamweaver förblir oförändrad. Utvecklare som inte kräver dynamiska frågor (och deras kunder) kan vara säkra på att koden som är skriven av Dreamweaver är säker.
Nackdelar
Eftersom Dreamweaver inte utformades för att stödja den här metoden som standard har den också några nackdelar, inklusive:
- Koden som genereras med den metod som beskrivs i dokumentet är sårbar för SQL-injektioner och måste därför skyddas på andra sätt. Listan över sådana skyddsmetoder inbegriper men är inte begränsad till:
- Att se till att slutanvändaren inte kan använda SQL-injektionsmetoden till att omfatta webbsidan och/eller databasen;
- Att undanta argument innan de når den faktiska SQL;
- Se till att även om användaren lyckas knäcka koden kan han/hon inte stjäla känslig information och/eller omfatta databasen.
- Två kända fel som inte kan åtgärdas förrän nästa version av Dreamweaver släpps:
- ColdFusion: Enkelt användargränssnitt för postuppsättningen tar bort hasharna runt variabla namn från SQL-frågan när de redigeras och sedan appliceras på nytt
- ColdFusion, ASP_VBS, ASP_JS: Testknappen från Avancerat gränssnitt för postuppsättning fungerar inte när SQL-frågan innehåller variabla namn
Hitta användarens konfigurationsmapp
- Windows XP
C:\Dokument och inställningar\[användarnamn]\Programdata\Adobe\Dreamweaver 9\Konfiguration
- Windows Vista:
C:\Användare\[användarnamn]\AppData\Roaming\Adobe\Dreamweaver 9\Configuration
- MacOS 10.4.x
/Användare/[användarnamn]/Bibliotek/Program\ Support/Adobe/Dreamweaver\ 9/Konfiguration