¿Cómo limitar el impacto y reducir el riesgo de inyección SQL para el sitio web existente?

34

Nuestro sitio web es 100% basado en API (con un cliente SPA). Recientemente, un hacker logró obtener la contraseña de nuestro administrador (con hash con SHA-256) a través de la inyección de SQL (y pwd de craqueo) de esta manera:

https://example.com/api/products?userId=3645&type=sqlinject here

Es solo un ejemplo, pero es mortal para nosotros. Afortunadamente, fue un buen hacker y nos acaba de enviar un correo electrónico sobre el hack. Por suerte, un sitio web con constantes de ataques a lo largo de sus 5 años de existencia.

Sí, SABEMOS que necesitamos verificar la entrada del usuario en todas partes y hacer un formato / escape / declaración preparados adecuadamente antes de enviar datos a MySQL. Lo sabemos, y lo estamos arreglando. Pero no tenemos suficientes desarrolladores / probadores para que sea 100% seguro.

Ahora, asumiendo que tenemos un 0.1% de probabilidad de ser SQL inyectado de alguna manera. ¿Cómo hacemos que sea más difícil para un hacker encontrarlo y limitar el daño posible?

Lo que hacemos en cuanto a soluciones rápidas / medidas adicionales:

  1. En la "puerta" de salida compartida por todas las API, ya no enviamos la excepción PDOException y el mensaje como antes. Pero todavía tenemos que decirle al cliente que ocurrió la excepción:

    {type: exception, code: err643, message: 'contact support for err643'}
    

    Soy consciente de que si el pirata informático ve una excepción, seguirá intentando ...

  2. El usuario que usa PHP para conectarse a MySQL solo puede CRUD a las tablas. ¿Es eso lo suficientemente seguro?

  3. Cambie el nombre de todas las tablas (usamos código abierto para que el pirata informático pueda adivinar el nombre de las tablas).

¿Qué más debemos hacer?

  

Actualización: dado que hay muchos comentarios, me gustaría darles un lado.   info

     
  1. En primer lugar, esta es la aplicación LEGACY, desarrollada por algunas personas similares a estudiantes, no de grado empresarial. El equipo de desarrollo no está en ninguna parte, ahora   solo uno o dos tipos de pruebas de desarrollo híbrido que manejan cientos de clases de acceso a datos.
  2.   
  3. La contraseña es hash con SHA-256, sí, apesta. Y lo estamos cambiando a php recomendado.
  4.   
  5. inyección SQL tiene 17 años. ¿Por qué sigue ahí?
  6.   
  7. ¿Las declaraciones preparadas son 100% seguras contra la inyección de SQL?
  8.   
    
pregunta Phung D. An 20.06.2018 - 21:41
fuente

11 respuestas

80

No gaste mucho tiempo en soluciones o medias correcciones. Cada minuto que dedica a intentar implementar lo que se sugiere aquí es un minuto que podría haber dedicado a implementar declaraciones preparadas. Esa es la única solución verdadera.

Si tienes una vulnerabilidad de SQLi, el atacante probablemente ganará al final, sin importar lo que hagas para intentar desacelerarla. Así que ten en cuenta que, si bien puedes hacer mejoras, al final estás jugando a una partida perdida a menos que arregles la causa raíz del problema.

En cuanto a lo que ha intentado hasta ahora: ocultar mensajes de error es un buen comienzo. Le recomiendo que aplique permisos aún más estrictos (ver más abajo). Es discutible si ayuda a cambiar los nombres de tablas , pero probablemente no resulte perjudicial.

Bien, entonces, ¿qué pasa con esos permisos? Limite lo que el usuario de la base de datos puede hacer tanto como sea posible, a nivel de tabla o incluso de columna. Incluso puede crear múltiples usuarios de bases de datos para bloquear aún más las cosas. Por ejemplo, solo use un usuario de base de datos con permiso para escribir cuando en realidad está escribiendo. Solo conéctese con un usuario que tenga derecho a leer la columna de contraseña cuando realmente necesite leer la columna de contraseña. De esa manera, si tiene una vulnerabilidad de inyección en alguna otra consulta, no se puede aprovechar para filtrar contraseñas.

Otra opción es usar un firewall de aplicación web (WAF), como mod_security para Apache. Puedes configurarlo para bloquear solicitudes que parezcan sospechosas. Sin embargo, ningún WAF es perfecto. Y lleva tiempo y energía configurarlo. Probablemente esté mejor usando ese tiempo para implementar declaraciones preparadas.

Nuevamente, no permita que nada de esto lo atraiga a una falsa sensación de seguridad. No hay ninguna sustitución para arreglar la causa raíz del problema.

    
respondido por el Anders 20.06.2018 - 22:08
fuente
65

La única forma correcta es usar declaraciones preparadas.

  1. Si disfrazas los mensajes de error, es un poco más difícil, pero no detendrá a los atacantes.
  2. Puede restringir los derechos, pero el atacante podría obtener todos los derechos otorgados al usuario. También es posible ejecutar comandos de shell desde una inyección SQL en algunos casos.
  3. Cambiar el nombre de las tablas no te ayudará. La herramienta sqlmap forzará bruscamente los nombres de tabla para usted char por char. Esto no necesita mucho tiempo.

También puede restringir el número de llamadas, para dificultar la tarea de los atacantes o hacer alertas sobre las llamadas sospechosas y detener este manual, pero la única forma correcta de resolver este problema es mediante declaraciones preparadas. Simplemente haga grep a través de su código fuente y eche un vistazo a las declaraciones SQL con contenido dinámico y reemplácelas.

    
respondido por el trietend 20.06.2018 - 22:05
fuente
25
  

Lo sabemos, pero no tenemos suficientes desarrolladores / probadores para que sea 100%   seguro.

Este es el problema real . Hasta que no contrates más personas o reasignes prioridades, no estás seguro. Detener el 99.9% de los atacantes significa que sus datos han sido comprometidos. Las soluciones de curita son una cosa mala y no funcionan . No pierda tiempo en refactorizar su patrón de acceso SQL mientras pueda solucionar problemas reales.

En cuanto a la solución de código, el uso de sentencias preparadas, como sugieren muchas personas aquí, es la forma más fácil de usar SQL de forma segura.

Es mucho más fácil que tratar de usar php para limpiar los argumentos, y cuesta mucho menos tiempo. Simplemente grep su base de código completa para todo lo relacionado con SQL y corríjalo.

    
respondido por el Jacco van Dorp 21.06.2018 - 12:23
fuente
9

Limpia todo el SQL de tu código para siempre

La mejor solución para este problema es eliminar todo el código SQL como cadenas literales de su código y nunca volver a introducirlo. En su lugar, use el software ORM como Hibernate o Entity Framework. De todos modos, este software es mucho más fácil de usar que el SQL sin formato y parametriza automáticamente su SQL cuando finalmente llega a la base de datos. Si es posible para usted, tenga esta política.

Si no tiene tiempo para aprender e implementar estos paquetes, o no puede usarlos por alguna otra razón, la respuesta de trietend es la siguiente mejor opción, utilizando declaraciones preparadas . Esto le permitirá estar a salvo de la inyección de SQL. Es decir, hasta que ejerza presión sobre sus desarrolladores para que ofrezcan soluciones rápidas, en ese momento, una vez más, pueden olvidarse de parametrizar una sola variable, dejándolo en el lugar donde comenzó.

    
respondido por el Nacht 21.06.2018 - 07:37
fuente
9
  

Lo sabemos, pero no tenemos suficientes desarrolladores / probadores para hacerlo 100% seguro.

No está del todo claro qué quiere decir con esta declaración, ya que las declaraciones preparadas son triviales en todos los lenguajes web populares (PHP, Python, Java, node.js, etc.) y son más o menos una medida 100% efectiva. contra la inyección de SQL.

¿Quiere decir que subcontrata el desarrollo y no puede permitirse el control de calidad del código que escriben para usted en el contrato? No puede darse el lujo de no hacerlo: sigue siendo el responsable aquí.

