Come prevenire XSS con HTML / PHP?


256

Come posso impedire XSS (cross-site scripting) usando solo HTML e PHP?

Ho visto numerosi altri post su questo argomento ma non ho trovato un articolo che chiarisca e dichiari concisamente come prevenire effettivamente XSS.


3
Nota solo che ciò non risolverà il caso in cui potresti voler utilizzare l'input dell'utente come attributo HTML. Ad esempio, l'URL di origine di un'immagine. Non è un caso comune, ma facile da dimenticare.
Michael Mior,

@MichaelMior ecco un soluzione per evitare XSS in hrefo srcattributo HTML: stackoverflow.com/questions/19047119/...
baptx

C'è un bell'articolo qui che spiega XSS e come prevenirlo in diverse lingue (incluso PHP).
XCore

Risposte:


296

Fondamentalmente è necessario utilizzare la funzione htmlspecialchars()ogni volta che si desidera inviare qualcosa al browser proveniente dall'input dell'utente.

Il modo corretto di utilizzare questa funzione è qualcosa del genere:

echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8');

Google Code University ha anche questi video molto educativi sulla sicurezza Web:


7
@TimTim: nella maggior parte dei casi, sì. Tuttavia, quando devi consentire l'input HTML, le cose diventano un po 'più complicate e, in tal caso, ti consiglio di usare qualcosa come htmlpurifier.org
Alix Axel,

@Alix Axel, quindi la tua risposta è usare htmlspecialchars o usare htmlpurifier.org ?
TimTim

3
Se è necessario accettare l'input HTML, utilizzare Purificatore HTML, in caso contrario htmlspecialchars().
Alix Axel,

9
htmlspecialchars o htmlentities? Controllare qui stackoverflow.com/questions/46483/...
kiranvj

4
Il più delle volte è corretto, ma non è così semplice. Dovresti considerare di inserire una stringa non attendibile in HTML, Js, Css e considerare di inserire HTML non attendibile in HTML. Guarda questo: owasp.org/index.php/…
uomo di bronzo,

41

Uno dei miei riferimenti OWASP preferiti è la spiegazione di Cross-Site Scripting perché mentre ci sono un gran numero di vettori di attacco XSS, il seguito di alcune regole può difendersi fortemente dalla maggior parte di essi!

Questo è il cheat sheet di sicurezza PHP


7
Anch'io .. Questo è il cheat sheet dell'evasione del

1
Non esattamente XSS, ma penso che XSS e CSRF siano comunemente confusi ed entrambi sono davvero pericolosi: owasp.org/index.php/…
Simon

2
Questa pagina non esiste più
Mazzy


15

Uno dei passaggi più importanti è la sanificazione di qualsiasi input dell'utente prima che venga elaborato e / o restituito al browser. PHP ha alcune funzioni di " filtro " che possono essere utilizzate.

Il modulo che di solito hanno gli attacchi XSS è quello di inserire un collegamento ad alcuni javascript fuori sede che contengano intenzioni dannose per l'utente. Leggi di più qui .

Ti consigliamo anche di testare il tuo sito: posso consigliare il componente aggiuntivo di Firefox XSS Me .


Di cosa ho bisogno per assicurarmi di disinfettare esattamente l'input da. C'è un carattere / stringa particolare a cui devo fare attenzione?
TimTim

27
@TimTim - no. Tutto l'input dell'utente deve sempre essere considerato come ostile intrinsecamente.
zombat,

Inoltre, i dati interni (dipendenti, amministratore di sistema, ecc.) Potrebbero non essere sicuri. È necessario identificare e monitorare (con data di registro e utente) i dati visualizzati con interpretazione.
Samuel Dauzon,

9

