¿Cómo ayuda la limitación del número de resultados de una consulta SQL?

2

Esta respuesta altamente votada afirma que como protección contra la inyección de SQL, uno debería

  

siempre use parámetros enlazados y limite la cantidad de resultados que se devuelven

(énfasis mío)

Obviamente, el consejo para limitar el número de resultados no se puede aplicar a ciegas. Si su aplicación requiere que todos los resultados funcionen correctamente en un contexto específico, entonces colocar un límite estricto en los resultados puede causar errores. (Esta es una situación con la que estoy lidiando actualmente. Estoy detallando todos los errores en una aplicación porque esto se hizo en silencio por algunas funciones muy utilizadas). No creo que la respuesta sugiera que la aplicación debería estar diseñada para Los resultados de la página, ya sea, ya que la paginación solo significa que un atacante debe realizar varias búsquedas para obtener los resultados que desea.

La pregunta acerca de qué es la inyección SQL no hace mención de esto como defensa.

¿De qué está hablando esta respuesta? ¿La limitación de resultados reduce mi riesgo asociado con la inyección de SQL? ¿En qué contextos se puede usar si es así?

(Pequeña aclaración preventiva: estoy, por supuesto, 1000% a bordo con el uso de consultas parametrizadas en cada entrada de usuario).

    
pregunta jpmc26 20.10.2017 - 02:04
fuente

1 respuesta

2
  

limita la cantidad de resultados que se devuelven

Esto suena un poco tonto. Es posible explotar una inyección SQL incluso si la consulta no devuelve ningún resultado, o incluso si los resultados no se muestran en absoluto.

En caso de que se pregunte: si puede inyectar algo que pueda desencadenar un error de la base de datos dependiente de los datos, o un error de la aplicación, o hacer que devuelva cero o un resultado, puede consultar la base de datos para las pruebas booleanas, como:

email REGEXP "^[a-k]"? yes
email REGEXP "^[a-g]"? yes
email REGEXP "^[a-d]"? no
email REGEXP "^[e-g]"? yes, no need to ask!

sigue reduciéndolo ... Luego, con un poco de paciencia, un guión, una dicotomía y expresiones regulares, puedes volcar cualquier línea en cualquier tabla, incluido el esquema de información, que debería ser tu primer objetivo por cierto. Hace un tiempo, un tipo se jactó en un foro francés sobre la seguridad de su sitio con una de esas vulnerabilidades. Dos horas después, tenía todas las contraseñas, que estaban almacenadas en texto claro dentro de una copia de seguridad de una tabla en algún lugar dentro de un esquema que había olvidado. ..

También, dijo que la víctima estaba dispuesta a pensar que las citas mágicas eran seguras. Así que no pude inyectar ningún 'pero ... CHAR () puede construir cualquier cadena sin ningún tipo' ...

También es posible hacer que el servidor ejecute una consulta muy larga e intensiva (un tipo de ataque de DOS) que no devuelva ningún resultado, por lo que el LÍMITE tampoco ayudará en este caso. Para LULZ extra, puede hacer que la consulta bloquee cada fila con un SELECT FOR UPDATE y luego cuelgue hasta que la página agote el tiempo de espera con una gran UNION. Este DOS es todo el sitio web.

También si puede inyectar un "-" o un ";" para comentar el resto de la consulta, entonces el LÍMITE es, por supuesto, comentado. Clásico atemporal, set id="OR 1; -" en el foro "elimina tu propia publicación" thingie and ... tada!

DELETE FROM posts WHERE id=1234 OR 1 -- LIMIT 1

OMI, la mejor manera de evitar la inyección de SQL es usar un marco o una biblioteca de bases de datos que lo haga más conveniente y más rápido para usar el método seguro en lugar del método inseguro. Entonces, no hay tentación de cortar esquinas ...

Algunas implementaciones de parámetros enlazados fallan en esta prueba, porque son inconvenientes de usar y / o mal diseñados. Considere el siguiente ejemplo de PHP / PDO:

$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);
$stmt->execute();

Esto usa tres líneas de código en lugar de una. Peor aún, los tipos de variables están ocultos en algún lugar dentro de la llamada bind_param, son posicionales y dentro de una cadena que se duplicará en todo el código. Además, si tiene 20 columnas, ¿cuál es el int cuando la cadena de tipo es "sssssssssssssisss"? OK, mueva el cursor, cuente los caracteres, uno, dos, tres ... También el tipo viene dado por un carácter, por lo que cuando use postgres y su columna sea del tipo "matriz de ints" o "polígono", no lo hará. trabaje para tener que volver a un método inseguro, quizás citar manualmente ... aaargh ...

$req = $bdd->prepare('INSERT INTO decisions(decision, numero, publique, date, ip) VALUES(:decision, :numero, :publique, NOW(), :ip)');
$req->execute(array(
    'decision' => $want,
    'numero' => $nombremagique,
    'publique' => $publique,
    'ip' => $ip
    ));

Esto es un paso adelante, ya que los parámetros ahora están nombrados en lugar de posicionales, lo cual es mucho más fácil de usar. Pero aún tiene que preparar / ejecutar, lo que (a menos que utilice declaraciones falsas preparadas de PDO que creo que solo funcionan en MySQL) da como resultado dos viajes de ida y vuelta a la base de datos, lo que significa que el método seguro es más lento que el inseguro. Esto también es un fracaso, porque el método seguro debería ser el mejor, el más fácil de usar y también el más rápido si queremos evitar la tentación de cortar esquinas ...

Una opción mucho mejor en este caso sería usar un ORM. Crea un objeto y haz object- > insert (). Si el ORM es bueno, comprobará los tipos y manejará las consultas de manera segura.

Para consultas sin formato, mi interfaz favorita es DBAPI de python, usaré psycopg2 como ejemplo:

conn.execute("SELECT * FROM ... WHERE firstname=%s AND lastname=%s", (val1,val2))

Solo requiere una línea, una base de datos de ida y vuelta, es rápido, los argumentos se escapan y se convierten automáticamente de acuerdo a sus tipos, incluso los tipos de usuarios pueden tener codificadores / decodificadores personalizados agregados, maneja Unicode, básicamente lo hace todo.

data = {'firstname': 'Bob', 'lastname': 'Smith'}
conn.execute("SELECT * FROM ... WHERE firstname=%(firstname)s AND lastname=%(lastname)s", data)

También se pueden utilizar argumentos con nombre. Esto suele ser más conveniente, ya que los argumentos generalmente provendrán de algo así como una biblioteca de formularios, que los entregará empaquetados en una matriz dict / asociativa.

Por lo tanto, para responder a tu pregunta, desconfiaría de los datos aleatorios como "agregar un límite"; un enfoque sistemático es mucho mejor, como dije, la interfaz utilizada para consultar el DB debería hacer que la elección segura sea la elección obvia, más rápida, más fácil, más conveniente, etc.

    
respondido por el peufeu 20.10.2017 - 12:38
fuente

Lea otras preguntas en las etiquetas