X
BLOG

Cómo prevenir la inyección de SQL

Cómo prevenir la inyección de SQL Tiempo de leer: 10 minutos

El SQL (lenguaje de consulta estructurado) es una debilidad de software y vulnerabilidades de seguridad bien conocida, si no una de las más conocidas. A pesar de su reputación, cómo prevenir la inyección de SQL sigue siendo una de las principales vulnerabilidades, y los ataques siguen creciendo.

Encontrar inyecciones de SQL

Las vulnerabilidades de inyección (de las cuales las inyecciones de SQL son un tipo) son el problema de seguridad de la aplicación web número uno según el OWASP Top 10. Las inyecciones de SQL son el número seis en el CWE Top 25. Otros ejemplos del mismo tipo de vulnerabilidades de seguridad son:

  • Inyección de comando (CWE-77)
  • Inyección de comandos del sistema operativo (CWE-78)
  • Inyección de hibernación (CWE-564)
  • Inyección de lenguaje de expresión (CWE-917)

Todas estas vulnerabilidades comparten un atributo común. Se explotan utilizando datos de fuera del sistema, entrada de usuario o archivo, o lo que sea, en funciones potencialmente peligrosas.

Obtenga el informe técnico gratuito: El valor comercial del software seguro

Afortunadamente, las inyecciones de SQL son detectables mediante herramientas tanto estática como dinámicamente. Sin embargo, nunca puedes estar seguro de haberlos atrapado a todos. La prevención de las inyecciones de SQL también es clave para reducir la frecuencia y el impacto de estas vulnerabilidades. Es probable que un proceso de DevSecOps maduro que incorpore la detección y prevención de vulnerabilidades detecte y evite que este tipo de vulnerabilidades ingresen a un producto lanzado.

¿Qué es SQL?

SQL es un lenguaje específico de dominio diseñado para la gestión de bases de datos relacionales. Las bases de datos relacionales presentan datos como una colección de tablas en filas y columnas. Cada fila tiene una clave que proporciona la relación con otras tablas. A continuación, se muestra un ejemplo de tabla "usuario":

[tabla "1" no encontrada /]

SQL es el lenguaje de elección para administrar, consultar y manipular datos en una base de datos de relaciones. Define las tablas y relaciones en la creación de bases de datos. Para la mayor parte del uso diario, los desarrolladores utilizan SQL para "CRUD": para crear, leer, actualizar y eliminar datos.

¿Por qué SQL es explotable?

Los lenguajes de programación generales no incluyen soporte para SQL. El acceso a los comandos de la base de datos se realiza a través de una API proporcionada por el proveedor de la base de datos. En muchos casos, los comandos SQL se envían como cadenas que la API interpreta y aplica a la base de datos. Aquí hay algunas consultas SQL simples:

Una consulta SQL típica tiene la siguiente forma:

Seleccionar (algo) de (en algún lugar) (condición opcional)

Usando las tablas anteriores como ejemplo para recuperar el correo electrónico de la fila donde el apellido es "Smith", se usa la siguiente declaración SQL:

Seleccione el correo electrónico del usuario donde apellido = 'Smith'

El resultado sería el siguiente:

Smith1234@mail.com John.smith@mail.net Smith1234@mail.com

Obtener información de los usuarios con un formulario web (ver más abajo) es un caso de uso común en aplicaciones web. Los datos que los usuarios ingresan en el campo "Nombre", por ejemplo, se utilizan para formar consultas SQL basadas en la entrada recibida. Considere el siguiente formulario web simple:

Formulario web simple

El software procesa el formulario y asigna los valores a variables como esta:

String formName = request.getParameter (Nombre);

La cadena ingresada como "Nombre" se usa para ensamblar la consulta usando esa entrada del usuario:

String myQuery = "seleccionar mensaje del usuario donde email = '" + formName + "';"

Usando esta consulta construida:

Seleccione el mensaje del usuario donde correo electrónico = 'Smith1234@mail.com';