In ordine di preferenza:

  1. Se si utilizza un motore di template (ad esempio Twig, Smarty, Blade), verificare che offra una fuga sensibile al contesto. So per esperienza che Twig lo fa.{{ var|e('html_attr') }}
  2. Se si desidera consentire HTML, utilizzare HTML Purifier . Anche se pensi di accettare solo Markdown o ReStructuredText, vuoi comunque purificare l'HTML in questi linguaggi di markup.
  3. In caso contrario, utilizzare htmlentities($var, ENT_QUOTES | ENT_HTML5, $charset)e assicurarsi che il resto del documento utilizzi lo stesso set di caratteri di $charset. Nella maggior parte dei casi, 'UTF-8'è il set di caratteri desiderato.

Inoltre, assicurati di uscire dall'output, non dall'input .


7

Pubblicazione incrociata di questo come riferimento consolidato dalla beta della documentazione SO che non è in linea.

Problema

Lo scripting tra siti è l'esecuzione involontaria di codice remoto da parte di un client Web. Qualsiasi applicazione Web potrebbe esporsi a XSS se accetta input da un utente e lo emette direttamente su una pagina Web. Se l'input include HTML o JavaScript, il codice remoto può essere eseguito quando questo contenuto viene renderizzato dal client Web.

Ad esempio, se un lato di terze parti contiene un file JavaScript:

// http://example.com/runme.js
document.write("I'm running");

E un'applicazione PHP genera direttamente una stringa passata al suo interno:

<?php
echo '<div>' . $_GET['input'] . '</div>';

Se un parametro GET non selezionato contiene, <script src="http://example.com/runme.js"></script>l'output dello script PHP sarà:

<div><script src="http://example.com/runme.js"></script></div>

Verrà eseguito JavaScript di terze parti e l'utente vedrà "Sono in esecuzione" sulla pagina Web.

Soluzione

Come regola generale, non fidarsi mai dell'input proveniente da un client. Ogni valore GET, POST e cookie potrebbe essere qualsiasi cosa e dovrebbe pertanto essere convalidato. Quando si emette uno di questi valori, sfuggirli in modo che non vengano valutati in modo imprevisto.

Tieni presente che anche nelle applicazioni più semplici i dati possono essere spostati e sarà difficile tenere traccia di tutte le fonti. Pertanto è consigliabile evitare sempre l' output.

PHP offre alcuni modi per sfuggire all'output a seconda del contesto.

Funzioni di filtro

Le funzioni del filtro PHP consentono di disinfettare o convalidare i dati di input nello script php in molti modi . Sono utili durante il salvataggio o l'output dell'input del client.

Codifica HTML

htmlspecialcharsconvertirà i "caratteri speciali HTML" nelle loro codifiche HTML, il che significa che non verranno elaborati come HTML standard. Per correggere il nostro esempio precedente usando questo metodo:

<?php
echo '<div>' . htmlspecialchars($_GET['input']) . '</div>';
// or
echo '<div>' . filter_input(INPUT_GET, 'input', FILTER_SANITIZE_SPECIAL_CHARS) . '</div>';

Uscita:

<div>&lt;script src=&quot;http://example.com/runme.js&quot;&gt;&lt;/script&gt;</div>

Tutto all'interno del <div>tag non verrà interpretato come un tag JavaScript dal browser, ma come un semplice nodo di testo. L'utente vedrà in sicurezza:

<script src="http://example.com/runme.js"></script>

Codifica URL

Quando si genera un URL generato dinamicamente, PHP fornisce la urlencodefunzione per generare in modo sicuro URL validi. Ad esempio, se un utente è in grado di inserire dati che diventano parte di un altro parametro GET:

<?php
$input = urlencode($_GET['input']);
// or
$input = filter_input(INPUT_GET, 'input', FILTER_SANITIZE_URL);
echo '<a href="http://example.com/page?input="' . $input . '">Link</a>';

Qualsiasi input dannoso verrà convertito in un parametro URL codificato.

Utilizzo di librerie esterne specializzate o elenchi OWASP AntiSamy

A volte vorrai inviare HTML o altri tipi di input di codice. Dovrai mantenere un elenco di parole autorizzate (lista bianca) e non autorizzate (lista nera).

