Utilisation de paramètres Dynamic SQL dans des jeux d’enregistrements standard dans Dreamweaver 8.0.2 et CS3

Sommaire

Correction de la vulnérabilité de sécurité de la base de données

Pour renforcer la sécurité dans Dreamweaver 8.0.2 et CS3, nous avons supprimé la possibilité d’utiliser des paramètres SQL dynamiques dans les jeux d’enregistrements standard. Le code d’origine exposait les bases de données à des injections de code SQL. L’utilisation d’instructions préparées pour les modèles de serveur ASP_VBS et ColdFusion a résolu la vulnérabilité de sécurité. Il était nécessaire d’ajouter une fonction à la page lors de l’application du premier jeu d’enregistrements, puis d’appeler cette fonction à partir de tous les jeux d’enregistrements de la page pour PHP_MySQL. Cette technique est similaire aux instructions préparées pour ASP_VBS et ColdFusion.

Dans le but de réduire la vulnérabilité en matière de sécurité, nous avons privilégié la sécurité au détriment d’une certaine flexibilité. Nous voulons donner aux développeurs qui ont déjà développé ou prévoient de développer des extensions qui devront gérer des requêtes SQL dynamiques, ainsi qu’aux utilisateurs finaux qui souhaitent personnaliser leurs jeux d’enregistrements afin qu’ils acceptent des paramètres dynamiques, la possibilité de modifier ces jeux d’enregistrements personnalisés à partir de Dreamweaver.

Injections de code SQL et méthodes de prévention

Une injection de code SQL est une technique d’injection de code qui exploite une vulnérabilité de sécurité se produisant dans la couche de base de données d’une application. La vulnérabilité est présente lorsque l’entrée utilisateur n’est pas correctement filtrée pour des caractères d’échappement littéraux de chaîne intégrés dans des instructions SQL ou lorsque l’entrée utilisateur n’est pas fortement typée et donc exécutée de manière inattendue. Il s’agit en fait d’une instance d’une classe plus générale de vulnérabilités pouvant se produire lorsqu’un langage de programmation ou de script est intégré dans un autre. Pour plus d’informations, consultez Wikipedia.

Exemple de requête SQL standard :

SELECT * FROM company_com WHERE name_com = '$companyName'

Dans l’exemple ci-dessus, la variable $companyName est lue à partir d’un champ de formulaire d’entrée, de sorte que l’utilisateur dispose du contrôle total de la valeur qui sera envoyée. Dans le meilleur des cas, lorsque l’utilisateur saisit « Adobe » (sans les guillemets) dans l’entrée correspondant au nom de la société, la requête SQL aura l’aspect suivant :

SELECT * FROM company_com WHERE name_com = 'Adobe'

Toutefois, dans certains cas, des utilisateurs peuvent vouloir compromettre le site web et voler des informations sensibles et/ou détruire la base de données. En prenant l’exemple ci-dessus, une injection de code SQL sera facile à réaliser en saisissant la chaîne suivante « blabla' OR '1'='1 » (sans les guillemets). Avec cette séquence de caractères, l’utilisateur aura accès à toutes les entreprises au lieu d’une seule. Le code SQL exécuté est :

SELECT * FROM company_com WHERE name_com = 'blabla' OR '1'='1'

En outre, si l’utilisateur souhaite supprimer l’intégralité de la table company_com, il lui suffit de transmettre la chaîne suivante « x'; DROP TABLE company_com; -- » (sans les guillemets). Voici la requête SQL complète :

SELECT * FROM company_com WHERE name_com = 'x'; DROP TABLE company_com; --'

Utilisez l’une des mesures suivantes pour protéger votre site web contre les injections de code SQL :

  • Restreignez la saisie à un ensemble minimal de caractères autorisés en supprimant automatiquement tous les autres caractères qui se trouvent en dehors de la plage donnée.
  • Échappez tous les caractères susceptibles de poser problème lorsqu’ils sont utilisés dans une requête SQL (par exemple, les guillemets simples).
  • Utilisez des instructions préparées.
  • Utilisez des droits d’accès à la base de données.
  • Utilisez des procédures stockées.