El resultado de esto (usando la tabla anterior como ejemplo) es el siguiente:

Hola como estas

Con suerte, es fácil ver cómo todo esto puede salir mal. Dado el uso de la entrada del usuario directamente en la cadena, alguien que comprenda la sintaxis SQL puede manipularla fácilmente para generar la consulta SQL. Considere el siguiente ejemplo:

Usando el mismo formulario anterior, alguien ingresa “Smith1234@mail.com 'o' 1 '=' 1” en el campo de correo electrónico.

El mismo código ensamblará la siguiente cadena de consulta SQL:

Seleccione el mensaje del usuario donde correo electrónico = 'Smith1234@mail.com' o '1' = '1';

Agregar algo que parezca inocuo como "o 1 = 1" cambia la lógica de la consulta y potencialmente filtra datos al devolver todas las filas en la tabla llamada "usuario". En este caso, mostrándole mensajes para cada usuario de la tabla. Un problema de privacidad grave y, en algunas jurisdicciones o contextos, también un problema legal potencial, como GDPR, HIPAA o CCPA.

La consulta anterior termina con el siguiente resultado no deseado:

Hola contraseña 1234 ¿Cómo estás? No le digas a nadie Wassup

Cómo funciona una inyección SQL

La esencia básica de una inyección SQL (y otros tipos de vulnerabilidades de inyección) es el uso de datos no verificados desde fuera de la aplicación, como el texto de entrada del usuario, en una cadena de consulta SQL. La descripción de CWE 89: "Neutralización incorrecta de elementos especiales utilizados en un comando SQL ('Inyección SQL')" define esto con mayor precisión:

“Sin la eliminación o citación suficiente de la sintaxis SQL en las entradas controlables por el usuario, la consulta SQL generada puede hacer que esas entradas se interpreten como SQL en lugar de como datos ordinarios del usuario. Esto se puede usar para alterar la lógica de la consulta para evitar los controles de seguridad o para insertar declaraciones adicionales que modifiquen la base de datos de back-end, posiblemente incluyendo la ejecución de comandos del sistema ".

La misma entrada en la base de datos CWE (CWE 89) proporciona otro ejemplo simple de este ataque. Suponiendo que la aplicación realiza una consulta en nombre del usuario "wiley" y el usuario construye la entrada de tal manera que contiene instrucciones SQL, por ejemplo:

nombre'; BORRAR DE elementos; -

Si esta aplicación no realiza ninguna verificación de validez en esta entrada, construye una consulta como esta:

SELECCIONE * DE los elementos DONDE propietario = 'wiley' Y nombre del elemento = 'nombre'; BORRAR DE elementos; - '

Si este ataque tiene éxito, elimina todos los datos de los elementos de la tabla, lo que causa estragos en la base de datos. Cualquier comando SQL válido podría potencialmente ejecutarse de esta manera. Este es un ejemplo de ataque de escritura / modificación donde la intención es corromper la base de datos o insertar información no deseada. El ejemplo anterior ("o 1 = 1") es un ataque de lectura donde la intención es la fuga de datos.

Muchas implementaciones de servidores de bases de datos aceptan el punto y coma como separador de comandos, lo que permite que tales inyecciones de SQL sean tan peligrosas. El “-” final indica que el resto del texto es un comentario, lo que obliga al intérprete de SQL a ignorar las comillas finales, lo que de otro modo causaría un error de sintaxis. Hay varias formas de engañar a la cadena de consulta ensamblada. A veces, de formas que los desarrolladores nunca imaginaron.

Mitigaciones para protegerse contra inyecciones de SQL

Hay varias mitigaciones disponibles que los desarrolladores deben implementar. En primer lugar, la postura de seguridad debe considerar todos los datos que provienen de fuera de la aplicación y que no son de confianza. Las siguientes son estrategias de mitigación típicas:

  • Utilice declaraciones preparadas con consultas parametrizadas.
  • Utilice procedimientos almacenados.
  • Validación de entrada de lista blanca.
  • Escape de toda la entrada suministrada.