È possibile scaricare elenchi standard disponibili sul sito Web di OWASP AntiSamy . Ogni elenco è adatto per un tipo specifico di interazione (ebay api, tinyMCE, ecc ...). Ed è open source.

Esistono librerie esistenti per filtrare l'HTML e prevenire gli attacchi XSS per il caso generale e che eseguono almeno elenchi AntiSamy con un utilizzo molto semplice. Ad esempio hai HTML Purifier


5

Molti framework aiutano a gestire XSS in vari modi. Quando lanci il tuo o se c'è qualche preoccupazione XSS, possiamo sfruttare filter_input_array (disponibile in PHP 5> = 5.2.0, PHP 7.) In genere aggiungerò questo frammento al mio SessionController, perché tutte le chiamate passano lì prima di qualsiasi altro controller interagisce con i dati. In questo modo, tutti gli input dell'utente vengono disinfettati in 1 posizione centrale. Se questo viene fatto all'inizio di un progetto o prima che il tuo database sia avvelenato, non dovresti avere alcun problema al momento dell'output ... interrompe l'immondizia, l'immondizia fuori.

/* Prevent XSS input */
$_GET   = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING);
$_POST  = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
/* I prefer not to use $_REQUEST...but for those who do: */
$_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST;

Quanto sopra rimuoverà TUTTI i tag HTML e script. Se hai bisogno di una soluzione che consenta tag sicuri, basata su una whitelist, dai un'occhiata a Purificatore HTML .


Se il database è già avvelenato o si desidera gestire XSS al momento dell'output, OWASP consiglia di creare una funzione wrapper personalizzata echoe di utilizzarla OVUNQUE dove si generano valori forniti dall'utente:

//xss mitigation functions
function xssafe($data,$encoding='UTF-8')
{
   return htmlspecialchars($data,ENT_QUOTES | ENT_HTML401,$encoding);
}
function xecho($data)
{
   echo xssafe($data);
}

2

Puoi anche impostare alcune intestazioni di risposta HTTP relative a XSS tramite header(...)

X-XSS-Protection "1; mode = block"

per sicurezza, la modalità di protezione XSS del browser è abilitata.

Politica sulla sicurezza dei contenuti "default-src 'self'; ..."

per abilitare la sicurezza dei contenuti sul lato browser. Vedi questo per i dettagli sulla politica di sicurezza dei contenuti (CSP): http://content-security-policy.com/ Soprattutto l'impostazione di CSP per bloccare gli script incorporati e le fonti di script esterne è utile contro XSS.

per un gruppo generale di utili intestazioni di risposta HTTP riguardanti la sicurezza della tua webapp, guarda OWASP: https://www.owasp.org/index.php/List_of_useful_HTTP_headers


1
<?php
function xss_clean($data)
{
// Fix &entity\n;
$data = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;'), $data);
$data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
$data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');

// Remove any attribute starting with "on" or xmlns
$data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);

// Remove javascript: and vbscript: protocols
$data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);

// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data);

// Remove namespaced elements (we do not need them)
$data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);

do
{
    // Remove really unwanted tags
    $old_data = $data;
    $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
}
while ($old_data !== $data);

// we are done...
return $data;
}

5
Non dovresti usare preg_replacecome usa evalnei tuoi input. owasp.org/index.php/PHP_Security_Cheat_Sheet#Code_Injection
CrabLab

0

Usa htmlspecialcharssu PHP. Su HTML, cerca di evitare di usare:

element.innerHTML = “…”; element.outerHTML = “…”; document.write(…); document.writeln(…);

dove varè controllato dall'utente .

Ovviamente prova anche a evitare eval(var), se devi usarne uno, prova a scappare da JS , HTML li sfugge e potresti dover fare un po 'di più, ma per le basi questo dovrebbe essere sufficiente.


0

Il modo migliore per proteggere i tuoi input è usare la htmlentitiesfunzione. Esempio:

htmlentities($target, ENT_QUOTES, 'UTF-8');

Puoi ottenere maggiori informazioni qui .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.