Nous recommandons d’utiliser des instructions préparées, décrites plus en détail dans la section Utilisation d’instructions préparées. Dreamweaver 8.0.2 et CS3 utilisent l’approche des instructions préparées pour les modèles de serveur ASP_VBS, ASP_JavaScript, Cold Fusion et JSP, ainsi que l’approche d’échappement des entrées de l’utilisateur pour le modèle de serveur PHP_MySQL. Pour plus d’informations sur les attaques par injection de code SQL, reportez-vous à la page SQL Injection Attacks by Example (Attaques par injection SQL par exemple).

Utilisation d’instructions préparées

Dreamweaver 8.0.2 et CS utilisent des instructions préparées car cette solution nous garantit que le remplacement des paramètres SQL réels par des valeurs données (qui peuvent être envoyées via URL) sera effectué sur le serveur et non sur la page. Les instructions préparées garantissent également que la valeur transmise au SQL lui-même possède le type de données approprié et utilise l’échappement approprié (si un paramètre est de type int, alors l’utilisateur ne pourra pas envoyer de lettre, ou si un paramètre est de type text, alors la chaîne passée entière sera correctement échappée et l’utilisateur n’aura pas la possibilité d’effectuer une injection de code SQL).

Instructions préparées pour les modèles de serveur ASP_VBS et ASP_JS

Pour ASP_VBS et ASP_JS, nous utilisons la couche de base de données ADODB. L’exemple suivant présente un jeu d’enregistrements ASP_VBS simple qui utilise un paramètre SQL dynamique pour filtrer les résultats :

... <% 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 %> ...

Instructions préparées pour le modèle de serveur ColdFusion

ColdFusion offre une prise en charge intégrée des instructions préparées. L’exemple suivant présente un jeu d’enregistrements ColdFusion simple qui utilise un paramètre SQL dynamique pour filtrer les résultats :

... <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> ...

Instructions préparées simulées pour le modèle de serveur PHP_MySQL

Les instructions préparées pour PHP sont disponibles depuis la version MySQL 4.1, mais nous souhaitons également prendre en charge les versions précédentes de MySQL, c’est pourquoi nous avons décidé d’implémenter une fonction personnalisée qui a presque le même effet. L’exemple suivant présente un jeu d’enregistrements PHP simple qui utilise un paramètre SQL dynamique pour filtrer les résultats :

... <?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); ?> ...

Autorisation des paramètres dynamiques dans les jeux d’enregistrements standard

Comment pouvons-nous donc assurer la protection contre les injections de code SQL tout en autorisant les paramètres dynamiques et en assurant que le code résultant reste modifiable via le panneau Comportements de serveur de Dreamweaver ? La solution consiste à insérer les paramètres dynamiques dans la requête SQL elle-même, plutôt que de les transmettre en tant que paramètres SQL classiques (qui seraient soit échappés, soit ajoutés au SQL via des instructions préparées).

Important : les utilisateurs ne peuvent pas générer ce type de code à l’aide des objets et des comportements de serveur fournis avec Dreamweaver 8.0.2 ou CS3 (exemple : insertion d’un jeu d’enregistrements via le panneau Comportements de serveur ou la barre Insertion). Cette solution nécessite que l’utilisateur modifie le code manuellement ou installe une extension tierce qui crée ce code. La responsabilité de rédiger un code sécurisé incombe à l’utilisateur ou au développeur du logiciel tiers.

Cette méthode a pour but de permettre aux développeurs expérimentés de créer des requêtes SQL dynamiques. Une mauvaise utilisation de cette solution donne aux pirates la possibilité d’obtenir des privilèges indésirables et/ou de compromettre les pages web et la base de données. Veuillez vous référer à la section Injections de code SQL et méthodes de prévention pour minimiser les risques d’injections de code SQL.

Nous avons déterminé que cette solution serait utilisée principalement dans les cas suivants :

  • Création/mise à jour manuelle d’une requête SQL pour accepter des paramètres dynamiques.
  • Création/mise à niveau des extensions existantes pour générer des paramètres dynamiques.