Estos se describen con más detalle en el OWASP hoja de trucos para inyecciones SQL.

Prueba de inyecciones de SQL

Un enfoque típico de la seguridad es realizar varios tipos de pruebas de seguridad como parte de las operaciones regulares de control de calidad cuando se está ejecutando el software integrado. Desafortunadamente, las pruebas funcionales no intentan insertar vulnerabilidades en los campos de entrada del usuario porque la mayoría de los probadores no piensan como malos actores.

Además del hecho de que tradicionalmente no tienen el tiempo o la dirección para hacerlo. También es difícil probar manualmente las vulnerabilidades de tipo inyección, ya que requiere probar tantas combinaciones diferentes de entradas. Aquí es donde intervienen las pruebas de fuzzing o fuzzing. Crea datos aleatorios, inesperados e inválidos como entradas a la aplicación bajo prueba. Las pruebas de fuzz son parte de las pruebas de penetración, ya que el objetivo es exponer las vulnerabilidades de seguridad a través de las interfaces expuestas.

Pruebas de penetración

Las pruebas de penetración (y, por extensión, las pruebas de fuzz) son beneficiosas porque pueden descubrir problemas de seguridad que se han infiltrado en el proceso y revelar problemas de seguridad importantes. Sin embargo, como todas las pruebas dinámicas, depende de la cantidad de prueba, código y cobertura de API para probar completamente todas las permutaciones y combinaciones posibles. Las pruebas de penetración dependen de la minuciosidad de las pruebas funcionales, que normalmente se realizan a nivel de la interfaz de usuario. Esto hace que sea importante respaldar sus esfuerzos de pruebas de penetración con pruebas de API y SAST para asegurarse de que está siendo minucioso.

Prueba de API

Las pruebas de API ayudan a cambiar las pruebas funcionales y de seguridad a la izquierda al eliminar la dependencia de las pruebas de IU frágiles y que consumen mucho tiempo. La capa de API es donde reside gran parte de la funcionalidad de la aplicación y las pruebas son más resistentes a los cambios en este nivel y más fáciles de automatizar y mantener.

Pruebas de penetración a nivel API

Las pruebas de penetración a nivel de API para exponer las inyecciones de SQL son posibles con herramientas como Parasoft SOAtest donde las pruebas de fuzz automatizadas se crean a partir de pruebas funcionales existentes, ejercitan la lógica empresarial de la aplicación. Parasoft SOAtest se integra con Suite Burp que es una herramienta de prueba de penetración muy conocida.

Al ejecutar escenarios de prueba funcional con Parasoft SOAtest, las llamadas API definidas en la prueba se capturan junto con el tráfico de solicitud y respuesta. La herramienta de análisis de Burp Suite en cada prueba pasará los datos de tráfico a una instancia en ejecución separada de la aplicación Burp Suite, que realizará pruebas de penetración en la API según los parámetros de la API que observa en los datos de tráfico, utilizando su propia heurística.

La herramienta de análisis de Burp Suite tomará los errores encontrados por Burp Suite y los informará como errores dentro de SOAtest, asociados con la prueba que accedió a la API. Los resultados de Parasoft SOAtest se informan en el panel de análisis e informes de Parasoft. Para capacidades de informes adicionales.

Para obtener más información sobre esta integración, consulte nuestro Publicación anterior en las pruebas de penetración. Para obtener más información de Portswigger sobre el uso de Burp para inyecciones SQL, mira su publicación. A continuación se muestra una representación de cómo funciona esta integración con Burp:

Evite las inyecciones de SQL con Burp y Parasoft DTP

La integración de este tipo de pruebas de penetración en su proceso de CI / CD es una parte importante de la defensa contra las inyecciones de SQL y otros tipos de vulnerabilidades.

