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.