Les deux cas d’utilisation seront détaillés dans les paragraphes suivants.

Création/mise à jour manuelle d’une requête SQL pour accepter des paramètres dynamiques tout en conservant le jeu d’enregistrements modifiable depuis Dreamweaver

Dans l’exemple suivant, l’utilisateur souhaite ajouter une fonctionnalité de tri à une table dynamique. Il décide de recharger la page tout en transmettant les fichiers et le sens de tri à utiliser (croissant ou décroissant) dans l’URL. Voici un exemple d’URL initiale :

http://www.mydomain.com/index.php?sortCol=name_com&sortDir=asc

Le codage manuel est nécessaire pour accomplir cette tâche dans Dreamweaver à l’aide du panneau Comportements de serveur. Les extraits de code suivants présentent les modifications qui doivent être apportées à chaque modèle de serveur.

Important : veuillez noter qu’aucun des exemples ci-dessous n’a de validation contre les injections de code SQL. Leur rôle est purement éducatif et ils ne doivent pas être utilisés en l’état dans la production. Les exemples sont aussi simples que possible, ils ne disposent donc pas d’une sécurité optimale. Vous devez mettre en place une protection contre les injections de code SQL. Pour plus d’informations, reportez-vous à la section Injections de code SQL et méthodes de prévention.

ASP_VBS

Avant :

... <% 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 %> ...

Après :

... <% 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

Avant :

... <cfquery name="Recordset1" datasource="company_employee"> SELECT * FROM company_com </cfquery> ...

Après :

... <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

Avant :

... <?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); ?> ...

Après :

... <?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); ?> ...

Création/mise à niveau des extensions existantes pour générer des paramètres dynamiques tout en assurant que le jeu d’enregistrements reste modifiable depuis Dreamweaver

Considérez maintenant le même scénario que ci-dessus, sauf que l’utilisateur souhaite créer une extension qui générera le code approprié plutôt que de compter sur une combinaison de fonctionnalités intégrées et de modification manuelle. La nouvelle extension doit générer le code correct pour les modèles de serveur ASP_VBS, ColdFusion et PHP_MySQL.

Important : dans les sections suivantes, nous allons uniquement nous concentrer sur la génération du code qui utilisera les valeurs des paramètres d’URL pour créer une requête SQL dynamique. L’objectif principal est de générer le code de manière à ce qu’il soit toujours reconnu par Dreamweaver et modifiable à partir d’interfaces Dreamweaver standard. Ce tutoriel ne prend pas en charge la validation des données d’entrée (via les paramètres d’URL) et ne protège pas le code SQL final contre toute tentative d’injection de code SQL, car la complexité de ce type de code n’entre pas dans le cadre de ce tutoriel. Le développeur est seul responsable de la protection du code SQL final contre les injections de code SQL.

Nous fournissons une extension de démonstration qui prend une connexion et une table comme entrées et génère une table dynamique affichant tous les enregistrements. Elle met également à jour le code SQL généré afin de contenir la variable orderBy, comme indiqué dans les exemples ci-dessus. L’extension a été conçue pour Dreamweaver 8.0.2 et CS3. Le code généré ne dispose pas d’une sécurité optimale, car l’objectif est de créer des jeux d’enregistrements contenant des paramètres dynamiques qui restent modifiables lors de l’utilisation d’interfaces Dreamweaver standard. Le code source de l’extension se trouve dans le dossier Configuration de l’utilisateur.

Dans tous les exemples de code ci-dessous, les sections mises en surbrillance ont été ajoutées pour activer la fonctionnalité de tri via les paramètres d’URL sortCol et sortDir.

ASP_VBS

Le code qui génère la requête SQL appropriée pour le modèle de serveur ASP_VBS se trouve dans le fichier « [DOSSIER_CONFIGURATION_DE_L’UTILISATEUR]/Commands/My Dynamic Table.js » dans le dossier Configuration de l’utilisateur. Le changement notable se trouve à la ligne 130 :

