Inhoud
Kwetsbaarheden in de databasebeveiliging aanpakken
Ter verbetering van de beveiliging in Dreamweaver 8.0.2 en CS3 hebben we de mogelijkheid verwijderd om dynamische SQL-parameters te gebruiken in standaardrecordsets. De oorspronkelijke code stelde databases bloot aan SQL-injecties. Met het gebruik van voorbereide instructies voor ASP_VBS- en ColdFusion-servermodellen werd het beveiligingslek verholpen. Het was nodig om een functie aan de pagina toe te voegen waar de eerste recordset wordt toegepast en die functie vervolgens vanuit alle recordsets op de pagina aan te roepen voor PHP_MySQL. Deze techniek is vergelijkbaar met de voorbereide instructies van ASP_VBS en ColdFusion.
In een poging om het beveiligingslek te dichten, hebben we enige flexibiliteit moeten inruilen voor beveiliging. We willen ontwikkelaars die al extensies hebben of van plan zijn extensies te ontwikkelen die dynamische SQL-queries moeten verwerken, evenals eindgebruikers die hun recordsets willen aanpassen om dynamische parameters te accepteren, de mogelijkheid bieden om deze aangepaste recordsets vanuit Dreamweaver te bewerken.
SQL-injecties en preventiemethoden
Een SQL-injectie is een techniek die misbruik maakt van een beveiligingslek in de databaselaag van een toepassing. Het beveiligingslek is aanwezig wanneer gebruikersinvoer ofwel onjuist is gefilterd op letterlijke escape-tekenreeksen die zijn ingesloten in SQL-instructies of gebruikersinvoer niet sterk wordt getypt en daardoor onverwacht wordt uitgevoerd. Dit is in feite een meer algemene klasse van kwetsbaarheden die kunnen optreden wanneer een programmeer- of scripttaal in een andere is ingebed. Ga naar Wikipedia voor meer informatie.
Voorbeeld van standaard SQL-query:
SELECT * FROM company_com WHERE name_com = '$companyName'
In het bovenstaande voorbeeld wordt de variabele $companyName gelezen uit een invoervormveld, zodat de gebruiker de volledige controle heeft over welke waarde wordt ingediend. In het gunstigste geval, wanneer de gebruiker "Adobe" invoert (zonder de aanhalingstekens) in de invoer die overeenkomt met de bedrijfsnaam, ziet de gegeven SQL-query er als volgt uit:
SELECT * FROM company_com WHERE name_com = 'Adobe'
Maar er zijn gevallen waarin gebruikers de website willen breken en gevoelige informatie willen stelen en/of de database willen vernietigen. Gezien het bovenstaande voorbeeld, is een SQL-injectie gemakkelijk te realiseren door de reeks "blabla' OF '1'= '1'' in te voeren (zonder de aanhalingstekens eromheen). Door deze reeks tekens te gebruiken, heeft de gebruiker toegang tot alle bedrijven in plaats van tot één bedrijf. De feitelijke SQL die wordt uitgevoerd is:
SELECT * FROM company_com WHERE name_com = 'blabla' OR '1'='1'
Bovendien, als de gebruiker de volledige tabel company_com wil verwijderen, kan hij/zij dit doen door simpelweg de volgende string "x '; DROP TABLE company_com; --" door te geven (zonder de omringende aanhalingstekens). Hier is de volledige SQL-query:
SELECT * FROM company_com WHERE name_com = 'x'; DROP TABLE company_com; --'
Gebruik een van de volgende maatregelen om uw website te beschermen tegen SQL-injecties:
- Beperk de invoer tot een minimum aantal toegestane tekens door automatisch alle andere tekens te verwijderen die buiten het opgegeven bereik vallen.
- Escape alle tekens die problemen kunnen veroorzaken bij gebruik binnen een SQL-query (bijvoorbeeld enkele aanhalingstekens).
- Gebruik voorbereide instructies.
- Gebruik toegangsrechten voor databases.
- Gebruik opgeslagen procedures.
We raden u aan voorbereide instructies te gebruiken, die in meer detail worden beschreven Voorbereide instructies gebruiken. Dreamweaver 8.0.2 en CS3 gebruiken de benadering van voorbereide instructies voor ASP_VBS-, ASP_JavaScript-, Cold Fusion- en JSP-servermodellen, en voeren een escape uit op de invoerbenadering van de gebruiker voor het PHP_MySQL-servermodel. Raadpleeg Voorbeelden van SQL-injectieaanvallen voor aanvullende informatie over SQL-injectieaanvallen.
Voorbereide instructies gebruiken
Dreamweaver 8.0.2 en CS gebruiken voorbereide instructies, omdat deze oplossing ons garandeert dat de vervanging van de werkelijke SQL-parameters door opgegeven waarden (mogelijk verzonden via URL) op de server wordt uitgevoerd en niet op de pagina. Voorbereide instructies garanderen ook dat de waarde die aan de SQL zelf wordt doorgegeven, het juiste gegevenstype heeft en de juiste escaping gebruikt (als wordt gezegd dat een parameter van het type int is, kan de gebruiker geen letter indienen of als een parameter van het type tekst is, wordt een juiste escape uitgevoerd op de hele doorgegeven reeks en krijgt de gebruiker niet de kans om een SQL-injectie uit te voeren).
Voorbereide instructies voor de ASP_VBS- en ASP_JS-servermodellen
Voor ASP_VBS en ASP_JS vertrouwen we op de ADODB-databaselaag. Het volgende voorbeeld toont een eenvoudige ASP_VBS-recordset die een dynamische SQL-parameter gebruikt om resultaten te filteren:
... <% 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 %> ...Voorbereide instructies voor het ColdFusion-servermodel
ColdFusion biedt ingebouwde ondersteuning voor voorbereide instructies. Het volgende voorbeeld toont een eenvoudige ColdFusion-recordset die een dynamische SQL-parameter gebruikt om resultaten te filteren:
... <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> ...Gesimuleerde voorbereide instructies voor het PHP_MySQL-servermodel
Voorbereide instructies voor PHP waren beschikbaar vanaf MySQL 4.1, maar omdat we ook eerdere versies van MySQL wilden ondersteunen, hebben we besloten om een aangepaste functie te implementeren die vrijwel hetzelfde doet. Het volgende voorbeeld toont een eenvoudige PHP-recordset die een dynamische SQL-parameter gebruikt om resultaten te filteren:
... <?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); ?> ...Dynamische parameters in standaardrecordsets toestaan
Dus hoe kunnen we ons beschermen tegen SQL-injecties terwijl we toch dynamische parameters toestaan en de resulterende code bewerkbaar houden via het venster Servergedrag in Dreamweaver? De oplossing is om de dynamische parameters in de SQL-query zelf in te voegen in plaats van ze door te geven als klassieke SQL-parameters (waarop escaping zou worden toegepast of die aan de SQL zouden worden toegevoegd via voorbereide instructies).
Belangrijk: Gebruikers kunnen dergelijke code niet genereren met de Objecten en Servergedrag die bij Dreamweaver 8.0.2 of CS3 worden geleverd (bijvoorbeeld: een recordset invoegen via het venster Servergedrag of de invoegbalk). Voor deze oplossing moet de gebruiker de code handmatig bewerken of een extensie van externe ontwikkelaar installeren die een dergelijke code maakt. De verantwoordelijkheid voor het schrijven van veilige code ligt bij de gebruiker of de externe softwareontwikkelaar.
Het doel van deze methode is om ervaren ontwikkelaars dynamische SQL-queries te laten maken. Misbruik van deze oplossing geeft een hacker de mogelijkheid om ongewenste machtigingen te krijgen en/of de webpagina's en database te breken. Raadpleeg SQL-injecties en preventiemethoden om het risico op SQL-injecties te minimaliseren.
De belangrijkste gevallen waarin we deze oplossing zouden gebruiken zijn:
- Handmatig een SQL-query maken/bijwerken om dynamische parameters te accepteren.
- Bestaande extensies maken/upgraden om dynamische parameters te genereren.
Beide toepassingen worden in de volgende paragrafen beschreven.
Handmatig een SQL-query maken/bijwerken om dynamische parameters te accepteren terwijl de Recordset nog steeds vanuit Dreamweaver bewerkbaar blijft
In het volgende voorbeeld wil de gebruiker sorteerfunctionaliteit toevoegen aan een dynamische tabel. Hij besluit de pagina opnieuw te laden terwijl hij via de URL de bestanden en de sorteerrichting (oplopend of aflopend) doorgeeft die moeten worden gebruikt. Een voorbeeld van een eerste URL is:
http://www.mydomain.com/index.php?sortCol=name_com&sortDir=ascHandmatige codering is vereist om deze taak in Dreamweaver uit te voeren met behulp van Servergedrag. De volgende codevoorbeelden tonen de wijzigingen die voor elk servermodel moeten worden aangebracht.
Belangrijk: Houd er rekening mee dat geen van de onderstaande voorbeelden validering heeft tegen SQL-injecties. De voorbeelden zijn uitsluitend informatief bedoeld en mogen in die staat niet voor productie worden gebruikt. De voorbeelden zijn niet betrouwbaar, om ze zo eenvoudig mogelijk te houden. U moet beschermen tegen SQL-injecties. Raadpleeg SQL-injecties en preventiemethoden voor aanvullende informatie.
ASP_VBS
Voor:
... <% 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 %> ...Na:
... <% 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
Voor:
... <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com </cfquery> ...Na:
... <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
Voor:
... <?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); ?> ...Na:
... <?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); ?> ...Maak/upgrade bestaande extensies om dynamische parameters te genereren terwijl u de Recordset nog steeds bewerkbaar houdt vanuit Dreamweaver
Bekijk nu hetzelfde scenario als hierboven, behalve dat de gebruiker een extensie wil maken die de juiste code zal genereren in plaats van te vertrouwen op een combinatie van ingebouwde functionaliteit en handmatige bewerking. De nieuwe extensie moet de juiste code genereren voor de servermodellen ASP_VBS, ColdFusion en PHP_MySQL.
Belangrijk: In de volgende secties richten we ons alleen op het genereren van code die de waarden uit de URL-parameters gebruikt om een dynamische SQL-query te maken. Het belangrijkste doel is het genereren van de code zodat deze nog steeds wordt herkend door Dreamweaver en kan worden bewerkt vanuit standaard Dreamweaver-interfaces. Deze tutorial behandelt niet de validatie van invoergegevens (via URL-parameters) en de bescherming van de uiteindelijke SQL tegen SQL-injectiepogingen, omdat de complexiteit van dergelijke code buiten het bereik van deze tutorial valt. De ontwikkelaar is als enige verantwoordelijk voor de bescherming van de uiteindelijke SQL tegen SQL-injecties.
We bieden een demo-extensie die een verbinding en een tabel als invoer neemt en een dynamische tabel genereert die alle records weergeeft. De extensie werkt ook de gegenereerde SQL bij zodat deze de variabele orderBy bevat, zoals weergegeven in de bovenstaande voorbeelden. De extensie is ontworpen voor Dreamweaver 8.0.2 en CS3. De gegenereerde code is niet onkwetsbaar, aangezien het doel is om recordsets te maken met dynamische parameters die bewerkbaar blijven met standaard Dreamweaver-interfaces. De broncode van de extensie bevindt zich in de configuratiemap van de gebruiker.
In alle onderstaande codevoorbeelden zijn de gemarkeerde secties toegevoegd om de sorteerfunctionaliteit via de URL-parameters sortCol en sortDir mogelijk te maken.
ASP_VBS
De code die de juiste SQL-query voor het ASP_VBS-servermodel genereert, bevindt zich in het bestand "[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js" in de configuratiemap van de gebruiker. De opmerkelijke wijziging is op regel 130:
... 130: paramObj.encodedSQL = "SELECT * FROM " + paramObj.table + " \" & orderBy & \""; ...Het deelnemersbestand voor het MyDynamicTable ASP_VBS-servergedrag bevat een extra deelnemer die de variabele-definitie "orderBy" aan de pagina toevoegt:
<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
Dezelfde "[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js" in de Gebruikersconfiguratiemap bevat ook de code voor het ColdFusion-servermodel. De relevante wijziging in dit geval is op regel 138:
... 138: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table + " #orderBy#"; ...Het deelnemersbestand voor het MyDynamicTable ColdFusion-servergedrag bevat een extra deelnemer die de variabele-definitie "orderBy" aan de pagina toevoegt:
<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
Het laatste servermodel in de extensie is PHP_MySQL. De relevante wijziging in het bestand "[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js" in de Gebruikersconfiguratiemap is op regel 122:
... 122: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table+ " \" . $orderBy . \""; ...Het deelnemersbestand voor het MyDynamicTable PHP_MySQL-servergedrag bevat een extra deelnemer die de variabele-definitie "orderBy" aan de pagina toevoegt:
<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>Voordelen
De hierboven beschreven methode heeft verschillende voordelen, waaronder:
- De meer gevorderde ontwikkelaars kunnen profiteren van de kwetsbaarheid met betrekking tot SQL-injectie om dynamisch complexere SQL's te bouwen.
- De gemaakte recordsets kunnen nog steeds worden bewerkt vanuit Dreamweaver.
- Het is niet moeilijk te integreren in bestaande Dreamweaver-extensies die recordsets genereren met dynamische SQL-parameters.
- De door Dreamweaver gegenereerde standaardcode blijft ongewijzigd. Ontwikkelaars die geen dynamische query's (en hun cliënten) nodig hebben, kunnen er zeker van zijn dat de door Dreamweaver geschreven code veilig is.
Nadelen
Aangezien Dreamweaver niet standaard is ontworpen om deze methode te ondersteunen, heeft het ook enkele nadelen, waaronder:
- De code die is gegenereerd met de methode die in dit document wordt beschreven, is kwetsbaar voor SQL-injecties en moet daarom op andere manieren worden beschermd. De lijst met dergelijke beschermingsmethoden omvat (maar is niet beperkt tot):
- Ervoor zorgen dat de eindgebruiker de SQL-injectiemethode niet kan gebruiken om de webpagina en/of database op te nemen;
- Een escape uitvoeren op de argumenten voordat ze de daadwerkelijke SQL bereiken;
- Ervoor zorgen dat zelfs als de gebruiker erin slaagt uw code te kraken, hij/zij geen gevoelige informatie zal stelen en/of de database niet zal opnemen.
- Twee bekende problemen die in de volgende versie van Dreamweaver zijn opgelost:
- ColdFusion: in de Simple Recordset-gebruikersinterface worden de hashes rond variabelenamen uit de SQL-query verwijderd wanneer deze wordt bewerkt en vervolgens opnieuw wordt toegepast
- ColdFusion, ASP_VBS, ASP_JS: testknop van de Advanced Recordset-gebruikersinterface werkt niet wanneer de SQL-query variabelenamen bevat
De locatie van de configuratiemap van de gebruiker
- Windows XP
C:\Documents and Settings\[gebruikersnaam]\Application Data\Adobe\Dreamweaver 9\Configuration
- Windows Vista
C:\Gebruikers\[gebruikersnaam]\AppData\Roaming\Adobe\Dreamweaver 9\Configuration
- Mac OS 10.4.x
/Users/[gebruikersnaam]/Library/Application\ Support/Adobe/Dreamweaver\ 9/Configuration