Potrebbe questo tipo di autenticazione essere attaccato?

21

Non sono un esperto di sicurezza informatica ma solo un webmaster e mi chiedo se questo tipo di autenticazione sia pericoloso. Ho provato la password come questa sul lato server usando PHP:

if (isset($_POST['pass_word']) AND $_POST['pass_word'] ==  $passwd)

$passwd deriva dal mio DB PostgreSQL. Pensavo che almeno non rischi l'iniezione SQL. Rischio qualche altro tipo di iniezione? Se è un modo pericoloso per autenticarsi, ti preghiamo di spiegarne il motivo.

    
posta kevin ternet 29.12.2016 - 15:59
fonte

2 risposte

68

Sembra che tu stia memorizzando le password in chiaro, che è una cattiva idea. Devi assicurarti che le password siano protette utilizzando, come minimo, password_hash() e password_verify() , che usa bcrypt sotto il cofano. Questo è semplice, facile, sicuro e perfettamente accettabile per la maggior parte degli scenari.

In alternativa puoi usare un metodo leggermente più sicuro come Argon2 , che ha vinto Password Hashing Competition ed è resistente al cracking di CPU e GPU, e mira anche a minimizzare il potenziale per gli attacchi di canale laterale. C'è un articolo che spiega come usare Argon2 come parte del PHP di libsodium wrapper, o direttamente usando la libreria "Halite" di Paragon, che offre Argon2 con crittografia simmetrica in cima per impedire l'accesso al solo database dal fornire hash utilizzabili, poiché la chiave simmetrica è memorizzata sul disco del server come un file. Questa opzione è più complicata, ma offre una sicurezza aggiuntiva se sei veramente paranoico. Ti suggerirei di evitarlo se non hai familiarità con lo sviluppo sicuro, tuttavia, poiché aumentano le possibilità che tu incasini qualcosa.

Raccomando anche di usare === per evitare strani casi di falsa uguaglianza usando matrici nelle query URL o null.

    
risposta data 29.12.2016 - 16:15
fonte
15

La risposta di Polynomial è azzeccata. Volevo segnalare altre potenziali vulnerabilità: la precedenza degli operatori, la facilità di revisione e la facilità di test.

Qualcosa della forma foo AND bar == baz è vulnerabile a ottenere la precedenza sbagliata. Vorrei verificare che, in sede di revisione, vi sia una lunga storia di misure di sicurezza sventate a causa di condizioni e priorità composte.

Ora, non c'è niente di sbagliato con quello che hai scritto, ma è molto facile sbagliarlo. Nella maggior parte dei linguaggi va bene perché gli operatori di confronto come == hanno una precedenza più alta rispetto agli operatori logici come AND , quindi la condizione si legge come (notare l'esplicito parens):

isset($_POST['pass_word']) AND
  ($_POST['pass_word'] ==  $passwd)

... ma è molto facile sbagliarlo. Ad esempio, AND e && non hanno la stessa precedenza. Cosa succede se invece di usare isset tu (o qualcuno che ti segue) hai usato una scorciatoia logica-o per impostare un valore predefinito? (Questo non è equivalente a isset , è solo un esempio di precedenza che causa problemi di sicurezza)

$_POST['pass_word'] || '' ==  $passwd

Questa linea gotcha è in realtà questa:

$_POST['pass_word'] || ('' ==  $passwd)

Ora chiunque abbia una password vuota, forse da un errore che lo recupera, effettuerà sempre il login.

Quando si tratta di codice sicuro, rendi esplicito il tuo paren per rendere noto il tuo intento al revisore e al programma.

Ancora meglio, evita le condizioni composte interamente nel codice di sicurezza. Non puoi sbagliare usando quello che non hai usato. Ad esempio, questo codice sfrutta l'incapsulamento e ritorno anticipato per fornire metodi di verifica e test molto facili.

// This deals with input and normalizing it.
function can_login($user) {
    if( !isset($_POST['pass_word']) ) {
        // Or it could throw an exception to give the
        // caller more information about why they didn't login
        return false;
    }

    return check_password($user, $_POST['pass_word']);
}

// This only deals with checking a password.
// Don't use this, it still has all the flaws Polynomial
// pointed out.
function check_password($user, $check) {
    // get_password() should throw an exception if it cannot
    // find that user's password to avoid accidentally thinking
    // their password is false or 0 or '' or something. This
    // avoids relying on the caller doing the check for failure.    
    return $check === get_password($user);
}

Potrebbe non essere il metodo migliore, ma è piccolo e facile da testare e recensire. Questo separa i dettagli della registrazione di un utente in (che potrebbe essere più di un controllo della password, come se fossero un utente valido) dal controllo della propria password. Qualsiasi problema può essere individuato e risolto senza dover guadare il resto del codice di accesso. Un revisore controllerà get_password e tutti i luoghi check_password e can_login vengono utilizzati.

Non posso sottolineare abbastanza quanto sia importante isolare i componenti protetti nel più piccolo pezzo possibile, usando il più semplice codice possibile, per renderli facili da recensire e testare.

BONUS : ecco qualcosa che ho pensato di fare, ma ho deciso che renderebbe il codice meno sicuro.

La mia idea originale era di avere una funzione che restituiva più valori: non fornivano una password, la loro password era sbagliata, la loro password era giusta. Ciò consentirebbe al chiamante di conoscere la differenza tra "la password è sbagliata" e "non hanno fornito una password, qualcosa è sbagliato".

function can_login($user) {
    if( !isset($_POST['pass_word']) ) {
        return NO_PASSWORD;
    }

    $password = get_password($user);
    if( $_POST['pass_word'] === $password ) {
        return YES;
    }
    else {
        return NO;
    }
}

E poi lo chiami così:

$rslt = can_login($user);

if( $rslt == YES ) {
    echo "Login!\n";
}
else if( $rslt == NO ) {
    echo "Wrong password.\n";
}
else if( $rslt == NO_PASSWORD ) {
    echo "No password given.\n";
}

Il problema è che rende la chiamata alla routine correttamente più complicata. Peggio ancora, se non leggi i documenti, penseresti che fosse questo:

if( can_login($user) ) {
    echo "Yes!\n";
}
else {
    echo "No!\n";
}

Poiché can_login restituisce sempre un valore vero, ora chiunque può accedere.

Ritorna a casa il punto: rendi il tuo codice sicuro il più semplice e infallibile possibile per il revisore, per il tester e per il chiamante. In questo caso, le eccezioni sono la giusta strada da percorrere in quanto forniscono un canale per ottenere più informazioni mantenendo can_login un semplice booleano. Se dimentichi di controllare che il programma si arresti in modo anomalo, ma l'accesso non riesce.

    
risposta data 29.12.2016 - 20:53
fonte

Leggi altre domande sui tag