En términos de inyección SQL , entiendo completamente la necesidad de parametrizar un string
parámetro; ese es uno de los trucos más antiguos del libro. Pero, ¿cuándo se puede justificar no parametrizar un SqlCommand
? ¿Algún tipo de datos se considera "seguro" para no parametrizar?
Por ejemplo: Yo no me considero en cualquier lugar cerca de un experto en SQL, pero no puedo pensar en ningún casos en los que sería potencialmente vulnerable a la inyección SQL para aceptar una bool
o un int
y simplemente concatenar la derecha por la consulta.
¿Es correcta mi suposición o eso podría dejar una gran vulnerabilidad de seguridad en mi programa?
Para aclarar, esta pregunta está etiquetada C#que es un lenguaje fuertemente tipado; cuando digo "parámetro", pienso en algo como public int Query(int id)
.
fuente
Respuestas:
Creo que es seguro ... técnicamente , pero es un hábito terrible. ¿Realmente quieres escribir consultas como esta?
También lo deja vulnerable en la situación en la que un tipo cambia de un número entero a una cadena (piense en el número de empleado que, a pesar de su nombre, puede contener letras).
Por lo tanto, cambiamos el tipo de EmployeeNumber de
int
astring
, pero olvidamos actualizar nuestras consultas SQL. ¡Ups!fuente
AddWithValue
ya? blogs.msmvps.com/jcoehoorn/blog/2014/05/12/…AddWithValue
menos que tenga un mapeo de tipos de bases de datos como parte de la construcción dinámica de la declaración. Supongo que tiene una lista de columnas de destino y, como parte del diccionario, podría tener los tipos si lo desea. De lo contrario, solo recibe el impacto del rendimiento. En última instancia, creo que es solo una buena información.AddWithValue
Ya podemos dejar de consumir ?" realmente debería ser "si conoce el tipo, entonces debe abstenerse de usarAddWithValue
.Cuando se utiliza una plataforma inflexible de tipos en un equipo a controlar (como un servidor web), puede evitar la inyección de código para las consultas con solamente
bool
,DateTime
oint
los valores (y otra numérica). Lo que preocupa son los problemas de rendimiento causados por obligar al servidor SQL a volver a compilar cada consulta y evitando que obtenga buenas estadísticas sobre qué consultas se ejecutan con qué frecuencia (lo que perjudica la administración de la caché).Pero esa parte de "en una computadora usted controla" es importante, porque de lo contrario un usuario puede cambiar el comportamiento utilizado por el sistema para generar cadenas a partir de esos valores para incluir texto arbitrario.
También me gusta pensar a largo plazo. ¿Qué sucede cuando la base de código fuertemente tipada vieja y rota de hoy se transfiere a través de la traducción automática al lenguaje dinámico nuevo y de repente pierde la verificación de tipo, pero aún no tiene todas las pruebas unitarias correctas para el código dinámico? ?
Realmente, no hay una buena razón para no usar parámetros de consulta para estos valores. Es la forma correcta de hacerlo. Continúe y codifique los valores en la cadena sql cuando realmente sean constantes, pero de lo contrario, ¿por qué no usar simplemente un parámetro? No es difícil.
En última instancia, no llamaría a esto un error , per se, pero lo llamaría un olor : algo que no llega a ser un error por sí mismo, pero es una fuerte indicación de que los errores están cerca, o lo estarán eventualmente. Un buen código evita dejar olores, y cualquier buena herramienta de análisis estático lo marcará.
Agregaré que, desafortunadamente, este no es el tipo de argumento que puedes ganar directamente. Suena como una situación en la que estar "en lo correcto" ya no es suficiente, y no es probable que pisar los pies de sus compañeros de trabajo para solucionar este problema por su cuenta promueva una buena dinámica de equipo; en última instancia, podría doler más de lo que ayuda. Un mejor enfoque en este caso puede ser promover el uso de una herramienta de análisis estático. Eso daría legitimidad y credibilidad a los esfuerzos dirigidos y retrocediendo y arreglando el código existente.
fuente
DateTime
oint
En algunos casos, ES posible realizar un ataque de inyección SQL con variables no parametrizadas (concatenadas) que no sean valores de cadena; consulte este artículo de Jon: http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables -cultura / .
El problema es que cuando
ToString
se llama, algún proveedor de cultura personalizada puede transformar un parámetro que no es una cadena en su representación de cadena, lo que inyecta algo de SQL en la consulta.fuente
int
s?"CultureInfo
difícil saber por qué necesitaría la inyección SQL de todos modos.Esto no es seguro incluso para tipos que no son cadenas. Utilice siempre parámetros. Período.
Considere el siguiente ejemplo de código:
A primera vista, el código parece seguro, pero todo cambia si realiza algunos cambios en la configuración regional de Windows y agrega una inyección en formato de fecha corta:
Ahora el texto del comando resultante se ve así:
Se puede hacer lo mismo para el
int
tipo, ya que el usuario puede definir un signo negativo personalizado que se puede cambiar fácilmente a una inyección SQL.Se podría argumentar que la cultura invariante debería usarse en lugar de la cultura actual, pero he visto concatenaciones de cadenas como esta tantas veces y es bastante fácil pasar por alto cuando se concatenan cadenas con objetos usando
+
.fuente
int
tipo?"SELECT * FROM Table1 WHERE Id =" + intVariable.ToString ()
Seguridad
Está bien.
Los atacantes no pueden inyectar nada en su variable int escrita.
Rendimiento
incorrecto.
Es mejor usar parámetros, por lo que la consulta se compilará una vez y se almacenará en caché para el próximo uso. La próxima vez, incluso con diferentes valores de parámetros, la consulta se almacena en caché y no necesita compilarse en el servidor de la base de datos.
Estilo de codificación
Mala práctica.
"SELECT * FROM Product WHERE Id =" + TextBox1.Text
Aunque no es tu pregunta, pero quizás sea útil para futuros lectores:
¡Desastre de seguridad !
Incluso cuando el
Id
campo es un número entero, su consulta puede estar sujeta a inyección SQL. Suponga que tiene una consulta en su aplicación"SELECT * FROM Table1 WHERE Id=" + TextBox1.Text
, un atacante puede insertar en el cuadro de texto1; DELETE Table1
y la consulta será:Si no desea usar una consulta parametrizada aquí, debe usar valores escritos:
Tu pregunta
Creo que cambiar esos códigos no es una pérdida de tiempo. De hecho, se recomienda el cambio.
si su compañero de trabajo usa variables int, no tiene ningún riesgo de seguridad, pero creo que cambiar esos códigos no es una pérdida de tiempo y, de hecho, se recomienda cambiar esos códigos. Hace que el código sea más legible, más fácil de mantener y hace que la ejecución sea más rápida.
fuente
.ToString()
está determinado por un elemento de configuración del sistema operativo que es fácil de cambiar para incluir texto arbitrario.En realidad, hay dos preguntas en una. Y la pregunta del título tiene muy poco que ver con las preocupaciones expresadas por el OP en los comentarios posteriores.
Aunque me doy cuenta de que para el OP es su caso particular lo que importa, para los lectores que vienen de Google, es importante responder a la pregunta más general, que puede expresarse como "¿Es la concatenación tan segura como las declaraciones preparadas si me asegure que cada literal que estoy concatenando es seguro? ". Entonces, me gustaría concentrarme en este último. Y la respuesta es
Definitivamente no.
La explicación no es tan directa como le gustaría a la mayoría de los lectores, pero haré todo lo posible.
He estado reflexionando sobre el tema durante un tiempo, lo que resultó en el artículo (aunque basado en el entorno PHP) donde traté de resumir todo. Se me ocurrió que la cuestión de la protección contra la inyección de SQL a menudo se elude hacia algunos temas relacionados pero más estrechos, como el escape de cadenas, la conversión de tipos y demás. Aunque algunas de las medidas pueden considerarse seguras cuando se toman por sí mismas, no existe un sistema ni una regla simple a seguir. Lo que hace que sea un terreno muy resbaladizo, poniendo demasiado en la atención y experiencia del desarrollador.
La cuestión de la inyección de SQL no se puede simplificar a una cuestión de sintaxis en particular. Es más amplio de lo que pensaba el desarrollador promedio. También es una cuestión metodológica . No se trata solo de "Qué formato en particular debemos aplicar", sino también de " Cómo se debe hacer".
(Desde este punto de vista, un artículo de Jon Skeet citado en la otra respuesta lo está haciendo bastante mal que bien, ya que nuevamente es quisquilloso en algún caso límite, se concentra en un problema de sintaxis en particular y no aborda el problema en su totalidad).
Cuando intenta abordar la cuestión de la protección no como un todo, sino como un conjunto de diferentes problemas de sintaxis, se enfrenta a una multitud de problemas.
A diferencia de ese lío, las declaraciones preparadas son de hecho El Santo Grial:
(Pensando más, descubrí que el conjunto actual de marcadores de posición no es suficiente para las necesidades de la vida real y debe extenderse, tanto para estructuras de datos complejas, como matrices, e incluso palabras clave SQL o identificadores, que a veces deben agregarse al consulta dinámicamente también, pero un desarrollador se queda desarmado para tal caso y se ve obligado a recurrir a la concatenación de cadenas, pero eso es otra cuestión).
Curiosamente, la controversia de esta pregunta es provocada por la naturaleza muy controvertida de Stack Overflow. La idea del sitio es hacer uso de preguntas particulares de los usuarios que preguntan directamente para lograr el objetivo de tener una base de datos de respuestas de propósito general adecuada para los usuarios que provienen de la búsqueda . La idea no es mala per se , pero falla en una situación como esta: cuando un usuario hace una pregunta muy limitada , particularmente para tener una discusión en una disputa con un colega (o para decidir si vale la pena refactorizar el código). Si bien la mayoría de los participantes experimentados están tratando de escribir una respuesta, teniendo en cuenta la misión de Stack Overflow en su totalidad, lo que hace que su respuesta sea buena para tantos lectores como sea posible, no solo para el OP.
fuente
No pensemos solo en consideraciones de seguridad o de seguridad de tipos.
La razón por la que utiliza consultas parametrizadas es para mejorar el rendimiento a nivel de la base de datos. Desde la perspectiva de una base de datos, una consulta parametrizada es una consulta en el búfer SQL (para usar la terminología de Oracle, aunque imagino que todas las bases de datos tienen un concepto similar internamente). Entonces, la base de datos puede contener una cierta cantidad de consultas en la memoria, preparadas y listas para ejecutarse. Estas consultas no necesitan ser analizadas y serán más rápidas. Las consultas que se ejecutan con frecuencia suelen estar en el búfer y no es necesario analizarlas cada vez que se utilizan.
A NO SER QUE
Alguien no usa consultas parametrizadas. En este caso, el búfer se vacía continuamente a través de un flujo de consultas casi idénticas, cada una de las cuales debe ser analizada y ejecutada por el motor de la base de datos y el rendimiento se ve afectado por completo, ya que incluso las consultas que se ejecutan con frecuencia terminan siendo analizadas varias veces al día. día. Me he ganado la vida sintonizando bases de datos y esta ha sido una de las mayores fuentes de fruta madura.
AHORA
Para responder a su pregunta, SI su consulta tiene una pequeña cantidad de valores numéricos distintos, probablemente no causará problemas y, de hecho, puede mejorar el rendimiento infinitesimalmente. Sin embargo, si hay potencialmente cientos de valores y la consulta recibe muchas llamadas, afectará el rendimiento de su sistema, así que no lo haga.
Sí, puede aumentar el búfer SQL, pero siempre es a expensas de otros usos más críticos de la memoria, como el almacenamiento en caché de índices o datos. Moraleja, use consultas parametrizadas de manera bastante religiosa para que pueda optimizar su base de datos y usar más memoria del servidor para las cosas que importan ...
fuente
Para agregar algo de información a la respuesta de Maciek:
Es fácil alterar la información cultural de una aplicación de terceros .NET llamando a la función principal del ensamblado por reflexión:
Esto solo funciona si la función principal de BobbysApp es pública. Si Main no es público, puede haber otras funciones públicas a las que podría llamar.
fuente
En mi opinión, si puede garantizar que el parámetro con el que trabaja nunca contendrá una cadena, es seguro, pero no lo haría en ningún caso. Además, verá una ligera caída en el rendimiento debido al hecho de que está realizando una concatenación. La pregunta que te haría es ¿por qué no quieres usar parámetros?
fuente
Está bien pero nunca es seguro ... y la seguridad siempre depende de las entradas, por ejemplo, si el objeto de entrada es TextBox, los atacantes pueden hacer algo complicado ya que el cuadro de texto puede aceptar cadenas, por lo que hay que poner algún tipo de validación / conversión para poder evitar que los usuarios ingresen incorrectamente. Pero la cosa es que no es seguro. Tan simple como eso.
fuente
No, puede obtener un ataque de inyección SQL de esa manera. He escrito un artículo antiguo en turco que muestra cómo aquí . Ejemplo de artículo en PHP y MySQL pero el concepto funciona igual en C # y SQL Server.
Básicamente atacas de la siguiente manera. Consideremos que tiene una página que muestra información de acuerdo con el valor de ID de número entero. No ha parametrizado este valor, como se muestra a continuación.
Bien, supongo que estás usando MySQL y ataco de la siguiente manera.
Tenga en cuenta que aquí el valor inyectado no es una cadena. Estamos cambiando el valor char a int usando la función ASCII. Puede lograr lo mismo en SQL Server usando "CAST (YourVarcharCol AS INT)".
Después de eso, uso las funciones de longitud y subcadena para encontrar el nombre de su base de datos.
Luego, usando el nombre de la base de datos, comienza a obtener nombres de tablas en la base de datos.
Por supuesto que tienes que automatizar este proceso, ya que solo obtienes UN carácter por consulta. Pero puedes automatizarlo fácilmente. Mi artículo muestra un ejemplo en watir . Utilizando solo una página y sin valor de ID parametrizado. Puedo aprender cada nombre de tabla en su base de datos. Después de eso puedo buscar tablas importantes. Llevará tiempo, pero es factible.
fuente
public void QuerySomething(int id) { // this will only accept an integer }
Bueno ... una cosa es segura: la seguridad NO está bien, cuando concatenas una cadena (tomada por el usuario) con tu cadena de comandos SQL. No importa siempre que la cláusula where se refiera a un Entero oa cualquier tipo; pueden producirse inyecciones.
Lo que importa en SQL Injection es el tipo de datos de la variable que solía obtener el valor del usuario.
Suponiendo que tenemos un número entero en la cláusula where y:
la variable de usuario es una cadena. Entonces está bien, no es muy fácil de inyectar (usando UNION) pero es muy fácil pasar por alto usando 'OR 1 = 1' - como ataques ...
Si la variable de usuario es un número entero. Entonces, nuevamente podemos 'probar' la fuerza del sistema pasando pruebas de números grandes inusuales para detectar fallas del sistema o incluso para un desbordamiento de búfer oculto (en la cadena final) ...;)
Quizás los parámetros de las consultas o (mejor aún, en mi opinión) de los Procedimientos almacenados no son 100% seguros para las amenazas, pero son la medida menos necesaria (o la más elemental si lo prefiere) para minimizarlas.
fuente