¿Puedo proteger contra la inyección SQL escapando de comillas simples y rodeando la entrada del usuario con comillas simples?

StackOverflow https://stackoverflow.com/questions/139199

Pregunta

Me doy cuenta de que las consultas SQL parametrizadas son la forma óptima de sanear las entradas de los usuarios al crear consultas que contienen entradas de usuarios, pero me pregunto qué hay de malo en tomar las entradas de los usuarios y escapar de las comillas simples y rodeando toda la cadena con comillas simples . Aquí está el código:

sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"

Cualquier comilla simple que ingrese el usuario se reemplaza con comillas simples dobles, lo que elimina la capacidad de los usuarios para finalizar la cadena, por lo que cualquier otra cosa que puedan escribir, como puntos y comas, signos de porcentaje, etc., formarán parte de la cadena y no se ejecuta realmente como parte del comando. Estamos utilizando Microsoft SQL Server 2000, por lo que creo que la comilla simple es el único delimitador de cadena y la única forma de escapar del delimitador de cadena, por lo que no hay forma de ejecutar nada que el usuario escriba.

No veo ninguna forma de lanzar un ataque de inyección SQL contra esto, pero me doy cuenta de que si esto fuera tan a prueba de balas como me parece, alguien más ya lo habría pensado y sería una práctica común. Mi pregunta es esta: ¿qué tiene de malo este código? ¿Alguien sabe una forma de obtener un ataque de inyección SQL más allá de esta técnica de desinfección? Sería muy útil la entrada de muestra del usuario que explota esta técnica.

ACTUALIZACIÓN:

Gracias a todos por sus respuestas; casi toda la información que encontré en mi investigación apareció en esta página en alguna parte, lo cual es un signo de la inteligencia y la habilidad de las personas que se han tomado el tiempo de sus ocupados días para ayudarme con esta pregunta.

La razón por la que todavía no he aceptado ninguna de las respuestas es que todavía no conozco ninguna forma de lanzar efectivamente un ataque de inyección SQL contra este código. Algunas personas sugirieron que una barra invertida escaparía de una comilla simple y dejaría que la otra terminara la cadena para que el resto de la cadena se ejecutara como parte del comando SQL, y me doy cuenta de que este método funcionaría para inyectar SQL en una base de datos mySQL, pero en MS SQL 2000, la única manera (que he podido encontrar) de escapar de una sola cita es con otra qoute; Las barras invertidas no lo harán. Y a menos que haya una manera de detener el escape de la comilla simple, ninguna parte del resto de la entrada del usuario se ejecutará porque todo se tomará como una cadena contigua.

Entiendo que hay mejores formas de desinfectar la información pero realmente estoy más interesado en saber por qué el método que proporcioné anteriormente no funciona. Si alguien conoce alguna forma específica de montar un ataque de inyección SQL contra este método de desinfección, me encantaría verlo.

¿Fue útil?

Solución

Primero que nada, es solo una mala práctica. La validación de entrada siempre es necesaria, pero también siempre es dudosa.
Peor aún, la validación de la lista negra siempre es problemática, es mucho mejor definir explícita y estrictamente qué valores / formatos acepta. Es cierto que esto no siempre es posible, pero en cierta medida siempre debe hacerse.
Algunos trabajos de investigación sobre el tema:

El punto es que cualquier lista negra que hagas (y listas blancas demasiado permisivas) se puede omitir. El último enlace a mi artículo muestra situaciones en las que incluso las citas que se escapan se pueden omitir.

Incluso si estas situaciones no se aplican a usted, sigue siendo una mala idea. Además, a menos que su aplicación sea trivialmente pequeña, tendrá que lidiar con el mantenimiento y tal vez una cierta cantidad de gobierno: ¿cómo se asegura de que se haga bien, en todas partes todo el tiempo?

