2012-06-07 4 views
5

SfondoL'implementazione di un sistema di giunzione "configurabile", in modo sicuro

Ciao, sto sviluppando uno strumento sperimentale/educativo in PHP e MySQL. Sono nuovo di SQL, ma voglio fare le cose nel modo giusto fin dall'inizio. Uso le istruzioni PDO preparate per tutte le sostituzioni variabili e il backticking ovunque possibile (quindi, come ho capito, non sarà trasferibile su database non MySQL). Per quanto riguarda il mio problema, ho un'idea su come andare avanti, ma ci vorranno diverse ore per implementare (sono nuovo anche per la sintassi di SQL), quindi nel frattempo ho pensato di creare una domanda prima solo nel caso qualcuno possa urlare, "Questo non è il modo di farlo!" e salvami ore di sforzi.

Problemi

desidero creare un'interfaccia in cui un utente seleziona dal menu a discesa:

  1. un tavolo A,
  2. uno o più campi in quella tabella, ad esempio A.x e A.y,
  3. un tavolo B,
  4. uno o più campi in quella tabella, ad esempio B.z e B.y,

e su presentazione avrebbe eseguito il codice di un join interno, corrispondenti rispettivamente ciascun campo, ad esempio A.x = B.z, A.y = B.y, ecc. E restituiscono tutte le righe corrispondenti.

Il mio piano è semplicemente generare un'istruzione SQL INNER JOIN, eseguire il looping dei campi e inserire segnaposti (?), associare i rispettivi parametri e infine eseguire l'istruzione.

C'è un modo più semplice per farlo? C'è un modo migliore per farlo? Sarà in qualche modo sfruttabile?

Grazie mille, in anticipo. Se nessuno risponde entro la fine (dubbioso), posterò la mia soluzione.

Misc.

supponga che io convalidare

  1. che l'utente seleziona un numero uguale di campi tra A e B,
  2. che esistono i campi e tabelle,
  3. ecc

e che i nomi dei campi non devono essere identici: verranno abbinati in ordine. (Indicare altri dettagli di cui potrei non essere a conoscenza!)

Infine, l'obiettivo è che queste selezioni vengano salvate in una tabella "Impostazioni". In effetti, gli utenti creano "viste" che vorrebbero vedere ogni volta che tornano.

+0

+1 domanda veramente informativa ... strada da percorrere. – Wh1T3h4Ck5

risposta

2

Stai facendo talmente bene che mi sento davvero in colpa, sottolineando che stai facendo qualcosa di sbagliato! :)

È possibile utilizzare solo le istruzioni preparate parametrizzare i valori di campo — identificatori non SQL, ad esempio nomi di colonna o di tabella. Pertanto non sarà possibile passare A.x, B.z ecc. Nei criteri JOIN mediante parametri di istruzione preparati: l'utente deve fare invece di fare ciò che si sente terribilmente sbagliato e concatenarli direttamente nella stringa SQL.

Tuttavia, non tutto è perduto. In un certo ordine vaga di preferenza, è possibile:

  1. presentare all'utente una lista di opzioni, da cui è poi rimontare lo 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> 
    

    Allora il vostro gestore di moduli:

    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"; 
    

    Questo approccio ha il vantaggio che, a meno di compromettere PHP (nel qual caso si avranno preoccupazioni ben più grandi dell'iniezione SQL), SQL arbitrario assolutamente non può trova la sua strada nel tuo RDBMS.

  2. Assicurarsi che l'input dell'utente corrisponde a uno dei valori previsti:

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

    Allora il vostro gestore di moduli:

    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]"; 
    

    Questo approccio si basa sulla funzione di PHP in_array per la sicurezza (e anche espone al usa i nomi delle colonne sottostanti, ma data la tua domanda dubito che sia una preoccupazione).

  3. eseguire alcune pulizia di ingresso, come ad esempio:

    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']) . '`' 
    

    Mentre noi qui riportiamo l'input dell'utente e sostituire qualsiasi tentativo da parte dell'utente di fuggire da quella citazione, questo approccio potrebbe essere pieno di tutti i tipi di difetti e vulnerabilità (nella funzione PHP mb_ereg_replace o nella gestione di stringhe appositamente predisposte da MySQL all'interno di un identificatore quotato).

    È lontano meglio se è possibile utilizzare uno dei metodi precedenti per evitare di inserire del tutto le stringhe definite dall'utente nel proprio SQL.

+0

Grazie per le parole incoraggianti; ancora di più per una risposta così completa. Sì, alla fine ho capito che gli identificatori non potevano essere parametrizzati, e dopo un po 'di navigazione ho iniziato a sospettare che la concatenazione potesse essere l'unico modo. Quindi lo posi e andai a dormire, sperando che qualcuno conoscesse un modo migliore di un controllo/disinfezione campo per campo. Sfortunatamente come fai notare, non sembra esserci! Ma sono contento di poter andare avanti con fiducia. Vedo il vantaggio della preferenza [1], tuttavia vorrei che _like_ gli utenti vedessero i nomi delle colonne, quindi vado con [2]. (E [3] mi avrebbe tenuto sveglio la notte.) Grazie ancora! –

1

Supponendo che l'input dell'utente sia limitato alla sola selezione di tabelle e campi (ad es.senza condizioni aggiuntive), dovresti stare bene con il tuo approccio; sembra interessante :)

Una cosa che vorrei aggiungere è che alcuni join sono migliori di altri. Ad esempio, l'unione di due tabelle utilizzando le loro chiavi primarie (o altri indici) funzionerà meglio di due colonne non correlate per le quali è richiesta una scansione completa della tabella.

Tutto dipende da quanto sono grandi le tabelle in primo luogo; per meno di qualche migliaio di dischi, dovresti stare bene; tutto ciò che va oltre la seria contemplazione è a posto :)

+1

Non è possibile parametrizzare gli identificatori ... – eggyal

+0

@eggyal ah, c'è anche quello :) ben chiazzato! –

+0

Grazie; questa sembra in parte una versione compatta della risposta che ho accettato - mi dispiace doverlo dare all'altro ragazzo/ragazza. Sì, sembra che dovrò visualizzare e controllare i nomi delle tabelle e dei campi. Buon punto sull'efficienza dei join. Poiché questo è in parte uno strumento educativo, in realtà vorrei lasciare all'utente il dettaglio da gestire, ma creerò sicuramente una didascalia che spiega l'importanza delle chiavi primarie e degli indici (indici?). –