sqlmap - ¿Puedo protegerme contra la inyección de SQL escapando la comilla simple y la entrada del usuario circundante con comillas simples?
sql injection test (18)
Sí, puedes, si ...
Después de estudiar el tema, creo que la información desinfectada que sugirió es segura, pero solo bajo estas reglas:
nunca permite que los valores de cadena provenientes de los usuarios se conviertan en algo más que cadenas literales (es decir, evite dar la opción de configuración: "Ingrese nombres / expresiones de columna SQL adicionales aquí:"). Tipos de valores distintos de cadenas (números, fechas, ...): conviértalos a sus tipos de datos nativos y proporcione una rutina para el literal de SQL de cada tipo de datos.
- Las declaraciones SQL son problemáticas para validar
o bien utiliza columnas
nvarchar
/nchar
(y prefijos literales de cadena conN
) O valores límite que entran en columnasvarchar
/char
a caracteres ASCII solamente (por ejemplo, lanzar excepción al crear una instrucción SQL)- de esta manera evitará la conversión automática de apóstrofes de CHAR (700) a CHAR (39) (y tal vez otros hackers Unicode similares)
siempre valida la longitud del valor para que se ajuste a la longitud real de la columna (excepción de lanzamiento si es más larga)
- hubo un defecto conocido en SQL Server que permite omitir el error SQL lanzado en el truncamiento (lo que lleva a un truncamiento silencioso)
se asegura de que
SET QUOTED_IDENTIFIER
siempre estéON
- cuidado, se toma en cuenta en tiempo de análisis, es decir, incluso en secciones de código inaccesibles
Cumpliendo con estos 4 puntos, deberías estar seguro. Si infringe alguno de ellos, se abre una forma de inyección de SQL.
Me doy cuenta de que las consultas SQL parametrizadas son la forma óptima de desinfectar las entradas de los usuarios cuando se crean consultas que contienen entradas del usuario, pero me pregunto qué hay de malo en tomar las entradas del usuario y escanear las comillas simples y rodear toda la cadena con comillas simples. Aquí está el código:
sSanitizedInput = "''" & Replace(sInput, "''", "''''") & "''"
Cualquier comilla simple que ingrese el usuario se reemplaza por comillas simples dobles, lo que elimina la capacidad del usuario de finalizar la cadena, por lo que cualquier otra cosa que escriba, como punto y coma, signos de porcentaje, etc., será parte de la cadena y no realmente ejecutado como parte del comando. Estamos utilizando Microsoft SQL Server 2000, para lo cual creo que la comilla simple es el único delimitador de cadena y la única manera de escapar del delimitador de cadena, por lo que no hay forma de ejecutar nada de lo que escriba el usuario.
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 parece, alguien más ya lo habría pensado y sería una práctica común. Mi pregunta es esta: ¿qué pasa con 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? La entrada del usuario de muestra que explota esta técnica sería muy útil.
ACTUALIZAR:
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 que es una señal 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 aún no acepté 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 la otra para terminar 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 forma (que he podido encontrar) de escapar una comilla simple es con otra qoute única; las barras invertidas no lo harán. Y a menos que haya una forma de detener el escape de la comilla simple, no se ejecutará ninguna parte del resto de la entrada del usuario porque se tomará como una cadena contigua.
Entiendo que hay mejores formas de desinfectar los comentarios, pero estoy realmente más interesado en saber por qué el método que proporcioné arriba no funcionará. Si alguien sabe de alguna manera específica de montar un ataque de inyección SQL contra este método de desinfección, me encantaría verlo.
¡Qué feo código sería toda esa sanitización de la entrada del usuario! Luego, el torpe StringBuilder 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 de SQL son realmente una buena adición.
Además, ¿por qué reinventar la rueda?
De acuerdo, esta respuesta se relacionará con la actualización de la pregunta:
"Si alguien sabe de alguna manera 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 la barra invertida de MySQL, y teniendo en cuenta que en realidad estamos hablando de MSSQL, en realidad hay 3 formas posibles de SQL aún inyectando su código
sSanitizedInput = "''" & Replace (sInput, "''", "''''") & "''"
Tenga en cuenta que estos no serán todos válidos en todo momento y dependen mucho de su código real:
- Inyección de SQL de segundo orden: si una consulta SQL se reconstruye en base a datos recuperados de la base de datos después de escapar , los datos se concatenan sin protección y pueden indirectamente inyectarse en SQL. Ver
- Truncado de cadena - (un poco más complicado) - Escenario es que tiene dos campos, digamos un nombre de usuario y 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 tienes este código:
username = left(Replace(sInput, "''", "''''"), 20)
Entonces, lo que obtienes es el nombre de usuario, se escapó y luego se recortó a 20 caracteres. El problema aquí es que incluiré mi cita en el 20 ° carácter (por ejemplo, después de 19 a) y su cita de escape se recortará (en el 21 ° carácter). Entonces el SQL
sSQL = "select * from USERS where username = ''" + username + "'' and password = ''" + password + "''"
combinado con el nombre de usuario malformado antes mencionado resultará en que la contraseña ya está fuera de las cotizaciones, y solo contendrá la carga útil directamente.
3. Contrabando Unicode: en ciertas situaciones, es posible pasar un carácter Unicode de alto nivel que parece una cita, pero no lo es , hasta que llega a la base de datos, donde de repente está . Como 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.
De todos modos, es una mala idea, ya que parece que lo sabes.
¿Qué tal algo así como escapar de la cita en una cadena como esta: / ''
Su reemplazo resultaría en: / ''''
Si la barra invertida escapa de la primera comilla, la segunda comilla ha terminado la cadena.
El saneamiento de entrada no es algo que quieras medio asno. Usa tu culo completo Use expresiones regulares en campos de texto. PruebeCalcular sus números al tipo numérico correcto e informe un error de validación si no funciona. Es muy fácil buscar patrones de ataque en su entrada, como ''-. Supongamos que todas las entradas del usuario son hostiles.
En lugar de cambiar una comilla simple a (lo que parece) dos comillas simples, ¿por qué no simplemente cambiarla a un apóstrofo, una comilla o eliminarla por completo?
De cualquier manera, es un poco complicado ... especialmente cuando tienes legítimamente 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.
En pocas palabras: nunca preguntes si te escapas. Estás obligado a obtener algo mal. En su lugar, use consultas parametrizadas, o si no puede hacer eso por algún motivo, use una biblioteca existente que lo haga por usted. No hay razón para hacerlo tú mismo.
En primer lugar, es solo una mala práctica. La validación de entrada siempre es necesaria, pero también es siempre 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 aceptas. Es cierto que esto no siempre es posible, pero en cierta medida siempre debe hacerse.
Algunos trabajos de investigación sobre el tema:
- http://www.imperva.com/docs/WP_SQL_Injection_Protection_LK.pdf
- http://www.it-docs.net/ddata/4954.pdf (Divulgación, esta última fue mía;))
- https://www.owasp.org/images/d/d4/OWASP_IL_2007_SQL_Smuggling.pdf (basado en el documento anterior, que ya no está disponible)
El punto es que cualquier lista negra que usted haga (y las listas blancas demasiado permisivas) puede pasarse por alto. El último enlace a mi documento muestra situaciones en las que se puede eludir incluso el escape de cotizaciones.
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 ocuparse del mantenimiento, y tal vez de una cierta cantidad de gobernanza: ¿cómo se asegura de que se haga bien, en todas partes todo el tiempo?
La forma correcta de hacerlo:
- Validación de la lista blanca: tipo, longitud, formato o valores aceptados
- Si quieres una lista negra, sigue adelante. Citar escaparse es bueno, pero dentro del contexto de las otras mitigaciones.
- Use los objetos de comando y parámetro para crear y validar
- Llamar consultas parametrizadas solamente.
- Mejor aún, use procedimientos almacenados exclusivamente.
- Evite el uso de SQL dinámico y no utilice 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 directamente a las tablas.
- también puede verificar fácilmente que toda la base de código solo acceda al DB a través de SP ...
Hay dos formas de hacerlo, sin excepciones, para estar a salvo de las inyecciones de SQL; declaraciones preparadas o procedimientos almacenados previamente.
He utilizado esta técnica cuando se trata de la funcionalidad de "búsqueda avanzada", donde construir una consulta desde cero era la única respuesta viable. (Ejemplo: permita al usuario buscar productos basados en un conjunto ilimitado de restricciones en los atributos del producto, mostrando columnas y sus valores permitidos como controles de la GUI para reducir el umbral de aprendizaje para los usuarios).
En sí mismo es seguro AFAIK. Sin embargo, como señaló otro contestador, también es posible que tenga que lidiar con el "backspace escaping" (aunque no al pasar la consulta a SQL Server utilizando ADO o ADO.NET, al menos, no puede garantizar todas las bases de datos o tecnologías).
El inconveniente es que realmente debe estar seguro de qué cadenas contienen la entrada del usuario (siempre potencialmente malicioso) y qué cadenas son consultas SQL válidas. Una de las trampas es si usa valores de la base de datos: ¿esos valores fueron originalmente provistos por el usuario? Si es así, también deben ser escapados. Mi respuesta es intentar 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 simplemente más simple.
Me doy cuenta de que esto es mucho tiempo después de que se hizo la pregunta, pero ...
Una forma de lanzar un ataque sobre el procedimiento ''quote the argument'' es con el truncamiento de cadena. Según MSDN, en SQL Server 2000 SP4 (y SQL Server 2005 SP1), una cadena demasiado larga se truncará silenciosamente.
Cuando citas una cadena, la cuerda aumenta de tamaño. Cada apóstrofo se repite. Esto se puede usar para enviar partes del SQL fuera del búfer. Entonces, podrías recortar partes de una cláusula where.
Probablemente, esto sea más útil en un escenario de página de "administración de usuario" donde podría abusar de la declaración de "actualización" para no hacer todas las comprobaciones 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 se ejecute en truncamiento.
Yo recomendaría ir con parámetros. Siempre. Solo desearía poder hacer cumplir eso en la base de datos. Y como efecto colateral, es más probable que obtenga mejores resultados de caché porque la mayoría de las declaraciones tienen el mismo aspecto. (Esto fue cierto en Oracle 8)
Patrick, ¿estás agregando comillas simples a TODA la entrada, incluso a la entrada numérica? Si tiene una entrada numérica, pero no está poniendo las comillas simples a su alrededor, entonces tiene una exposición.
Podría funcionar, pero me parece un poco chistoso. Recomiendo verificar que cada cadena sea válida probándola contra una expresión regular en su lugar.
Respuesta simple: funcionará a veces, pero no todo el tiempo. Desea utilizar la validación de lista blanca en todo lo que hace, pero me doy cuenta de que no siempre es posible, por lo que está obligado a ir con la mejor lista negra de adivinanzas. Del mismo modo, desea utilizar procesos almacenados parametrizados en todo , pero una vez más, eso no siempre es posible, por lo que está obligado a utilizar sp_execute con parámetros.
Hay formas de solucionar cualquier lista negra que se pueda usar (y algunas listas blancas también).
Un escrito 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 a uno real, hágalo. Pero no pienses que estás a salvo.
Sí, eso debería funcionar hasta que alguien ejecute SET QUOTED_IDENTIFIER OFF y use una comilla doble para ti.
Editar: No es tan simple como no permitir que el usuario malintencionado apague los identificadores entre comillas:
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 orígenes de datos ODBC, en atributos de conexión ODBC o en propiedades de conexión OLE DB. El valor predeterminado para SET QUOTED_IDENTIFIER es OFF para las conexiones de las aplicaciones DB-Library.
Cuando se crea un procedimiento almacenado, las configuraciones SET QUOTED_IDENTIFIER y SET ANSI_NULLS se capturan y se usan para invocaciones posteriores de ese procedimiento almacenado .
SET QUOTED_IDENTIFIER también corresponde a la configuración QUOTED_IDENTIFER de ALTER DATABASE.
SET QUOTED_IDENTIFIER se establece en el tiempo de análisis . Establecer en tiempo de análisis significa que si la instrucción SET está presente en el lote o procedimiento almacenado, surte efecto, independientemente de si la ejecución del código llega realmente a ese punto; y la instrucción SET se aplica antes de que se ejecuten las instrucciones.
Hay muchas formas en que QUOTED_IDENTIFIER podría estar apagado sin que necesariamente lo sepas. Es cierto, este no es el exploit de la pistola humeante que está buscando, pero es una superficie de ataque bastante grande. Por supuesto, si también escapó las comillas dobles, entonces estamos de vuelta donde comenzamos. ;)
Si bien puede encontrar una solución que funcione para cadenas, para los predicados numéricos también debe asegurarse de que solo estén pasando números (¿se puede analizar la verificación simple como int / doble / decimal?).
Es mucho trabajo extra.
Si tiene consultas parametrizadas disponibles, debería usarlas en todo momento. Solo se necesita una consulta para deslizarse a través de la red y su DB está en riesgo.
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 caracter unicode
(en el último caso, tendría que ser algo que se expandió solo después de haber hecho su reemplazo)