La forma correcta de hacerlo:

  • Validación de lista blanca: tipo, longitud, formato o valores aceptados
  • Si quieres poner en la lista negra, adelante. La cita de escape es buena, pero dentro del contexto de las otras mitigaciones.
  • Usar objetos de comando y parámetro, para preparar y validar
  • Llamar solo consultas parametrizadas.
  • Mejor aún, use procedimientos almacenados exclusivamente.
  • Evite usar SQL dinámico y no use la concatenación de cadenas para generar consultas.
  • Si usa SP, también puede limitar los permisos en la base de datos para ejecutar solo los SP necesarios, y no acceder a las tablas directamente.
  • también puede verificar fácilmente que toda la base de código solo accede a la base de datos a través de SP ...

Otros consejos

Bien, esta respuesta se relacionará con la actualización de la pregunta:

  

" Si alguien conoce alguna forma específica de montar un ataque de inyección SQL contra este método de desinfección, me encantaría verlo. "

Ahora, además del escape de barra invertida de MySQL, y teniendo en cuenta que en realidad estamos hablando de MSSQL, en realidad hay 3 formas posibles de inyectar su código aún en SQL

  

sSanitizedInput = " '" &erio; Reemplazar (sInput, " '" ;, "' '") & amp; " '"

Tenga en cuenta que estos no serán todos válidos en todo momento, y son muy dependientes de su código real a su alrededor:

  1. Inyección SQL de segundo orden: si se reconstruye una consulta SQL en función de los datos recuperados de la base de datos después de escapar , los datos se concatenan sin escape y pueden inyectarse indirectamente en SQL. Ver
  2. Truncamiento de cadenas - (un poco más complicado) - Escenario: tiene dos campos, digamos un nombre de usuario y una contraseña, y el SQL los concatena a ambos. Y ambos campos (o solo el primero) tienen un límite estricto de longitud. Por ejemplo, el nombre de usuario está limitado a 20 caracteres. Digamos que tiene este código:
username = left(Replace(sInput, "'", "''"), 20)

Entonces lo que obtienes es el nombre de usuario, escapado y luego recortado a 20 caracteres. El problema aquí: pegaré mi cita en el vigésimo carácter (por ejemplo, después de las 19 a), y su cita de escape se recortará (en el carácter 21). Luego el SQL

sSQL = "select * from USERS where username = '" + username + "'  and password = '" + password + "'"

combinado con el nombre de usuario con formato incorrecto mencionado anteriormente dará como resultado que la contraseña ya esté fuera de las comillas, y solo contendrá la carga útil directamente.
 3. Contrabando de Unicode: en ciertas situaciones, es posible pasar un carácter Unicode de alto nivel que parece como una cita, pero no - hasta que llega al base de datos, donde de repente es . Ya que no es una cita cuando la valida, será fácil ... Consulte mi respuesta anterior para obtener más detalles y enlace a la investigación original.

En pocas palabras: nunca haga una consulta escapando usted mismo. Estás obligado a equivocarte. En su lugar, use consultas parametrizadas, o si no puede hacer eso por alguna razón, use una biblioteca existente que lo haga por usted. No hay razón para hacerlo tú mismo.

Me doy cuenta de que esto es mucho tiempo después de que se hizo la pregunta, pero ...

Una forma de lanzar un ataque al procedimiento de "citar el argumento" es mediante el truncamiento de cadenas. Según MSDN, en SQL Server 2000 SP4 (y SQL Server 2005 SP1), una cadena demasiado larga se truncará silenciosamente.

Cuando usted cita una cadena, la cadena aumenta de tamaño. Se repite cada apóstrofe. Esto se puede usar para empujar partes del SQL fuera del búfer. Por lo tanto, podría efectivamente eliminar partes de una cláusula where.

Esto probablemente sería más útil en un escenario de página de 'administrador de usuarios' en el que podría abusar de la declaración 'actualizar' para no hacer todas las verificaciones que se suponía que debía hacer.

Entonces, si decides citar todos los argumentos, asegúrate de saber qué sucede con los tamaños de cadena y asegúrate de que no te encuentres con el truncamiento.

