Simple cross-site requests ovvero processare richieste in modo sicuro

Discussione in 'Snippet PHP' iniziata da MarcoGrazia, 10 Aprile 2019.

  1. MarcoGrazia

    MarcoGrazia Utente Attivo

    Registrato:
    15 Dicembre 2009
    Messaggi:
    664
    Mi Piace Ricevuti:
    7
    Punteggio:
    18
    Sesso:
    Maschio
    Occupazione:
    Sviluppare web design (Studiare)
    Località:
    Udine
    Home Page:
    Ciao.
    Prima di tutto spiego di cosa si tratta.
    Come tutti sanno, quando da un browser si richiede una pagina web, si mette in moto un meccanismo complesso che permette al "sistema internet" di funzionare.
    Il browser manda una serie di input sotto forma di messaggi codificati al server per richiedere quanto richiesto dall'utente e il server risponde in qualche modo.
    Fin qui la base fin troppo semplificata :D

    Saltando ogni precisazione, anche perché molti di voi già le conoscono, mi soffermo solo su due aspetti particolari della questione:
    1. mostrare la pagina
    2. inviare richieste GET o POST
    La prima è perfino ovvia, ma mettiamo che questa pagina contenga un form di dati da inviare, per esempio una pagina di login, tanto per citare.
    Se inseriamo all'inizio della nostra pagina, ovviamente scritta in PHP, qualcosa del genere:
    PHP:
    ...
    echo 
    '<p>VARIABILE _SERVER</p><pre>';
    var_dump($_SERVER);
    echo 
    '</pre>';
    ...
    Otterremo qualche cosa del genere:
    Cioè indicazioni riguardo la richiesta che abbiamo fatto al server.
    Fino a qui nulla di strano, anzi è la norma.
    Ma che succede se dalla stessa pagina inviamo una richiesta di tipo GET o POST?
    La risposta è in una piccola ma significativa risposta nella variabile _SERVER che poi andremo a sfruttare per rendere più sicura la nostra applicazione.
    Ecco il punto 2: inviando una richiesta con dati al server, questo risponde inserendo l'origine di chi ha fatta la richiesta.
    A dire il vero ne inserisce diverse di cose, ma ORIGIN è ciò che ci serve per verificare la richiesta.
    Essa contiene l'origine da cui è stata fatta la richiesta.
    Qui si vede l'indirizzo IP ma si può trovare il dominio se questo è settato; io ho il mio serverino casalingo che non ha settato un dominio, quindi mostra l'IP.
    Fa lo stesso, l'importante è il dato, e noi lo sfrutteremo.
    Lo script che presento, riguarda proprio la verifica dell'origine della richiesta e serve a verificare se un invio tramite GET o POST provenga proprio dal dominio a cui si sta cercando di loggarci.
    Lo script è commentato, quindi spero che basti questo, in ogni caso mi fermo qui per ora, dato che come al solito mi sono già dilungato abbastanza. :rolleyes:

    Script per evitare (mitigare) l'accesso indesiderato alla nostra applicazione:
    file: login.php
    PHP:
    <?php
    /*
     * Copyright 2019 Marco Grazia
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
     * MA 02110-1301, USA.
     *
     *
     */
    session_start();
    ob_start();
    try {

    $errore '';  //    Un flag per mostrare un eventuale errore nella pagina.
    //  Semplicemente la paginetta di login. Inserite la vostra
    $htmlFile =<<<EOF
    <!DOCTYPE html>
    <html lang="it">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Login</title>
        </head>
     
        <body>
            <h1>Sign in</h1>
            
    $errore
            <form  action="login.php" method="POST">
                <p><label for="account">Account:</label><br>
                <input name="account" type="text" id="account" required value="" placeholder="Il tuo account"></p>
                <p><label for="password">Password:</label><br>
                <input name="password" type="password" id="password" required value=""></p>
                <p><input type="submit" value="Sign  in" name="invia"></p>
            </form>
        </body>
    </html>\n
    EOF;

    //    Anti Cross-Origin (Se la richiesta viene da GET o POST, e da questo sito, prosegue)
    //    Se è settato HTTP_ORIGIN all'interno della variabile _SERVER e se è settata con il nome del mio sito, proseguo...
    if (isset($_SERVER['HTTP_ORIGIN']) === true && $_SERVER['HTTP_ORIGIN'] == 'http://mio_sito') {

                    
    //  Invio gli header che dispongono l'accettazione del sito, ovvero della pagina.
                    
    header ('Access-Control-Allow-Origin: http://tuo_sito');
                    
    header ('Content-Type: text/html; charset=UTF-8');

                    
    //  Quindi processo la richiesta, ovvero l'accesso al mio sito:
                   
    if (filter_has_var(INPUT_POST'invia) && filter_has_var(INPUT_POST, 'invia') == 'Sign in')  {
                      
                        //  Qui, se i dati del modulo corrispondono, avviene il login.
                        //  Ma non è questo l'
    argomento di discussionequindi evito di mostrare altro

                   
    } else {
                       
    //    Se si verifica una bad request mostro un messaggio nella pagina di login,
                      //    settando la variabile $login.
                       
    $errore '<h3 style="colore: red;">Si è verificato un errore processando i tuoi dati, riprova.</h3>';
                       echo 
    $htmlFile;  //    Mostro la pagina con la minaccia :-)
                  
    }
                  
    //    Se invece la fase di login è andata a buon fine indirizzo l'utente alla pagina che cercava.
                 
    header('location: http:://mio_sito/' $pagina_destinazione);
                 exit();
    } elseif (isset(
    $_SERVER['HTTP_ORIGIN']) === false) {
                
    //  E se arrivo qui direttamente dal sito?
               //  Secondo lo script mi rimanderebbe indietro, quindi processo pure il caso in cui HTTP_ORIGIN non settato.
               //  In questo caso, semplicemente mostro la pagina di login.
              
    header('Content-Type: text/html; charset=UTF-8');
              echo 
    $htmlFile;
              
    //  E attendo fiducioso che l'utente svolga la fase di login.
    } else {
        
    //    Questo è decisamente il caso peggiore: HTTP_ORIGIN esiste, ma il sito di provenienza non corrisponde all'origine.
        //    A questo punto bisogna dire al client browser che le cose non funzioneranno.
        
    header('Content-Type: text/html; charset=UTF-8');
        
    //    Creo al volo una paginetta di risposta.
        
    echo <<<EOF
    <!DOCTYPE html>
    <html lang="it">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Login</title>
        </head>
     
        <body>
            <h1>Sign in problem</h1>
            <h2>There is a problem with the Cross-Origin method</h2>
            <p>You are probably trying to log in from a domain other than <a href="http://mio_sito">mio_sito</a>.</p>
            <p>Bye!</p>
        </body>
    </html>\n[/I]
    EOF;
    }
     
    //  Fine fase di controllo.
    }  //  Fine try
    catch (Exception $e) {
        do {
            
    printf("%s:%d %s (%d) [%s]\n"$e->getFile(), $e->getLine(), $e->getMessage(), $e->getCode(), get_class($e));
        } while(
    $e $e->getPrevious());
    }
    ?>
    Lo script è tutto qui, in pratica come si può vedere non fa altro che processare la richiesta del server HTTP_ORIGIN, se c'è ne verifica l'autenticità.
    Ovvio che http://mio_sito va modificato al bisogno.
    Io ci ho giocato per un po' inserendo nell'url anche l'indirizzo di localhost e mi ha bloccato perché non corrispondeva all'IP del server.
    Ah! Quasi dimenticavo, è importante considerate anche il protocollo di destinazione, ovvero http://mio_sito e https:://mio_sito, non sono uguali ;-)
    Non sarà ne perfetta e ne definitiva dato che per bloccare Cross-Site Request Forgery (CSRF) serve altro, ma è un inizio.
    Lo script è ovviamente embrionale, quì è rilasciato sotto licenza, gradirei chi lo usa di mantenerla, grazie.
    :confused:
     
    Ultima modifica: 10 Aprile 2019
  2. marino51

    marino51 Utente Attivo

    Registrato:
    28 Febbraio 2013
    Messaggi:
    2.518
    Mi Piace Ricevuti:
    123
    Punteggio:
    63
    Occupazione:
    free lance
    Località:
    Lombardia
    si incontrano subito due problemi,
    il primo problema é generato dai browser che non gestiscono "origin"
    il secondo problema é conseguente all'uso di un dato praticamente statico e in chiaro, quindi facilmente intercettabile da malintenzionati
    ti chiedo quindi, perché hai adottato questa soluzione (poco sicura ?)
    invece di usare una soluzione con token
    PHP:
     <input type="hidden" name="token" value="<?php print GenerateToken(); ?>" />
    che funziona con i vari browser e che puoi "personalizzare" legando la generazione del token all' utente specifico (se necessario per migliorare la sicurezza) ?

    ps, modifica lo script, manca apice di chiusura
    T, 'invia) &&
     
Sto caricando...

Condividi questa Pagina