¿Quiere decir que sus desarrolladores están demasiado ocupados implementando funciones para que se demoren algunas horas en actualizar el código base? Todo lo que no sea declaraciones preparadas tomará más tiempo y costará más (por ejemplo, ORM, obsfucación, permisos específicos, etc.).

¿Quiere decir que sus desarrolladores no son lo suficientemente competentes para realizar esta tarea simple? Ese es un problema muy grande (o para ser más precisos, la inyección de SQL no debería ser su mayor preocupación en ese momento).

¿Quiere decir que su base de código es un lío de lógica de espagueti con formato de escopeta de include s anidado que incluso los desarrolladores competentes tardarán una eternidad en realizar lo que de otra manera sería una tarea simple de unas pocas horas? Del mismo modo, mayor problema.

Cualquiera que sea, la solución es usar declaraciones preparadas, alrededor de ayer.

    
respondido por el Jared Smith 21.06.2018 - 16:18
fuente
5

Una vez que se haya implementado el consejo inicial de "declaración preparada", recomendaría leer acerca de la inyección SQL. Preguntar "cómo puedo prevenir la inyección de SQL" es una pregunta bastante amplia. La seguridad es un tema con el que usted (más bien, todo su equipo de desarrollo) necesita estar muy familiarizado para reducir el riesgo de compromiso. Un agujero socava todos tus otros esfuerzos.

Visite Open Web Application Security Project (OWASP) aquí: enlace

Particularmente el material adicional:

  • Hoja de referencia de prevención de inyección SQL
  • Hoja de referencia de parametrización de consulta
  • Artículo guía sobre cómo evitar las vulnerabilidades de inyección de SQL

Y también

  • Cómo revisar el código para las vulnerabilidades de inyección SQL .
respondido por el Nicolas 21.06.2018 - 20:02
fuente
4

Una medida provisional sería observar un firewall de aplicación web (WAF): hay algunas compañías que ofrecen esto como un servicio, y algunos proveedores de nube que también tienen ofertas. CloudFlare parece ofrecer uno, un cliente usó Incapsula y sé que la oferta AWS WAF tiene algunos conjuntos de reglas administradas para la inyección de SQL.

La forma en que funciona es que todo el tráfico se envía a / a través del WAF (ya sea incluyéndolo en los servidores o configurando el DNS para que apunte al WAF como lo haría para un CDN), y busca cosas que se parezcan a SQL Inyección de intentos en los parámetros. No detectará todos los ataques y puede bloquear el tráfico legítimo, pero es una solución disponible de bandaid.

    
respondido por el CoderTao 21.06.2018 - 23:17
fuente
2

Por "no tengo suficientes desarrolladores", entiendo que algunas personas influyentes en su organización piensan que hay mayores prioridades, por lo que los recursos que tenga están dedicados a esas cosas. Por lo tanto, solucionar su problema de seguridad es perder a otras cosas en un análisis de costo-beneficio.

Su tarea aquí es cambiar de opinión, tanto como implementar la solución. Cuantifique lo que está en juego: ¿cuál sería el daño, tanto en términos de dinero como de reputación, de otra infracción? Es posible que existan leyes en su jurisdicción que requieran que informe los compromisos de los datos de su usuario. ¿Sería eso vergonzoso? ¿Podría haber informes de los medios de comunicación sobre las malas prácticas de su organización?

Piense en la inversión para arreglar la seguridad como un seguro. En retrospectiva, después de saber que su edificio no se quemó durante un período de diez años, podría argumentar que sus primas fueron un desperdicio. Pero usted y su organización pagan por el seguro, porque enfrentan un futuro incierto. Es la gestión de riesgos.

El riesgo tiene dos factores: probabilidad e impacto. La probabilidad de que se produzca otra infracción es alta. Usted sabe que puede y se ha hecho al menos una vez. Si las posibles consecuencias también son malas, debe aumentar la prioridad de corregir sus vulnerabilidades de seguridad.

    
respondido por el Concrete Gannet 22.06.2018 - 05:28
fuente
0

Como todos han mencionado, corrija el código en el sitio.