Recomendaría ir con los parámetros. Siempre. Solo desearía poder aplicar eso en la base de datos. Y como efecto secundario, es más probable que obtengas mejores resultados de caché porque la mayoría de las declaraciones tienen el mismo aspecto. (Esto fue cierto en Oracle 8)

El saneamiento de entrada no es algo que quieras medio tonto. Usa todo tu trasero. Usa expresiones regulares en los campos de texto. Pruebe a aplicar sus números al tipo numérico adecuado e informe un error de validación si no funciona. Es muy fácil buscar patrones de ataque en su entrada, como '-. Suponga que todas las entradas del usuario son hostiles.

Utilicé esta técnica al tratar con la funcionalidad de 'búsqueda avanzada', donde construir una consulta desde cero era la única respuesta viable. (Ejemplo: permitir que el usuario busque productos basados ??en un conjunto ilimitado de restricciones en los atributos del producto, mostrando columnas y sus valores permitidos como controles GUI para reducir el umbral de aprendizaje para los usuarios).

En sí mismo es seguro AFAIK. Sin embargo, como señaló otro respondedor, es posible que también deba lidiar con el escape de retroceso (aunque no al pasar la consulta a SQL Server usando ADO o ADO.NET, al menos, no puede responder por todas las bases de datos o tecnologías).

El inconveniente es que realmente debe estar seguro de qué cadenas contienen información del usuario (siempre potencialmente maliciosa) y qué cadenas son consultas SQL válidas. Una de las trampas es si usa valores de la base de datos: ¿fueron originalmente proporcionados por el usuario? Si es así, también deben escapar. Mi respuesta es tratar de desinfectar lo más tarde posible (¡pero no más tarde!) Al construir la consulta SQL.

Sin embargo, en la mayoría de los casos, el enlace de parámetros es el camino a seguir: es más simple.

Es una mala idea de todos modos, como parece saber.

¿Qué pasa con algo como escapar de la cita en una cadena como esta: \ '

Su reemplazo resultaría en: \ ''

Si la barra invertida escapa a la primera cita, entonces la segunda cita ha terminado la cadena.

Respuesta simple: funcionará a veces, pero no todo el tiempo. Desea utilizar la validación de lista blanca en todo , pero me doy cuenta de que eso no siempre es posible, por lo que está obligado a elegir la mejor lista negra. Del mismo modo, desea utilizar procesos almacenados parametrizados en todo , pero una vez más, eso no siempre es posible, por lo que se ve obligado a usar sp_execute con parámetros.

Hay formas de evitar cualquier lista negra utilizable que se te ocurra (y algunas listas blancas también).

Un informe decente está aquí: http://www.owasp.org/index. php / Top_10_2007-A2

Si necesita hacer esto como una solución rápida para darle tiempo para obtener uno real, hágalo. Pero no creas que estás a salvo.

Hay dos formas de hacerlo, sin excepciones, para estar a salvo de las inyecciones de SQL; Declaraciones preparadas o procedimientos almacenados pre-calibrados.

Si tiene consultas parametrizadas disponibles, debe utilizarlas en todo momento. Todo lo que necesita es que una consulta se deslice a través de la red y su base de datos está en riesgo.

Sí, eso debería funcionar hasta que alguien ejecute SET QUOTED_IDENTIFIER OFF y usa una doble cita en ti.

Editar: no es tan simple como no permitir que el usuario malintencionado desactive los identificadores citados:

  

El controlador ODBC de SQL Server Native Client y el proveedor OLE DB de SQL Server Native Client para SQL Server establecen automáticamente QUOTED_IDENTIFIER en ON al conectarse. Esto se puede configurar en las fuentes de datos ODBC, en los atributos de conexión ODBC o en las propiedades de conexión OLE DB. El valor predeterminado para SET QUOTED_IDENTIFIER está APAGADO para conexiones desde aplicaciones de DB-Library.

     

Cuando se crea un procedimiento almacenado, la configuración de SET QUOTED_IDENTIFIER y SET ANSI_NULLS se captura y se usa para las invocaciones posteriores de ese procedimiento almacenado .

     

SET QUOTED_IDENTIFIER también corresponde a la configuración QUOTED_IDENTIFER de ALTER DATABASE.

     