La penetración y el fuzzing son ciertamente un proceso importante y crítico en DevSecOps. Sin embargo, plantea preguntas.

  • ¿Qué sucede cuando las pruebas detectan vulnerabilidades de seguridad?
  • ¿Qué sucede cuando un equipo de software descubre que gran parte del manejo de las entradas de los usuarios es inseguro?
  • Ciertamente hay que arreglarlo, pero ¿a qué costo?

Encontrar problemas de seguridad graves en esta etapa tardía del desarrollo provoca importantes costos y retrasos. La prevención y la detección son clave para desplazar las operaciones de seguridad más a la izquierda hacia donde sea más barato y más fácil de arreglar.

Desplazar la detección y eliminación de inyecciones SQL más hacia la izquierda

Adoptar un enfoque DevSecOps para el desarrollo de software significa integrar la seguridad en todos los aspectos de la canalización de DevOps. Así como los equipos impulsan procesos de calidad como el análisis de código y las pruebas unitarias lo antes posible en el SDLC, lo mismo ocurre con la seguridad.

Las inyecciones de SQL podrían ser cosa del pasado si los equipos adoptan este enfoque de manera más amplia. El aumento de los ataques significa que aún no está sucediendo. Independientemente, describamos un enfoque para prevenir las inyecciones de SQL lo antes posible.

Encontrar y corregir posibles inyecciones de SQL (y otras vulnerabilidades de inyección) vale la pena en comparación con parchear (¡y disculparse por!) Una aplicación lanzada. Un solo incidente significativo puede empresas de costos $ 200,000 o más. Muchos incidentes les ocurren a las pequeñas empresas. Un solo ataque puede causar un estrés financiero grave, sin mencionar los posibles problemas regulatorios con respecto a las divulgaciones de incumplimiento y la protección de la PII.

El enfoque de detección y prevención que se describe a continuación se basa en cambiar la mitigación de las inyecciones de SQL a las primeras etapas de desarrollo y reforzar esto con la detección a través del análisis de código estático.

Cómo detectar inyecciones de SQL

La detección de inyecciones de SQL se basa en un análisis estático para encontrar este tipo de vulnerabilidades en el código fuente. La detección ocurre en el escritorio del desarrollador y en el sistema de compilación. Puede incluir código existente, heredado y de terceros.

La detección de problemas de seguridad de forma continua garantiza la búsqueda de cualquier problema que:

  • Los desarrolladores se perdieron en el IDE.
  • Existen en un código anterior a su nuevo enfoque de detección y prevención.

El enfoque recomendado es un modelo de confianza pero verificación. El análisis de seguridad ocurre en el nivel de IDE, donde los desarrolladores toman decisiones en tiempo real según los informes que obtienen. A continuación, verifique en el nivel de compilación. Idealmente, el objetivo a nivel de compilación no es encontrar vulnerabilidades. Es para verificar que el sistema esté limpio.

Como ejemplo, considere la aplicación de demostración de Parasoft, Parabank. Existe una posible inyección de SQL en el archivo StockDataInserter.java en com.parasoft.parabank.dao.jdbc.internal:

… Cadena final sql = sb.toString (); filas = (nextId - lastId) / JdbcSequenceDao.OFFSET; totalRows + = filas; getJdbcTemplate (). update (sql); ...

El informe generado en el momento de la compilación por Parasoft Jtest es el siguiente:

Encontrar inyecciones de SQL: Informe de Parasoft Jtest

En el detalle está la siguiente advertencia:

Llamada a un método peligroso StockDataInserter.java (96): getJdbcTemplate (). Update (sql); *** Datos contaminados: SQL

Con un rastreo a un punto anterior donde se encuentran los datos contaminados por la fuente (desmarcados, entrada no validada desde fuera de la aplicación):