... 130: paramObj.encodedSQL = "SELECT * FROM " + paramObj.table + " \" & orderBy & \""; ...

Le fichier des participants pour le comportement de serveur ASP_VBS MyDynamicTable comprend un participant supplémentaire qui ajoute la définition de variable « orderBy » à la page :

<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

Le même fichier « [DOSSIER_CONFIGURATION_DE_L’UTILISATEUR]/Commands/My Dynamic Table.js » dans le dossier Configuration de l’utilisateur contient également le code du modèle de serveur ColdFusion. Dans ce cas, le changement significatif se trouve à la ligne 138 :

... 138: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table + " #orderBy#"; ...

Le fichier des participants pour le comportement de serveur ColdFusion MyDynamicTable comprend un participant supplémentaire qui ajoute la définition de variable « orderBy » à la page :

<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

Le dernier modèle de serveur de l’extension est PHP_MySQL. La modification significative du fichier « [DOSSIER_CONFIGURATION_DE_L’UTILISATEUR]/Commands/My Dynamic Table.js » dans le dossier Configuration de l’utilisateur se trouve à la ligne 122 :

... 122: paramObj.SQLStatement = "SELECT * FROM " + paramObj.table+ " \" . $orderBy . \""; ...

Le fichier des participants pour le comportement de serveur PHP_MySQL MyDynamicTable comprend un participant supplémentaire qui ajoute la définition de variable « orderBy » à la page :

<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>

Avantages

La méthode décrite ci-dessus présente plusieurs avantages, notamment :

  • Les développeurs les plus expérimentés peuvent tirer parti de la vulnérabilité aux injections de code SQL pour construire des SQL plus complexes, de manière dynamique.
  • Les jeux d’enregistrements créés seront toujours modifiables dans Dreamweaver.
  • Il n’est pas difficile de procéder à l’intégration dans les extensions Dreamweaver existantes qui génèrent des jeux d’enregistrements avec des paramètres SQL dynamiques.
  • Le code par défaut généré par Dreamweaver reste inchangé. Les développeurs qui n’ont pas besoin de requêtes dynamiques (et leurs clients) peuvent être assurés que le code écrit par Dreamweaver est sécurisé.

Inconvénients

Dreamweaver n’a pas été conçu par défaut pour prendre en charge cette méthode et présente donc également quelques inconvénients, notamment :

  • Le code généré à l’aide de la méthode décrite dans ce document est vulnérable aux injections de code SQL et doit donc être protégé d’autres manières. Vous pouvez utiliser certaines méthodes de protection, notamment :
    • Assurez-vous que l’utilisateur final ne peut pas utiliser la méthode d’injection de code SQL pour compromettre la page web et/ou la base de données ;
    • Échappez les arguments avant qu’ils n’atteignent le SQL réel.
    • Assurez-vous que même si l’utilisateur réussit à casser votre code, il ne volera pas les informations sensibles et/ou ne compromettra pas la base de données.
  • Deux problèmes connus ne peuvent pas être résolus avant la publication de la prochaine version de Dreamweaver :
    • ColdFusion : l’IU des jeux d’enregistrements simples supprime les hachages autour des noms de variables de la requête SQL lors de la modification, puis de la réapplication.
    • ColdFusion, ASP_VBS, ASP_JS : le bouton Tester de l’IU des jeux d’enregistrements avancés ne fonctionne pas lorsque la requête SQL contient des noms de variables

Localisation du dossier Configuration de l’utilisateur

  • Windows XP :

    C:\Documents and Settings\[nom d’utilisateur]\Application Data\Adobe\Dreamweaver 9\Configuration
  • Windows Vista :

    C:\Users\[nom d’utilisateur]\AppData\Roaming\Adobe\Dreamweaver 9\Configuration
  • Mac OS 10.4.x :

    /Users/[nom d’utilisateur]/Library/Application\ Support/Adobe/Dreamweaver\ 9/Configuration
Logo Adobe

Accéder à votre compte