SET QUOTED_IDENTIFIER está configurado en tiempo de análisis . La configuración en tiempo de análisis significa que si la instrucción SET está presente en el procedimiento por lotes o almacenado, surte efecto, independientemente de si la ejecución del código realmente alcanza ese punto; y la instrucción SET entra en vigor antes de que se ejecute cualquier declaración.

Hay muchas formas en que QUOTED_IDENTIFIER puede estar desactivado sin que necesariamente lo sepas. Es cierto que este no es el problema de las armas humeantes que estás buscando, pero es una gran superficie de ataque. Por supuesto, si también escapó de comillas dobles, entonces volveremos a empezar. ;)

Su defensa fallaría si:

  • la consulta está esperando un número en lugar de una cadena
  • había otra forma de representar una comilla simple, incluyendo:
    • una secuencia de escape como \ 039
    • un carácter unicode

(en el último caso, tendría que ser algo que se expandió solo después de haber reemplazado)

Patrick, ¿estás agregando comillas simples alrededor de TODAS las entradas, incluso las entradas numéricas? Si tiene una entrada numérica, pero no pone las comillas simples alrededor, entonces tiene una exposición.

¡Qué código tan feo sería el saneamiento de los comentarios de los usuarios! A continuación, el StrunBuilder clunky para la declaración SQL. El método de declaración preparado da como resultado un código mucho más limpio, y los beneficios de inyección SQL son una adición realmente agradable.

También por qué reinventar la rueda?

En lugar de cambiar una sola cita a (lo que parece) dos comillas simples, ¿por qué no simplemente cambiarlo por un apóstrofe, una cita, o eliminarlo por completo?

De cualquier manera, es un poco un error ... especialmente cuando legítimamente tienes cosas (como nombres) que pueden usar comillas simples ...

NOTA: su método también asume que todos los que trabajan en su aplicación siempre recuerdan desinfectar las entradas antes de que lleguen a la base de datos, lo que probablemente no sea realista la mayor parte del tiempo.

Si bien es posible que encuentre una solución que funcione para las cadenas, para los predicados numéricos también debe asegurarse de que solo estén pasando números (la comprobación simple es ¿se puede analizar como int / double / decimal?).

Es mucho trabajo extra.

Podría funcionar, pero me parece un poco tonto. Recomiendo verificar que cada cadena sea válida probándola contra una expresión regular.

Sí, puedes, si ...

Después de estudiar el tema, creo que la introducción de datos de forma desinfectada como usted sugirió es segura, pero solo bajo estas reglas:

  1. nunca permite que los valores de cadena provenientes de los usuarios se conviertan en algo más que literales de cadena (es decir, evite dar la opción de configuración: " Ingrese nombres / expresiones de columnas SQL adicionales aquí: "). Tipos de valor distintos de cadenas (números, fechas, ...): conviértalos a sus tipos de datos nativos y proporcione una rutina para el literal SQL de cada tipo de datos.

    • Las declaraciones SQL son problemáticas para validar
  2. puede usar las columnas nvarchar / nchar (y los literales de cadena de prefijo con N ) O los valores límite que entran en varchar / char a caracteres ASCII solamente (por ejemplo, lanzar una excepción al crear una declaración SQL)

    • de esta manera, evitará la conversión automática de apóstrofes de CHAR (700) a CHAR (39) (y tal vez otros hacks Unicode similares)
  3. siempre validas la longitud del valor para que se ajuste a la longitud real de la columna (lanza la excepción si es más larga)

    • hubo un defecto conocido en SQL Server que permitió eludir el error de SQL generado en el truncamiento (lo que lleva a un truncamiento silencioso)
  4. te aseguras de que SET QUOTED_IDENTIFIER sea siempre ON

    • cuidado, se toma en efecto en el tiempo de análisis, es decir, incluso en secciones de código inaccesibles

Cumpliendo con estos 4 puntos, debe estar seguro. Si viola alguno de ellos, se abre una forma de inyección SQL.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top