Uso de parámetros dinámicos de SQL dentro de conjuntos de registros estándar en Dreamweaver 8.0.2 y CS3

Contenido

Solución de la vulnerabilidad de la seguridad de la base de datos

Para incrementar la seguridad en Dreamweaver 8.0.2 y CS3, hemos eliminado la posibilidad de utilizar parámetros SQL dinámicos dentro de conjuntos de registros estándar. El código original exponía las bases de datos a inyecciones de código SQL. El uso de instrucciones preparadas para los modelos de servidor ASP_VBS y ColdFusion ha solucionado la vulnerabilidad de seguridad. Era necesario añadir una función a la página cuando se aplica el primer conjunto de registros y luego se llama a esa función desde todos los conjuntos de registros de la página para PHP_MySQL. Esta técnica es similar a las instrucciones preparadas de ASP_VBS y ColdFusion.

Al intentar solucionar la vulnerabilidad de la seguridad, hemos renunciado a cierta flexibilidad en favor de la seguridad. Queremos ofrecer a los desarrolladores que ya tienen o prevén desarrollar extensiones que deberán manejar consultas SQL dinámicos, así como a los usuarios finales que quieren personalizar sus conjuntos de registros para aceptar parámetros dinámicos, la posibilidad de editar dichos conjuntos de registros personalizados desde Dreamweaver.

Inyecciones de código SQL y métodos de prevención

Una inyección de código SQL es una técnica que aprovecha una vulnerabilidad de seguridad que se produce en la capa de base de datos de una aplicación. La vulnerabilidad está presente cuando la entrada del usuario se filtra incorrectamente para los caracteres de escape literales de cadena incrustados en las instrucciones SQL o la entrada del usuario no se escribe con fuerza suficiente y, por lo tanto, se ejecuta inesperadamente. De hecho, es un ejemplo de una clase más general de vulnerabilidades que pueden producirse cuando un lenguaje de programación o de secuencia de comandos está incrustado dentro de otro. Para obtener más información, consulte la Wikipedia.

Ejemplo de consulta SQL estándar:

SELECT * FROM company_com WHERE name_com = '$companyName'

En el ejemplo anterior, la variable $companyName se lee desde un campo de formulario de entrada, por lo que el usuario tiene el control total del valor que se ejecutará. En el mejor de los casos, cuando el usuario indica Adobe (sin comillas) en la entrada correspondiente al nombre de la empresa, la consulta SQL dada tendrá el aspecto siguiente:

SELECT * FROM company_com WHERE name_com = 'Adobe'

Sin embargo, hay casos en que los usuarios pueden querer romper el sitio web y robar información confidencial o destruir la base de datos. Teniendo en cuenta el ejemplo anterior, una inyección de código SQL será fácil de lograr escribiendo la cadena: blabla 'OR' 1 '=' 1 (sin comillas de apertura ni cierre). Al indicar esta secuencia de caracteres, el usuario tendrá acceso a todas las empresas en lugar de a una sola. La instrucción SQL real que se ejecuta es:

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

Además, si el usuario quiere eliminar toda la tabla company_com, solo debe pasar la cadena siguiente: "x'; DROP TABLE company_com; --" (sin comillas de apertura ni cierre). Esta es la consulta SQL completa:

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

Aplique una de las medidas siguientes para proteger su sitio web contra inyecciones de código SQL:

  • Restrinja la entrada a un conjunto mínimo de caracteres permitidos eliminando automáticamente todos los demás caracteres que se encuentren fuera del rango especificado.
  • Aplique escape a todos los caracteres que pueden causar problemas cuando se usan dentro de una consulta SQL (por ejemplo, comillas simples).
  • Utilice instrucciones preparadas.
  • Utilice derechos de acceso a base de datos.
  • Utilice procedimientos almacenados.

Recomendamos las instrucciones preparadas que se describen con más detalle en Uso de instrucciones preparadas. Dreamweaver 8.0.2 y CS3 utilizan instrucciones preparadas para los modelos de servidor ASP_VBS, ASP_JavaScript, Cold Fusion y JSP, y aplican escape a la entrada del usuario para el modelo de servidor PHP_MySQL. Para obtener más información sobre los ataques de inyecciones de código SQL, consulte Ataques de inyecciones de código SQL por ejemplo.

Usar instrucciones preparadas

Dreamweaver 8.0.2 y CS utilizan instrucciones preparadas, ya que esta solución nos garantiza que la sustitución de los parámetros SQL reales por valores dados (posiblemente enviados a través de URL) se realizará en el servidor y no en la página. Las instrucciones preparadas también garantizan que el valor transferido a la propia consulta SQL tenga el tipo de datos adecuado y utilice el valor de escape adecuado: si se dice que un parámetro es de tipo int, el usuario no podrá enviar una letra; si un parámetro es de tipo texto, se aplicará escape correctamente a toda la cadena transferida y el usuario no tendrá la oportunidad de realizar una inyección de código SQL.

