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
Saltando ogni precisazione, anche perché molti di voi già le conoscono, mi soffermo solo su due aspetti particolari della questione:
Se inseriamo all'inizio della nostra pagina, ovviamente scritta in PHP, qualcosa del genere:
Otterremo qualche cosa del genere:
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.
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.
Script per evitare (mitigare) l'accesso indesiderato alla nostra applicazione:
file: login.php
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.
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
Saltando ogni precisazione, anche perché molti di voi già le conoscono, mi soffermo solo su due aspetti particolari della questione:
- mostrare la pagina
- inviare richieste GET o POST
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>';
...
Cioè indicazioni riguardo la richiesta che abbiamo fatto al server./home/server/upload.php:7:
array (size=32)
'HTTP_HOST' => string '192.168.56.1' (length=12)
'HTTP_CONNECTION' => string 'keep-alive' (length=10)
'HTTP_UPGRADE_INSECURE_REQUESTS' => string '1' (length=1)
'HTTP_USER_AGENT' => string 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36' (length=115)
'HTTP_DNT' => string '1' (length=1)
...
eccetera
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./home/server/upload.php:7:
array (size=37)
'HTTP_HOST' => string '192.168.56.1' (length=12)
'HTTP_CONNECTION' => string 'keep-alive' (length=10)
'CONTENT_LENGTH' => string '611' (length=3)
'HTTP_CACHE_CONTROL' => string 'max-age=0' (length=9)
'HTTP_ORIGIN' => string 'http://192.168.56.1' (length=19)
'HTTP_UPGRADE_INSECURE_REQUESTS' => string '1' (length=1)
'HTTP_DNT' => string '1' (length=1)
'CONTENT_TYPE' => string 'multipart/form-data; boundary=----WebKitFormBoundaryIbOZ8b9bSRcpmbve' (length=68)
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.
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 discussione, quindi 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());
}
?>
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.
Ultima modifica: