Una sicurezza adeguata è fondamentale per mantenere al sicuro il tuo sito o quello del tuo tema o degli utenti plug-in. In parte ciò significa convalida e sanificazione dei dati appropriati. In questo articolo vedremo perché questo è importante, cosa deve essere fatto e quali funzioni fornisce WordPress per aiutare.
Poiché sembrano esserci varie interpretazioni su cosa significano i termini "validazione", "fuga" e "sanificazione", chiarirò innanzitutto cosa intendo in questo articolo:
Quando i dati sono inclusi in un determinato contesto (ad esempio in un documento HTML), tali dati potrebbero essere interpretati erroneamente come un codice per quell'ambiente (ad esempio il codice HTML). Se tali dati contengono codice dannoso, quindi l'utilizzo di tali dati senza sanitarli, significa che il codice verrà eseguito. Il codice non deve necessariamente essere dannoso perché possa causare effetti indesiderati. Il compito di disinfettare è quello di assicurarsi che qualsiasi codice nei dati non sia interpretato come codice, altrimenti potresti finire come la scuola di Bobby Tables ...
"Exploits of a Mom" - xkcd
Un esempio apparentemente innocuo potrebbe essere il pre-riempimento di un campo di ricerca con il termine attualmente interrogato, utilizzando il carattere senza caratteri di escape $ _GET [ 's']
:
Ciò apre una vulnerabilità che potrebbe consentire l'iniezione di javascript, ad esempio, inducendo qualcuno a visitare http://yoursite.com?s= "/>
. Il termine di ricerca 'salta' fuori dall'attributo value e la parte seguente dei dati viene interpretata come codice ed eseguita. Per evitare ciò, WordPress fornisce get_search_query
che restituisce la query di ricerca sterilizzata. Anche se questo è un esempio "innocuo", lo script inserito potrebbe essere molto più dannoso e nella migliore delle ipotesi si limiterebbe a "spezzare" il form se i termini di ricerca contengono virgolette doppie.
In che modo questo codice dannoso (o altrimenti) potrebbe essersi trovato sul tuo sito non è la preoccupazione qui - ma piuttosto è per impedirne l'esecuzione. Né facciamo ipotesi sulla natura di questo codice indesiderato, o il suo intento - potrebbe essere stato semplicemente un errore da parte dell'utente. Questo mi porta alla regola n. 1 ...
È una massima comune che viene utilizzata per la sanitizzazione dei dati, ed è buona. L'idea è che non si dovrebbe assumere che tutti i dati inseriti dall'utente siano sicuri. Né dovresti presumere che i dati che hai recuperato dal database siano sicuri, anche se lo hai reso "sicuro" prima di inserirlo lì. In effetti, se i dati possono essere considerati "sicuri" non ha senso senza contesto. A volte gli stessi dati possono essere utilizzati in più contesti sulla stessa pagina. I titoli, ad esempio, possono tranquillamente contenere virgolette o virgolette quando si trovano all'interno dei tag di intestazione, ma causano problemi se utilizzati (senza caratteri di escape) all'interno di un attributo titolo di un tag di collegamento. Pertanto, è piuttosto inutile rendere i dati "sicuri" quando vengono aggiunti al database, poiché spesso è impossibile rendere i dati sicuri per tutti i contesti contemporaneamente. (Ovviamente deve essere reso sicuro da aggiungere al database - ma ci arriveremo più tardi).
Anche se si intende utilizzare tali dati in un contesto specifico, ad esempio un modulo, è ancora inutile disinfettare i dati durante la scrittura nel database poiché, come da Regola n. 1, non ci si può fidare che sia ancora sicuro quando si tiralo fuori di nuovo.
Questa è la massima procedurale che stabilisce quando è necessario convalidare i dati e quando si sanifica. In poche parole - convalida i tuoi dati (controlla che sia quello che dovrebbe essere - e che sia "valido") non appena lo ricevi dall'utente. Quando si arriva a utilizzare questi dati, ad esempio quando si esegue l'output, è necessario sfuggire (o disinfettarli). La forma di questa sanificazione dipende interamente dal contesto in cui la si utilizza.
Il miglior consiglio è di eseguire questo "in ritardo": sfuggire i dati immediatamente prima di utilizzarli o visualizzarli. In questo modo puoi essere sicuro che i tuoi dati sono stati correttamente disinfettati e non è necessario ricordare se i dati sono stati precedentemente controllati.
Si potrebbe pensare "Ok, convalidare prima di scrivere nel database e disinfettare quando lo si usa, ma non è necessario assicurarsi che i dati siano sicuri da scrivere nel database?". In generale, sì. Quando si aggiungono dati a un database, o semplicemente si utilizza un input per interagire con un database, è necessario sfuggire ai dati in cui è contenuto qualsiasi comando SQL. Ma questo mi porta alla Regola numero 3, una che vola in faccia alla Regola numero 1: Trust WordPress.
In un precedente articolo, ho preso l'input dell'utente (inviato da un modulo di ricerca tramite AJAX) e l'ho usato direttamente con get_posts ()
per restituire i post che corrispondono a quella query di ricerca:
$ posts = get_posts (array ('s' => $ _ REQUEST ['term']));
Un lettore attento ha notato che non avevo eseguito alcuna sanificazione - e avevano ragione. Ma non avevo bisogno di farlo. Quando si utilizzano funzioni di alto livello come get_posts ()
, non è necessario preoccuparsi di disinfettare i dati, perché le query del database sono tutte correttamente escapate dagli interni di WordPress. È una questione completamente diversa se si utilizza una query SQL diretta, ma vedremo questo in una sezione successiva. Allo stesso modo, funzioni come il titolo()
, the_permalink ()
, il contenuto()
ecc. eseguono la loro sanificazione (per il contesto appropriato).
Quando si ricevono dati inseriti da un utente è importante convalidare esso. (L'API delle impostazioni, trattata in questa serie, consente di specificare una funzione di callback per fare esattamente questo). I dati non validi sono corretti automaticamente, oppure il processo viene interrotto e l'utente viene restituito al modulo per riprovare (eventualmente con un messaggio di errore appropriato). La preoccupazione qui non è la sicurezza, ma piuttosto la validità: se lo stai facendo bene, WordPress si occuperà di aggiungere i dati in modo sicuro al database. Che cosa significa "valido" dipende da te: potrebbe significare un indirizzo email valido, un intero positivo, un testo di lunghezza limitata o uno di un array di opzioni specificate. Tuttavia, il tuo scopo è determinare la validità, WordPress offre molte funzioni che possono aiutarti.
Quando si aspettano dati numerici, è possibile verificare se i dati "sono una forma di numero", ad esempio is_int
o is_float
. Solitamente, è sufficiente trasmettere semplicemente i dati come numerici con: intval
o floatval
.
Se è necessario assicurarsi che il numero sia riempito con zeri iniziali, WordPress fornisce la funzione zeroise ()
. Quale prende i seguenti parametri:
Per esempio:
echo zeroise (70,4); // Stampa 0070
Per verificare la validità delle e-mail, WordPress ha il is_email ()
funzione. Questa funzione utilizza semplici controlli per convalidare l'indirizzo. Ad esempio, controlla che contenga il simbolo '@', che sia più lungo di 3 caratteri, che il dominio contenga solo caratteri alfa e trattini e così via. Ovviamente, non controlla che l'indirizzo e-mail esista effettivamente. Supponendo che l'indirizzo e-mail abbia superato i controlli, viene restituito, altrimenti viene restituito 'falso'.
$ email = is_email ('eomeone@e ^ esempio.com '); // $ email è impostato su false. $ email = is_email ('[email protected] '); // $ email è impostato su "[email protected]".
Spesso si potrebbe desiderare di consentire solo alcuni Tag HTML nei tuoi dati, ad esempio nei commenti pubblicati sul tuo sito. WordPress fornisce una famiglia di funzioni del modulo wp_kses_ *
(KSES Strips Evil Scripts). Queste funzioni rimuovono (alcuni sottoinsiemi di) tag HTML e possono essere utilizzati per garantire che i collegamenti nei dati siano di protocolli specifici. Ad esempio il wp_kses ()
la funzione accetta tre argomenti:
soddisfare
- (stringa) Contenuto da filtrare tramite ksesallowed_html
- Un array in cui ogni chiave è un elemento HTML consentito e il valore è una matrice di attributi consentiti per quell'elementoallowed_protocols
- Opzionale. Protocollo consentito nei collegamenti (ad esempio http
, mailto
, alimentazione
eccetera.)wp_kses ()
è una funzione molto flessibile che consente di rimuovere tag indesiderati o solo attributi indesiderati dai tag. Ad esempio, per consentire solo o
tag (ma ammetti solo l'attributo href):
$ content = "Clic qui da visitare wptuts+ "; echo wp_kses ($ content, array ('strong' => array (), 'a' => array ('href'))); // Stampa l'HTML" Fai clic qui per visitare wptuts+ ": Clicca qui per visitare wptuts+
Ovviamente, specificare ogni tag consentito e ogni attributo consentito può essere un compito laborioso. Quindi WordPress offre altre funzioni che ti permettono di usare wp_kses
con tag e protocolli consentiti preimpostati, ovvero quelli utilizzati per la convalida di post e commenti:
wp_kses_post ()
wp_kses_data ()
Le funzioni di cui sopra sono utili per garantire che l'HTML ricevuto dall'utente contenga solo elementi della lista bianca. Una volta fatto ciò, vorremmo anche assicurarci che ogni tag sia bilanciato, ovvero che ogni tag di apertura abbia il tag di chiusura corrispondente. Per questo possiamo usare balanceTags ()
. Questa funzione accetta due argomenti:
// Contenuto con chiusura mancante tag $ content = "Clic qui da visitare wptuts + "; eco balanceTags ($ content, true), // Stampa l'HTML" Clicca qui per visitare wptuts+ "
Se si desidera creare un file in una delle directory del proprio sito Web, è necessario assicurarsi che il nome file sia valido e legale. Dovresti anche assicurarti che il nome file sia unico per quella directory. Per questo WordPress fornisce:
sanitize_file_name ($ filename)
- disinfetta (o convalida) il nome del file rimuovendo i caratteri che sono illegali nei nomi di file su determinati sistemi operativi o che richiederebbero l'escape sulla riga di comando. Sostituisce gli spazi con trattini e trattini consecutivi con un trattino singolo e rimuove punti, trattini e caratteri di sottolineatura dall'inizio e dalla fine del nome file.wp_unique_filename ($ dir, $ nomefile)
- restituisce un unico (per la directory $ dir
), nomefile sanitizzato (utilizza sanitize_file_name
).Quando si ricevono i dati immessi in un campo di testo, è probabile che si desideri eliminare spazi bianchi, tabulazioni e interruzioni di linea, oltre a rimuovere tutti i tag. Per questo WordPress fornisce sanitize_text_field ()
.
WordPress fornisce anche sanitize_key
. Questa è una funzione molto generica (e talvolta utile). Semplicemente assicura che la variabile restituita contenga solo caratteri alfa minuscole, trattini e caratteri di sottolineatura.
Mentre la convalida riguarda la garanzia che i dati siano validi: la sanitizzazione dei dati consiste nel farcela sicuro. Mentre alcune delle funzioni di convalida di cui sopra possono essere utili per assicurarsi che i dati siano sicuri - in generale, non è sufficiente. Anche i dati "validi" potrebbero non essere sicuri in determinati contesti.
Semplicemente non puoi chiedere "Come faccio a rendere sicuri questi dati?". Invece dovresti chiedere, "Come faccio a rendere sicuri questi dati per utilizzarlo in X".
Per illustrare questo punto, supponiamo di avere un widget con un'area di testo in cui si intende consentire all'utente di inserire del codice HTML. Supponiamo che poi entrino:
Ciao mondo
Questo è perfettamente valido e sicuro, HTML - tuttavia quando si fa clic su Salva, scopriamo che il testo è saltato fuori dalla textarea. Il codice HTML non è sicuro come valore per l'area di testo:
Ciò che è sicuro da usare in un contesto, non è necessariamente sicuro in un altro. Ogni volta che si utilizzano o si visualizzano dati, è necessario tenere presente quali forme di igienizzazione devono essere eseguite per rendere sicuro l'utilizzo di tali dati. Ecco perché WordPress offre spesso diverse funzioni per lo stesso contenuto, ad esempio:
il titolo
- per usare il titolo in HTML standard (all'interno di tag di intestazione, per esempio)the_title_attribute
- per usare il titolo come valore di attributo (di solito l'attributo titolo in
tag)the_title_rss
- per usare il titolo nei feed RSSTutti eseguono l'igienizzazione necessaria per un particolare contesto e, se li stai utilizzando, dovresti assicurarti di utilizzare quello corretto. A volte, però, dovremo eseguire la nostra sanitizzazione, spesso perché abbiamo un input personalizzato oltre il titolo standard, il permalink, il contenuto ecc. Che WordPress gestisce per noi.
Quando stampi le variabili sulla pagina, dobbiamo essere consapevoli di come il browser le interpreterà. Consideriamo il seguente esempio:
supporre $ title =
. Piuttosto che visualizzare l'HTML tags, they will be interpreted as HTML and the enclosed javascript would be injected into the page.
This form of injection (as also demonstrated in the search form example) is called Cross-site scripting and this benign example belies its severity. Injected script can essentially control the browser and 'act on behalf' of the user or steal the user's cookies. This becomes an even more serious issue if the user is logged in. To prevent variables printed inside HTML being interpreted as HTML, WordPress provides the well known esc_html
function. In this example:
Now consider the following example:
Because $value
contains double quotes, unescaped it can jump out of the value attribute and inject script, for example, by using the onfocus
attribute. To escape unsafe characters (such as quotes, and double-quotes in this case), WordPress provides the function esc_attr
. Like esc_html
it replaces 'unsafe' characters by their entity equivalents. In fact, at the time of writing, these functions are identical - but you should still use the one that is appropriate for the context.
For this example we should have:
Both esc_html
and esc_attr
also come with __
, _e
, and _x
variants.
esc_html__('Text to translate', 'plugin-domain')
/ esc_attr__
- returns the escaped translated text,esc_html_e('Text to translate', 'plugin-domain')
/ esc_attr_e
- displays the escaped translated text and finally theesc_html_x('Text to translate', $context, 'plugin-domain')
/ esc_attr_x
- translates the text according to the passed context, and then returns the escaped translationFor class names, WordPress provides sanitize_html_class
- this escapes variables for use in class names, simply by restricting the returned value to alpha-numerics, hyphens and underscores. Note: It does not ensure the class name is valid (reference: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier).
In CSS, identifiers can contain only the characters
[a-zA-Z0-9]
and ISO 10646 characters U+00A0 and higher, plus the hyphen (-
) and the underscore (_
); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code.
Let's now look at another common practise, printing variables into the href
attribute:
" title="Link Title"> Link Text
Clearly it is vulnerable to the same form of attack as illustrated in escaping HTML and attributes. But what if the $url
was set as follows:
$url = 'javascript:alert(\'Injected javascript\')'
On clicking the link, the alert function would be fired. This contains no HTML, or any quotes that allow it to jump out of the href attribute - so esc_attr
is not sufficient here. This is why context matters: esc_attr($url)
would be safe in the title
attribute, but not for the href
attribute - and this is because of the javascript protocol - which while perfectly valid - is not to be considered safe in this context. Instead you should use:
esc_url
- for escaping URLs that will be printed to the page.esc_url_raw
- for escaping URLs to save to the database or use in URL redirecting.esc_url
strips out various offending characters, and replaces quotes and ampersands with their entity equivalents. It then checks that the protocol being used is allowed (javascript, by default, isn't).
What esc_url_raw
does is almost identical to esc_url
, but it does not replace ampersands and single quotes (which you don't want to, when using the URL as an URL, rather than displaying it).
In this example, we are displaying the URL, so we use esc_url:
" title="Link Title"> Link Text
Although not necessary in most cases, both functions accept an optional array to specify which protocols (such as http
, https
, ftp
, ftps
, mailto
, etc) you wish to allow.
Sometimes you'll want to print javascript variables to a page (usually in the head):
In effetti, se lo fai, dovresti quasi certamente usarlo wp_localize_script ()
- che gestisce la sanitizzazione per te. (Se qualcuno può pensare a un motivo per cui potrebbe essere necessario utilizzare il metodo precedente, mi piacerebbe sentirlo).
Tuttavia, per rendere sicuro l'esempio precedente, è possibile utilizzare ESC_JS
funzione:
Quando si visualizza il contenuto in una textarea, esc_html
non è sufficiente perché non raddoppia le entità. Per esempio:
testo grassetto'?>
$ var
stampato nella textarea apparirà come:
testo grassetto
Piuttosto che codificare anche il &
come &
nel tag.
Per questo WordPress fornisce esc_textarea
, che è quasi identico a esc_html
, ma raddoppia le entità codificate. Essenzialmente è poco più di un involucro per htmlspecialchars
. In questo esempio:
testo grassetto'?>
La visualizzazione di indirizzi e-mail sul tuo sito Web li rende inclini a raccogliere e-mail. Un metodo semplice è quello di mascherare l'indirizzo e-mail. WordPress fornisce antispambot
, che codifica parti casuali dell'indirizzo e-mail nelle loro entità HTML (e equivalenti esadecimali se $ mailto = 1
). Su ogni pagina caricare la codifica dovrebbe essere diversa e mentre l'indirizzo restituito viene visualizzato correttamente nel browser, dovrebbe apparire come gobbledygook agli spambots. La funzione accetta due argomenti:
e-mail
- l'indirizzo per offuscaremailto
- 1 o 0 (1 se si utilizza il protocollo mailto in un tag link)$ email = "[email protected]"; $ email = sanitize_email ($ email); echo '' .antispambot ($ email). ' ';
Se si desidera aggiungere (o rimuovere) variabili da una stringa di query (questo è molto utile se si desidera consentire agli utenti di selezionare un ordine per i propri messaggi), il modo più sicuro e semplice è quello di utilizzare add_query_arg
e remove_query_arg
. Queste funzioni gestiscono l'escape necessario per gli argomenti e i relativi valori da utilizzare nell'URL.
add_query_arg
accetta due argomenti:
parametri di query
- un array associativo di parametri -> valoriurl
- l'URL per aggiungere i parametri e i loro valori a. Se omesso, viene utilizzato l'URL della pagina correnteremove_query_arg
accetta anche due argomenti, il primo è un array di parametri da rimuovere, il secondo è come sopra.
// Se siamo su www.example.com/wp-admin/edit.php?post_type=book $ query_params = array ('page' => 'my-bage'); $ url = add_query_arg ($ query_params); // impostare $ url come: // www.example.com/wp-admin/edit.php?post_type=book&page=my-page
Come accennato in precedenza, la sterilizzazione non ha molto senso senza un contesto - quindi è quasi inutile sanificare i dati durante la scrittura nel database. Spesso, è necessario archiviare i dati nel formato raw in ogni caso e, in ogni caso, la regola n. 1 stabilisce che dovremmo sempre disinfettare l'output.
La convalida dei dati, d'altra parte, dovrebbe essere eseguita non appena viene ricevuta e prima che sia scritta nel database. L'idea è che i dati "non validi" debbano essere corretti automaticamente o essere contrassegnati per i dati, e solo i dati validi dovrebbero essere dati al database.
Detto questo, si consiglia di eseguire anche la convalida quando vengono visualizzati i dati. In effetti, a volte, la "convalida" garantisce anche la sicurezza dei dati. Ma qui la priorità è sulla sicurezza e dovresti evitare una convalida eccessiva che verrebbe eseguita su ogni caricamento della pagina (il file wp_kses_ *
le funzioni, ad esempio, sono molto costose da eseguire).
Quando si usano funzioni come get_posts
o classi come WP_Query
e WP_User_Query
, WordPress si prende cura della necessaria sanitizzazione nell'interrogazione del database. Tuttavia, quando si recuperano dati da una tabella personalizzata, o comunque si esegue una query SQL diretta sul database, la corretta sanitizzazione dipende da voi. WordPress, tuttavia, fornisce una classe utile, il $ wpdb
classe, che aiuta con l'escape di query SQL.
Consideriamo questo fondamentale 'SELEZIONARE
'comando, dove $ età
e $ firstname
sono variabili che memorizzano un'età e un nome che stiamo interrogando:
SELECT * WHERE age = "$ age" AND firstname = '$ firstname'
Non siamo sfuggiti a queste variabili, quindi potrebbero essere iniettati potenzialmente ulteriori comandi. Prendendo a prestito l'esempio di xkcd dall'alto:
$ età = 14; $ firstname = "Robert"; Studenti DROP TABLE; "; $ sql = "SELECT * WHERE age =" $ age "AND firstname = '$ firstname';"; $ risultati = $ wpdb-> query
Funzionerà come il comando (s):
SELECT * WHERE age = "14" AND firstname = 'Robert'; DROP TABLE Studenti; ';
E cancella la nostra intera tabella Studenti.
Per evitare ciò, possiamo usare il $ Wpdb-> preparare
metodo. Questo accetta due parametri:
%S
e i numeri decimali sono sostituiti dal segnaposto % d
e galleggia vicino % f
In questo esempio:
$ età = 14; $ firstname = "Robert"; Studenti DROP TABLE; "; $ sql = $ wpdb-> prepare ('SELECT * WHERE età =% d AND firstname =% s;', array ($ age, $ firstname)); $ risultati = $ wpdb-> get_results ($ sql);
La query SQL con escape ($ sql
in questo esempio) può quindi essere utilizzato con uno dei seguenti metodi:
$ Wpdb-> get_row ($ sql)
$ Wpdb-> get_var ($ sql)
$ Wpdb-> get_results ($ sql)
$ Wpdb-> get_col ($ sql)
$ Wpdb-> query ($ sql)
Per l'inserimento o l'aggiornamento dei dati, WordPress rende la vita ancora più semplice fornendo il $ Wpdb-> inserire ()
e $ Wpdb-> update ()
metodi.
Il $ Wpdb-> inserire ()
il metodo accetta tre argomenti:
%S
''% d
' o'% f
')$ età = 14; $ firstname = "Robert"; Studenti DROP TABLE; "; $ wpdb-> insert ('Studenti', array ('firstname' => $ firstname, 'age' => $ age), array ('% s', '% d'));
Il $ Wpdb-> update ()
il metodo accetta cinque argomenti:
// Aggiorna Robert '; DROP TABLE Studenti; a Bobby $ oldname = "Robert"; Studenti DROP TABLE; "; $ newname = "Bobby"; $ wpdb-> update ('Studenti', array ('firstname' => $ newname), array ('firstname' => $ oldname), array ('% s'), array ('% s'));
Sia il $ Wpdb-> inserire ()
e il $ Wpdb-> update ()
i metodi eseguono tutte le operazioni di sanitizzazione necessarie per la scrittura nel database.
Perché il $ Wpdb-> preparare
usi del metodo %
per distinguere i segnaposto, occorre prestare attenzione quando si usa il %
jolly in istruzioni SQL LIKE. Il Codice suggerisce di sfuggirgli con un secondo %
. In alternativa puoi sfuggire al termine da cercare like_escape
e quindi aggiungere il carattere jolly %
se del caso, prima di includerlo nella query utilizzando il metodo di preparazione. Per esempio:
$ Età = 14; $ firstname = "Robert"; Studenti DROP TABLE; "; SELECT * WHERE age = $ age (firstname LIKE '% $ firstname%');
Sarebbe stato reso sicuro con:
$ Età = 14; $ firstname = "Robert"; Studenti DROP TABLE; "; SELECT * WHERE age = $ age AND (firstname LIKE '% $ firstname%'); $ query = $ wpdb-> prepare ('SELECT * WHERE age =% d AND (firstname LIKE% s);', array ($ age, '%'. like_escape ($ firstname). '%'));
Questo non è un elenco esauriente delle funzioni disponibili per la convalida e l'igienizzazione, ma dovrebbe coprire la maggior parte dei casi d'uso. Molte di queste (e altre) funzioni possono essere trovate in /wp-includes/formatting.php
e consiglio vivamente di scavare nel codice di base e dare un'occhiata a come il core di WordPress convalida e sanifica i dati.
Hai trovato questo articolo utile? Avete ulteriori suggerimenti sulle migliori pratiche per la convalida e la sanificazione dei dati in WordPress? Fateci sapere nei commenti qui sotto.