Declaraciones preparadas para los modelos de servidor ASP_VBS y ASP_JS

Para ASP_VBS y ASP_JS, confiamos en la capa de base de datos ADODB. El ejemplo siguiente muestra un conjunto de registros ASP_VBS simple que usa un parámetro SQL dinámico para filtrar resultados:

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

Instrucciones preparadas para el modelo de servidor ColdFusion

ColdFusion ofrece compatibilidad integrada para instrucciones preparadas. El ejemplo siguiente muestra un conjunto de registros ColdFusion simple que usa un parámetro SQL dinámico para filtrar resultados:

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

Instrucciones preparadas simuladas para el modelo de servidor PHP_MySQL

Las instrucciones preparadas para PHP ya estaban disponibles con MySQL 4.1, pero debido a que también queríamos admitir versiones anteriores de MySQL, decidimos implementar una función personalizada que hace prácticamente lo mismo. El ejemplo siguiente muestra un conjunto de registros PHP simple que usa un parámetro SQL dinámico para filtrar resultados:

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

Permitir parámetros dinámicos en conjuntos de registros estándar

Así pues, ¿cómo podemos protegernos contra las inyecciones de código SQL y, al mismo tiempo, permitir parámetros dinámicos y mantener el código resultante editable mediante el panel Comportamientos de servidor en Dreamweaver? La solución es insertar los parámetros dinámicos en la propia consulta SQL, en lugar de pasarlos como parámetros SQL clásicos (que escaparían o se añadirían a SQL mediante instrucciones preparadas).

Importante: Los usuarios no pueden generar dicho código mediante Comportamientos de servidor y objeto que se proporcionan con Dreamweaver 8.0.2 o CS3 (por ejemplo, insertar un conjunto de registros mediante el panel Comportamientos de servidor o la barra Insertar). Esta solución requiere que el usuario edite el código manualmente o que instale una extensión de terceros que cree dicho código. La responsabilidad de escribir código seguro recae en el usuario o en un desarrollador de software externo.

La finalidad de este método es permitir que los desarrolladores experimentados creen consultas SQL dinámicas. El mal uso de esta solución proporciona al hacker la posibilidad de obtener privilegios no deseados o de romper las páginas web y la base de datos. Consulte Inyecciones de código SQL y métodos de prevención para minimizar el riesgo de las inyecciones de código SQL.

Estos son los principales casos donde hemos identificado que se utilizaría esta solución:

  • Crear o actualizar manualmente una consulta SQL para aceptar parámetros dinámicos.
  • Crear o actualizar extensiones para generar parámetros dinámicos.

Ambos casos de uso se detallarán en los párrafos siguientes.

Crear o actualizar manualmente una consulta SQL para aceptar parámetros dinámicos y mantener editable el conjunto de registros desde Dreamweaver

En el ejemplo siguiente, el usuario desea añadir la función de clasificación a una tabla dinámica. Decide volver a cargar la página mientras pasa los archivos y la dirección de clasificación que se utilizará (ascendente o descendente) en la dirección URL. Un ejemplo de dirección URL inicial es:

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

Se requiere una codificación manual para realizar esta tarea en Dreamweaver mediante Comportamientos de servidor. Los ejemplos de código siguientes muestran los cambios que deben efectuarse para cada modelo de servidor.

Importante: Ninguno de los ejemplos siguientes está validado respecto a las inyecciones de código SQL. Se proporcionan para fines ilustrativos. No deben utilizarse en tareas de producción. Los ejemplos no están protegidos para que resulten lo más sencillos posible. Debe protegerse contra las inyecciones de código SQL. Para obtener más información, consulte Inyecciones de código SQL y métodos de prevención.

ASP_VBS

Antes:

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

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

Antes:

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

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

Antes:

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

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

Crear o actualizar manualmente extensiones para generar parámetros dinámicos y mantener editable el conjunto de registros desde Dreamweaver

Ahora considere la misma situación hipotética anterior, excepto que el usuario desea crear una extensión que generará el correspondiente código en lugar de depender de una combinación de funcionalidad integrada y edición manual. La nueva extensión debe generar el código correcto para los modelos de servidor ASP_VBS, ColdFusion y PHP_MySQL.

Importante: Las secciones siguientes solo tratan sobre la generación de código que utilizará los valores de los parámetros de URL para crear una consulta SQL dinámica. El objetivo principal es generar el código para que Dreamweaver lo reconozca y pueda editarlo desde las interfaces estándar de Dreamweaver. Este tutorial no se ocupará de la validación de los datos de entrada (mediante parámetros de URL) ni protegerá el código SQL final contra cualquier intento de inyecciones de código SQL porque la complejidad de dicho código queda fuera del ámbito de este tutorial. El desarrollador es el único responsable de proteger el código SQL final contra las inyecciones de código SQL.

Proporcionamos una extensión de demostración que toma una conexión y una tabla como entrada y genera una tabla dinámica que muestra todos los registros. También actualiza el código SQL generado para contener la variable orderBy como se muestra en los ejemplos anteriores. La extensión se ha diseñado para Dreamweaver 8.0.2 y CS3. El código generado no está protegido, ya que el objetivo es crear conjuntos de registros que contengan parámetros dinámicos que se puedan editar mediante las interfaces estándar de Dreamweaver. El código fuente de la extensión se encuentra en la carpeta Configuration del usuario.

En todos los ejemplos de código a continuación, las secciones resaltadas se han añadido para activar la función de clasificación a través de los parámetros de URL sortCol y sortDir.

ASP_VBS

El código que genera la correspondiente consulta SQL para el modelo de servidor ASP_VBS se encuentra dentro del archivo "[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js" en la carpeta Configuration del usuario. El cambio está en la línea 130:

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

El archivo de participantes para el comportamiento del servidor MyDynamicTable ASP_VBS incluye un participante adicional que añade la definición de variable "orderBy" a la página:

<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

El mismo archivo "[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js" de la carpeta Configuration del usuario también contiene el código para el modelo de servidor ColdFusion; en este caso, el cambio correspondiente está en la fila 138:

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

El archivo de participantes para el comportamiento del servidor MyDynamicTable ColdFusion incluye un participante adicional que añade la definición de variable "orderBy" a la página:

<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

El último modelo de servidor de la extensión es PHP_MySQL. El cambio correspondiente en el archivo "[USER_CONFIGURATION_FOLDER]/Commands/My Dynamic Table.js" en la carpeta Configuration del usuario está en la línea 122:

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

El archivo de participantes para el comportamiento del servidor MyDynamicTable PHP_MySQL incluye un participante adicional que añade la definición de variable "orderBy" a la página:

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

Pros

El método descrito antes tiene varias ventajas:

  • Los desarrolladores más avanzados pueden aprovechar la vulnerabilidad de inyección de código SQL para construir códigos SQL más complejos de manera dinámica.
  • Los conjuntos de registros creados podrán seguir editándose desde Dreamweaver.
  • No es difícil integrar en las extensiones de Dreamweaver que generan conjuntos de registros con parámetros dinámicos de SQL.
  • El código predeterminado generado por Dreamweaver permanece sin cambios. Los desarrolladores que no requieren consultas dinámicas (y sus clientes) pueden tener la tranquilidad de que el código escrito por Dreamweaver es seguro.

Contras

Como Dreamweaver no se ha diseñado para admitir este método de manera predeterminada, también tiene algunos inconvenientes:

  • El código generado con el método descrito en este documento es vulnerable a las inyecciones de código SQL; por lo tanto, debe protegerse de otras maneras. Entre otros, estos son algunos métodos de protección:
    • Asegúrese de que el usuario final no pueda utilizar el método de inyección de código SQL para abarcar la página web o la base de datos.
    • Aplique escape los argumentos antes de que lleguen al código SQL real.
    • Asegúrese de que, incluso si el usuario logra descifrar su código, no robará información confidencial o no abarcará la base de datos.
  • Dos problemas conocidos que no se pueden solucionar hasta que aparezca la próxima versión de Dreamweaver:
    • ColdFusion: La interfaz de usuario de conjunto de registros simple elimina los caracteres hash de los nombres de variable en la consulta SQL cuando se editan y se vuelven a aplicar
    • ColdFusion, ASP_VBS, ASP_JS: El botón Test de la interfaz de usuario de conjuntos de registros avanzada no funciona si la consulta SQL contiene nombres de variable

Ubicación de la carpeta Configuration del usuario

  • Windows XP

    C:\Documents and Settings\[nombre de usuario]\Datos de programa\Adobe\Dreamweaver 9\Configuration
  • Windows Vista

    C:\Usuarios\[nombre de usuario]\AppData\Roaming\Adobe\Dreamweaver 9\Configuration
  • macOS 10.4.x

    /Usuarios/[nombre de usuario]/Librería/Aplicaciones\ Support/Adobe/Dreamweaver\ 9/Configuration
Logotipo de Adobe

Inicia sesión en tu cuenta