Punto de contaminación StockDataInserter.java (47): return getJdbcTemplate (). Query (SQL, new ResultSetExtractor  > () {*** Datos contaminados: getJdbcTemplate (). Query (SQL, new ResultSetExtractor

En la batalla en curso contra las inyecciones de SQL, los desarrolladores deben tomar en serio estas advertencias. Cualquier uso de datos no validados en consultas SQL es un riesgo grave. Incluso si una advertencia específica podría no ser un problema en la forma actual, es posible que una refactorización posterior pueda exponer estas vulnerabilidades. ¡Compruebe todos los datos utilizados en las cadenas de consulta!

De hecho, los desarrolladores deben validar cualquier dato externo a una aplicación para asegurarse de que cumpla con el formato y contenido esperados. Pasar a una filosofía de "validar siempre" y un proceso que se basa en la codificación segura en lugar de las pruebas de seguridad aumenta considerablemente la seguridad de su aplicación. Comience a endurecer el código para evitar que las inyecciones de SQL ocurran en primer lugar.

Cuándo y cómo prevenir la inyección de SQL

El momento y el lugar ideales para evitar las inyecciones de SQL es cuando los desarrolladores escriben código en su IDE. Los equipos que están adoptando estándares de codificación segura como SEI CERT C para C y C ++ y OWASP Top 10 para Java y .NET o CWE Top 25, todos tienen pautas que advierten sobre entradas no validadas en consultas SQL.

La ejecución de análisis estáticos en el código recién creado es rápida y sencilla y se integra fácilmente en el proceso de CI / CD. Es una buena práctica investigar las advertencias de seguridad y las prácticas de codificación inseguras en esta etapa para evitar que este código se incorpore a la compilación.

Utilice el análisis de código estático para evitar inyecciones de SQL

Una parte igualmente importante de detectar prácticas de codificación deficientes es la utilidad de los informes. Es importante poder comprender la causa raíz de las infracciones del análisis estático para poder solucionarlas de forma rápida y eficaz. Aquí es donde herramientas comerciales como Parasoft's Prueba C / C ++, dotTEST, Jtest brillar.

Las herramientas de prueba automatizadas de Parasoft brindan un seguimiento completo de las advertencias, las ilustran dentro del IDE y recopilan información de compilación y otra información de forma continua. Estos datos recopilados junto con los resultados de las pruebas y las métricas proporcionan una visión completa del cumplimiento con el estándar de codificación del equipo. También muestra el estado general de calidad y seguridad.

Los informes incluyen los modelos de riesgo que forman parte de la información proporcionada por OWASP, CERT y CWE. De esa manera, los desarrolladores comprenden mejor el impacto de las vulnerabilidades potenciales informadas por la herramienta y cuáles de estas vulnerabilidades deben priorizar. Todos los datos generados a nivel de IDE se correlacionan con las actividades posteriores descritas anteriormente.

Resumen

La infame vulnerabilidad de inyección de SQL continúa afectando a las aplicaciones web. A pesar del conocimiento de cómo funciona y se puede explotar, sigue siendo frecuente. Ver el Salón de la vergüenza de IoT para ejemplos recientes.

Proponemos un enfoque de prevención y detección para complementar las pruebas de seguridad activa. Este enfoque evita las inyecciones de SQL lo antes posible en el SDLC, antes de escribir en el código. Evitar las inyecciones de SQL en el IDE y detectarlas en la canalización de CI / CD es clave para eliminarlas de su software. Por último, busque y corrija estos errores durante las pruebas utilizando técnicas de prueba de penetración.

La batalla contra las inyecciones de SQL (así como otras explotaciones de datos contaminados) aún continúa. Los equipos inteligentes pueden cambiar el rumbo con el proceso, las herramientas y la automatización adecuados en sus flujos de trabajo existentes.

Vea Parasoft en acción: Solicite una prueba

Escrito por

Arthur Hicken

Arthur ha estado involucrado en seguridad de software y automatización de pruebas en Parasoft durante más de 25 años, ayudando a investigar nuevos métodos y técnicas (incluidas 5 patentes) mientras ayuda a los clientes a mejorar sus prácticas de software.

Reciba las últimas noticias y recursos sobre pruebas de software en su bandeja de entrada.

Prueba Parasoft