2012-06-07 10 views
5

AntecedentesLa implementación de un sistema de unión "configurable", con seguridad

Hola, Estoy desarrollando una herramienta experimental/educativa en PHP y MySQL. Soy nuevo en SQL, pero quiero hacer las cosas bien desde el principio. Estoy usando sentencias preparadas por PDO para todas las sustituciones de variables, y backticking en cualquier lugar posible (por lo tanto, como entiendo, no será portátil para bases de datos que no sean de MySQL). En cuanto a mi problema, tengo una idea de cómo proceder, pero me llevará varias horas implementarlo (soy nuevo incluso con la sintaxis de SQL), mientras tanto pensé que primero haría una pregunta solo en caso de que alguien pueda gritar: "¡Esta no es la manera de hacerlo!" y ahorrame horas de esfuerzo

Problema

me gustaría crear una interfaz donde un usuario podría seleccionar de menús desplegables:

  1. una mesa A,
  2. uno o más campos en esa tabla, por ejemplo A.x y A.y,
  3. una mesa B,
  4. uno o más campos en esa tabla, por ejemplo B.z y B.y,

y tras la presentación del código sería realizar una combinación interna, igualando cada campo, respectivamente, por ejemplo A.x = B.z, A.y = B.y, etc. y devuelve todas las filas coincidentes.

Mi plan es simplemente generar una declaración SQL INNER JOIN, recorriendo los campos e insertando marcadores de posición (?), vinculando los parámetros respectivos y finalmente ejecutando la instrucción.

¿Hay una manera más fácil de hacer esto? ¿Hay una mejor manera de hacer esto? ¿Será esto de alguna manera explotable?

Muchas gracias, de antemano. Si nadie responde antes de que termine (dudoso), publicaré mi solución.

Misc.

Supongamos que I validará

  1. que el usuario selecciona un número igual de campos entre A y B,
  2. que existen los campos y tablas,
  3. etc.

y que los nombres de los campos no necesitan ser idénticos: se emparejarán en orden. (¡Señale cualquier otro detalle del que pueda no estar enterado!)

Finalmente, el objetivo es que estas selecciones se guarden en una tabla de "configuraciones". En efecto, los usuarios crean "vistas" que les gustaría ver cada vez que vuelven.

+0

+1 pregunta realmente informativa ... manera de ir. – Wh1T3h4Ck5

Respuesta

2

¡Estás haciendo tanto bien que realmente me siento culpable al señalar que estás haciendo algo mal! :)

Solo puede usar sentencias preparadas para parametrizar valores de campo —, no identificadores de SQL como nombres de columnas o tablas. Por lo tanto, no podrá pasar A.x, B.z etc. en sus criterios JOIN por medio de los parámetros de declaraciones preparadas: debe hacer lo que parece terriblemente incorrecto y directamente concatenarlos en su cadena de SQL.

Sin embargo, no todo está perdido. En algún orden vaga de preferencia, usted puede:

  1. presentar al usuario una lista de opciones, desde la que, posteriormente, volver a montar el SQL:

    <select name="join_a"> 
        <option value="1">x</option> 
        <option value="2">y</option> 
    </select> 
    <select name="join_b"> 
        <option value="1">z</option> 
        <option value="2">y</option> 
    </select> 
    

    A continuación, el controlador de formulario:

    switch ($_POST['join_a']) { 
        case 1: $acol = 'x'; break; 
        case 2: $acol = 'y'; break; 
        default: die('Invalid input'); 
    } 
    switch ($_POST['join_b']) { 
        case 1: $bcol = 'z'; break; 
        case 2: $bcol = 'y'; break; 
        default: die('Invalid input'); 
    } 
    
    $sql .= "FROM A JOIN B ON A.$acol = B.$bcol"; 
    

    Este enfoque tiene la ventaja de que, a menos que comprometa PHP (en cuyo caso tendrá preocupaciones mucho más grandes que la inyección de SQL), SQL arbitrario absolutamente no puede encuentra su camino en su RDBMS.

  2. Asegúrese de que la entrada del usuario coincide con uno de los valores esperados:

    <select name="join_a"> 
        <option>x</option> 
        <option>y</option> 
    </select> 
    <select name="join_b"> 
        <option>z</option> 
        <option>y</option> 
    </select> 
    

    A continuación, el controlador de formulario:

    if (!in_array($_POST['join_a'], ['x', 'y']) 
    or !in_array($_POST['join_b'], ['z', 'y'])) 
        die('Invalid input'); 
    
    $sql .= "FROM A JOIN B ON A.$_POST[join_a] = B.$_POST[join_b]"; 
    

    Este enfoque se basa en la función de PHP in_array para la seguridad (y también expone a la usuario sus nombres de columna subyacentes, pero dada su aplicación, dudo que sea una preocupación).

  3. realizar alguna limpieza de entrada, tales como:

    mb_regex_encoding($charset); // charset of database connection 
    $sql .= 'FROM A JOIN B ON A.`' . mb_ereg_replace('`', '``', $_POST['join_a']) . '`' 
            . ' = B.`' . mb_ereg_replace('`', '``', $_POST['join_b']) . '`' 
    

    Mientras que aquí citamos la entrada del usuario y reemplaza cualquier intento por parte del usuario para escapar de esa cita, este enfoque podría estar lleno de todo tipo de defectos y vulnerabilidades (ya sea en la función mb_ereg_replace de PHP o en el manejo de MySQL de cadenas especialmente diseñadas dentro de un identificador citado).

    Es ahora mejor si es posible utilizar uno de los métodos anteriores para evitar insertar cadenas definidas por el usuario en el SQL de uno.

+0

Gracias por las palabras alentadoras; aún más para una respuesta tan completa. Sí, finalmente me di cuenta de que los identificadores no se podían parametrizar y, después de navegar, comencé a sospechar que la concatenación podría ser la única forma. Así que lo dejé y me dormí, esperando que alguien supiera una manera mejor que el control/desinfección campo por campo. Desafortunadamente, como usted señala, ¡no parece haberlo! Pero me alegro de poder avanzar con confianza. Veo la ventaja de la preferencia [1]; sin embargo, me gustaría que los usuarios vean los nombres de las columnas, así que voy con [2]. (Y [3] me mantendría despierto por la noche.) ¡Gracias de nuevo! –

1

Suponiendo que la entrada del usuario se limita a seleccionar solo las tablas y los campos (es decirsin condiciones adicionales), debería estar bien con su enfoque; suena interesante :)

Una cosa que me gustaría añadir es que ciertas combinaciones son mejores que otras. Por ejemplo, unir dos tablas usando sus claves primarias (u otros índices) funcionará mucho mejor que dos columnas no relacionadas para las cuales se requiere un escaneo completo de la tabla.

Todo esto depende de cuán grandes sean las tablas en primer lugar; por menos de unos miles de registros, deberías estar bien; cualquier cosa más allá de la contemplación seria está en su lugar :)

+1

Uno no puede parametrizar identificadores ... – eggyal

+0

@eggyal ah, también está :) ¡bien visto! –

+0

Gracias; esto parece en parte una versión compacta de la respuesta que acepté, lamento tener que dársela al otro chico/chica. Sí, parece que tendré que mostrar y verificar los nombres de tablas y campos. Buen punto acerca de la eficiencia de las uniones. Como esto es en parte una herramienta educativa, me gustaría dejar ese detalle al usuario para su administración, pero definitivamente crearé un título que explique la importancia de las claves e índices primarios (¿índices?). –

Cuestiones relacionadas