Puedes hacer cualquier parche que desees en una pared, pero hasta que la pared se construya correctamente, cualquier parche será un problema de sangrado.

Cualquier otra sugerencia para limpiar código es lo que Linus Torvalds llamaría masterbation.

  • Repare su código (desde los puntos de inyección más obvios hasta los últimos I.E Obtenga parámetros) y use declaraciones preparadas. cómo puedo evitar la inyección de SQL en PHP

    • Puedes probar un WAF (IE ModSecurity ) temporalmente hasta que puedas arreglar las cosas

    • Intente tal vez algún tipo de prohibición de IP en 'personas que prueban el sitio' o que bloquean consultas no seguras.

    • Si tiene la capacidad de crear una pantalla de inicio de sesión temporal (segura) hasta que esto se resuelva.

    • Pruebe un servicio de terceros hasta que esto se resuelva algo como cloudflare o cualquier otra cosa que esté disponible.

Las PDO están disponibles desde PHP 5.1 lanzado el 24 de noviembre de 2005 ... Los desarrolladores deben entender que son las consecuencias de sus malos hábitos de codificación y, personalmente, creo que debería haber responsabilidad por la calidad del trabajo que se proporciona.

En cualquier caso, las PDO son una cosa fácil de implementar. Si sus desarrolladores no pueden proporcionar una mejor calidad, sugeriría una purga ...

    
respondido por el ApertureSecurity 23.06.2018 - 13:53
fuente
0

La regla más importante es:

Utilice bibliotecas, herramientas y API que hacen que la opción segura sea la opción más fácil.

Nacht dice "solo usa un ORM", que es un subconjunto de esta regla.

Las declaraciones preparadas violan esta regla, ya que su uso es engorroso, hace que el código se hinche y, a menudo, pueden ser más lentos que las declaraciones no preparadas, según su base de datos. Son una solución válida. No criticaré a nadie que los use, pero no son necesariamente la mejor y más conveniente solución.

Y ... queremos que la opción segura sea fácil y conveniente, por lo que el programador la usa por interés propio y por pereza. ¡Queremos que el programador tenga que salir de su camino e invertir un poco de esfuerzo para usar la opción insegura! ...

Una solución mucho mejor es una API como Python dbapi. Escribí un php equivalente hace años, que era como:

db_query( "SELECT * FROM mytable WHERE id=%s", $id )

Esto es más conveniente que las declaraciones preparadas. Sólo una línea para escribir. También devuelve el resultado en una forma que puede usar foreach (), por lo que es mucho más fácil de usar que fetch () y esas cosas. Más importante aún, el código oculto detrás de esta API manejará el escape correctamente. Esto hace que las inyecciones de SQL sean irrelevantes. Esto maneja aproximadamente el 99% de las consultas si no usa un ORM. Sin embargo, las consultas dinámicas (generalmente de búsqueda) necesitarán un manejo especial, pero aún así es fácil.

Si tiene en cuenta esta regla, en algún momento tendrá que preguntarse por qué tiene que usar htmlspecialchars (), y por qué el idioma no tiene una característica que haga la configuración segura (es decir, sin XSS). vulns) la más fácil de usar? ¿Porque Oh porque? Y ahí es cuando sueltas PHP.

    
respondido por el peufeu 23.06.2018 - 21:05
fuente
0

Además de todas las grandes respuestas hasta ahora, si desea abordar el problema específico de extraer las credenciales de usuario, puede refactorizar su base de datos para tener las contraseñas en una tabla separada con derechos de acceso estrictos , similar a la forma en que los sistemas Unix los colocan en / etc / shadow.

Para verificar las credenciales, necesita un procedimiento almacenado que toma la contraseña (o hash) ingresada como parámetro y devuelve un valor booleano. En otras palabras, usted descarga la tarea de verificar las credenciales en la base de datos y el hash real nunca abandona la base de datos.

Esto requiere una refactorización mínima y resuelve tu problema inmediato en la raíz, en lugar de una inyección de SQL específica y te deja vulnerable a la siguiente.

    
respondido por el Tom 06.07.2018 - 12:17
fuente

Lea otras preguntas en las etiquetas