From Dynamic to Static e WordPress non lo buchi più

From Dynamic to Static
Sorgente immagine: https://greggbanse.com/

Introduzione

Alzi la mano chi non ha storto il naso leggendo il titolo di questo articolo?
Sicuramente moltissimi ed è stata la mia stessa reazione quando mi sono imbattuto in un post dal titolo The Unhackable WordPress Blog scritto da Matthew Bryant (mandatory).
Incuriosito dal titolo, volutamente provocatorio, ho continuato la lettura fino alla fine e sono rimasto molto entusiasta, in pratica il buon Matthew Bryant espone la sua teoria che si basa sul trovare un alto livello di sicurezza convertendo da dinamico a statico il CMS WordPress (e non solo).
Rifletteteci bene: senza più sessioni, senza più database, senza più pensare ai plugins di terze parti fatti con i piedi, senza le notti insonni con l’incubo dell’ennesimo 0day di wordpress.
Staticizzando wordpress non penseremmo più a vulnerabilità tipo CSRF, SQL Injection, RCE, XSS, in quest’ultimo caso non del tutto ma almeno ai Cross-Site Scripting che potrebbero provocare l’upload di una webshell o la perdita incontrollata delle sessioni.
Oltre a queste vulnerabilità mi vengono in mente altri tipi di attacchi  mirati ad impossessarsi delle credenziali che possono essere evitati, tipo: Phishing, BruteForce, Man in the Middle.
L’idea di Matthew è semplicissima, ovvero la possibilità di lavorare in locale sul proprio blog wordpress, convertirlo in statico, sistemare qua e là qualche link ed infine caricarlo online nell’area web fornita dal provider.
Alla fine della fiera il problema della sicurezza si focalizzerebbe più che altro sulle credenziali di accesso ai servizi del provider (ftp, sftp, http, ecc..), sulle credenziali di accesso del dominio, data breach o hacking del proprio provider/gestore di dominio e DOS/DDOS.
Purtroppo l’articolo “The Unhackable WordPress Blog” è più teorico che pratico quindi pronti via, seguendo le poche istruzioni, il mio test ha fallito miseramente.
Dopo svariate ore di imprecazioni, prove e scripting sono arrivato ad un risultato discreto che troverete passo dopo passo qui di seguito, compreso un simpatico bash script che automatizzerà tutto il processo.

Premessa
Rendere statico wordpress in alcuni casi non è una buona idea, se non impossibile.
Quali?
In tutti i casi in cui l'utente finale deve interagire con i dati contenuti nel database o nel filesystem, 
come per esempio uno shop online, live score, uploader di files.
Se il tuo sito rientra tra queste categorie puoi anche interrompere la lettura, ma se sei curioso e vuoi
imparare qualcosa di nuovo allora prosegui pure.

Finalmente la pratica

La procedura che riporterò di seguito è stata effettuata su sistema operativo Linux, quindi per i “Windowsiani” mi spiace ma dovrete trovare tools e batch/script alternativi a quelli che ho utilizzato.

Fase 1 – Preparazione dell’ambiente

Come prima cosa mi sono creato una macchina virtuale nel mio disco esterno criptato, replicando lo stesso OS e la stessa configurazione del server web in cui risiede il mio blog wordpress, dopo di che ho importato i files e il database del sito.
In questo modo tutta la parte di back-end da adesso in poi verrà gestita soltanto il locale, dalla scrittura degli articoli all’amministrazione del cms.
Naturalmente voi potete installare il tutto anche sulla vostra macchina locale, un server in lan, in dmz, insomma l’importante è che il cms non sia raggiungibile pubblicamente da tutti.

Una volta creato l’ambiente di lavoro il passo successivo è modificare il file /etc/hosts in modo da fare puntare il dominio del proprio cms verso la macchina locale.
Nel mio caso l’url del blog è www.homelab.it e l’ip della macchina virtuale 10.0.0.70:

claudio@KaliCla:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 KaliCla
10.0.0.70 www.homelab.it homelab.it
claudio@KaliCla:~$

Eseguendo un ping è possibile capire se il dominio vine risolto in maniera corretta:

claudio@KaliCla:~$ ping www.homelab.it
PING www.homelab.it (10.0.0.70) 56(84) bytes of data.
64 bytes from www.homelab.it (10.0.0.70): icmp_seq=1 ttl=64 time=0.481 ms
64 bytes from www.homelab.it (10.0.0.70): icmp_seq=2 ttl=64 time=0.297 ms
64 bytes from www.homelab.it (10.0.0.70): icmp_seq=3 ttl=64 time=0.361 ms
^C
--- www.homelab.it ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.297/0.379/0.481/0.079 ms
claudio@KaliCla:~$

Fase 2 – Individuazione alternativa statica a funzioni e plugins dinamici

Come anticipato nell’introduzione dell’articolo il problema nel rendere statico wordpress è la perdita delle proprietà dinamiche, come la possibilità di cercare post o lasciare commenti sul blog.
Per ovviare a queste due mancanze ho cercato due alternative esterne al cms, per i commenti ho utilizzato un plugin che si poggia alla famosa piattaforma DISQUS mentre per il sistema di ricerca ho optato per il Google Custom Search Engine (CSE).
Per installare il CSE di Google basta iscriversi sul sito e copiarsi il codice javascript che verrà generato automaticamente, ad esempio:

<script>
 (function() {
 var cx = '015793309105085050902:hsska5lc-e8';
 var gcse = document.createElement('script');
 gcse.type = 'text/javascript';
 gcse.async = true;
 gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
 '//cse.google.com/cse.js?cx=' + cx;
 var s = document.getElementsByTagName('script')[0];
 s.parentNode.insertBefore(gcse, s);
 })();
</script>
<gcse:search></gcse:search>

Copiato il codice è necessario creare un Widget “Testo” nel quale poi dovrà essere incollato il codice javascript.
Il risultato lo potete vedere sulla colonna di destra del mio blog (o in fondo se utilizzate dispositivi mobile).
Un ulteriore vantaggio di questa barra di ricerca è che più persone ne usufruiranno e più ne gioverà il vostro punteggio SEO.

Fase 3 – Download Front-end WordPress

Per rendere statico wordpress ho creato uno script in bash che potete scaricare dal mio repository GITHIB:

  ___ ___           _______                                           
 |   Y   .-----.   |   _   .----.-----.--------.                      
 |.  |   |  _  |   |.  1___|   _|  _  |        |                      
 |. / \  |   __|   |.  __) |__| |_____|__|__|__|                      
 |:      |__|      |:  |                                              
 |::.|:. |         |::.|                                              
 `--- ---' ______  `---'                       __          _______    
          |   _  \ .--.--.-----.---.-.--------|__.----.   |       |   
          |.  |   \|  |  |     |  _  |        |  |  __|   |___|   |   
          |.  |    |___  |__|__|___._|__|__|__|__|____|    /  ___/    
          |:  1    |_____|                                |:  1  \    
          |::.. . /                                       |::.. . |   
          `------'                                        `-------'   
                                   _______ __         __   __         
                                  |   _   |  |_.---.-|  |_|__.----.   
                                  |   1___|   _|  _  |   _|  |  __|   
                                  |____   |____|___._|____|__|____|   
                                  |:  1   |                           
                                  |::.. . |                           
                                  `-------'         by
                                              Claudio Viviani
                                           https://www.homelab.it
                                            info [at] homelab.it

      Download: https://github.com/claudioviviani/wp_dynamic2static

Se non avete intenzione  o voglia di capire come funziona lo script potete anche passare alla sezione successiva dell’articolo, altrimenti continuate pure con la lettura.
Questa è la fase principale, ovvero scaricare l’intero blog lato front-end in modo da poterlo visualizzare anche in modalità off-line.
Per questo scopo ho optato per il tool wget con la seguente sintassi:

claudio@KaliCla:~$ wget -m --html-extension --convert-links --domains homelab.it --no-parent https://www.homelab.it
  • -m modalità mirror, ovvero scarica recursivamente l’intero sito (pagine, css, js, ecc..).
  • –html-exension scarica le pagine con l’estensione html.
  • –convert-links converte i links in modo che funzionino anche in modalità off-line.
  • –domanins durante lo scarico non segue nessun link al difuori del dominio specificato.
  • –no-parent non segue i link al difuori della directory specificata nell’url

L’intero sito verrà scaricato in una directory il cui nome verrà ripreso dal dominio dell’url, nel mio caso “www.homelab.it

claudio@KaliCla:~$ ls www.homelab.it/
index.html index.php robots.txt wp-content wp-includes
claudio@KaliCla:~$

Scaricato l’intero sito è necessario occuparsi anche del feed RSS:

claudio@KaliCla:~$ wget -q https://www.homelab.it/index.php/feed/ -O www.homelab.it/index.php/feed/index.xml
  • -q quiet (no output)
  • -O Salva la pagina scaricata col nome personalizzato

Adesso che tutto il materiale off-line è disponibile si può notare come alcuni file, tipo css e js, siano stati rinominati con il numero versione:

wp-emoji-release.min.js?ver=4.3.1
/dazzling/style.css?ver=4.3.1
/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.2.1

Inoltre all’interno delle pagine html i link che fanno riferimento a questi file presentano il carattere “?” convertito in urlencode “%3F”.
Per rimediare a queste due problematiche ho deciso di affidarmi sempre a bash:

# Rinomino tutti i files in maniera corretta (rimuovo numero versione dal nome file)
for filemv in $(find . -name *\?v*=*); do mv $filemv $(echo $filemv | cut -d"?" -f1); done

# Edito tutti i files e converto il codice URLencode "?v" in "?v"
for filedit in $(grep "?v" . -R | cut -d ":" -f1); do sed -i -e 's/?v/?v/g' $filedit; done

A seconda delle vostre capacità potete ottimizzare gli script bash, anzi sono il primo ad accettare consigli e correzioni.

Come ultima cosa ho pensato di sfruttare il modulo di apache mod_rewrite tramite un file .htaccess per redirigere le chiamate della vecchia index.php e index.php/feed/:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /

RewriteRule ^index.php/feed/$ /index.php/feed/index.xml [L]
RewriteRule ^index.php/feed/index.html$ /index.php/feed/index.xml [L]

RewriteRule ^index.php/$ https://www.homelab.it/index.html [L]
</IfModule>

Prima di copiare l’intero sito statico nello spazio web del proprio provider, consiglio di testarlo prima all’interno della macchina virtuale in modo da rilevare eventuali errori o problemi di impaginazione.
Fatto questo non resta che commentare le righe aggiunte ad inizio procedura nel file /etc/hosts , fare un backup del blog wordpress dinamico, svuotare tutta la directory remota e poi copiare l’intero sito statico.
Una volta on-line il sito statico, ed accertato che tutto funzioni, potete eseguire un backup del vecchio database e rimuoverlo dal server remoto fornito dal vostro hosting provider.
Ogni volta che creerete una nuova pagina o un nuovo articolo sul blog off-line dovrete ripetere la procedura di conversione e di upload.

Un po di test

Ho deciso di effettuare due tipologie di test:

  1. Prestazioni/SEO
  2. Sicurezza

Test 1 – Prestazioni/SEO

Per questo test mi sono affidato a tre scanner online che effettuano un analisi del sito web e poi danno un punteggio a seconda delle prestazioni ed il rispetto delle regole SEO.
A sinistra (sopra per i dispositivi mobile) è possibile notare i risultati del blog dinamico mentre a destra (sotto per i dispositivi mobile) quello statico:

 

Pingdomhttp://tools.pingdom.com

 

From Dynamic to Static V.S From Dynamic to Static


 

Google Developershttps://developers.google.com/speed/pagespeed/insights/

wp_from_dynamic_2_static03            V.S           wp_from_dynamic_2_static04


 

GTmetrixhttps://gtmetrix.com/

 

From Dynamic to Static        V.S         From dynamic to Static


 

L’esito del test è chiaro (e logico), il cms statico è più performante di quello dinamico!
Un’altra dato importante è che non ci sono peggioramenti per quanto riguarda il SEO, quando andrete a fare le scansioni voi stessi ve ne renderete conto spulciando i dettagli dei risultati.

Test 2 – Sicurezza

Partendo dal fatto che sul mio blog non erano presenti plugins di WordPress con vulnerabilità note, non ho potuto eseguire test di sicurezza pratici ma se volete togliervi la curiosità potete installarne qualcuno affetto da SQL Injection, RCE, CSRF, o Arbitrary File Upload ecc..
Vedrete che il vostro cms statico ne uscirà indenne!
Per trovare plugins vulnerabili potete far riferimento a qualsiasi sito di exploits, ad esempio il famoso exploit-db.
Non potendo eseguire attacchi verso il mio cms ho voluto provare a vedere la differenza tra i risultati delle scansioni del noto wordpress security scanner wpcan .
Test cms DINAMICO:

claudio@KaliCla:~$ sudo wpscan https://www.homelab.it
_______________________________________________________________
        __          _______   _____                  
        \ \        / /  __ \ / ____|                 
         \ \  /\  / /| |__) | (___   ___  __ _ _ __  
          \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \ 
           \  /\  /  | |     ____) | (__| (_| | | | |
            \/  \/   |_|    |_____/ \___|\__,_|_| |_|

        WordPress Security Scanner by the WPScan Team 
                       Version 2.8
          Sponsored by Sucuri - https://sucuri.net
   @_WPScan_, @ethicalhack3r, @erwan_lr, pvdl, @_FireFart_
_______________________________________________________________

[+] URL: https://www.homelab.it/
[+] Started: Thu Oct 29 18:44:05 2015

[+] robots.txt available under: 'https://www.homelab.it/robots.txt'
... snip
... snip
[!] The WordPress 'https://www.homelab.it/readme.html' file exists exposing a version number
[+] Interesting header: SERVER: \m/_(>_<)_\m/
[+] Interesting header: X-POWERED-BY: PHP/5.4.44
[+] XML-RPC Interface available under: https://www.homelab.it/xmlrpc.php

[+] WordPress version 4.3.1 identified from meta generator

[+] WordPress theme in use: dazzling - v1.5.1

[+] Name: dazzling - v1.5.1
 | Location: https://www.homelab.it/wp-content/themes/dazzling/
 | Readme: https://www.homelab.it/wp-content/themes/dazzling/readme.txt
 | Style URL: https://www.homelab.it/wp-content/themes/dazzling/style.css
 | Theme Name: Dazzling
 | Theme URI: http://colorlib.com/wp/themes/dazzling
 | Description: Dazzling is a clean, modern, minimal and fully responsive flat design WordPress WooCommerce theme...
 | Author: Colorlib
 | Author URI: http://colorlib.com/wp/

[+] Enumerating plugins from passive detection ...
 | 3 plugins found:

[+] Name: add-to-any - v1.6.6
 | Location: https://www.homelab.it/wp-content/plugins/add-to-any/
 | Readme: https://www.homelab.it/wp-content/plugins/add-to-any/README.txt

[+] Name: ep-social-widget - v1.4.1
 | Location: https://www.homelab.it/wp-content/plugins/ep-social-widget/
 | Readme: https://www.homelab.it/wp-content/plugins/ep-social-widget/readme.txt

[+] Name: wordpress-23-related-posts-plugin - v3.5.4
 | Location: https://www.homelab.it/wp-content/plugins/wordpress-23-related-posts-plugin/
 | Readme: https://www.homelab.it/wp-content/plugins/wordpress-23-related-posts-plugin/readme.txt

[+] Finished: Thu Oct 29 18:44:23 2015
[+] Requests Done: 77
[+] Memory used: 11.238 MB
[+] Elapsed time: 00:00:17
claudio@KaliCla:~$

Test cms STATICO:

claudio@KaliCla:~$ sudo wpscan https://www.homelab.it
[sudo] password for claudio: 
_______________________________________________________________
        __          _______   _____                  
        \ \        / /  __ \ / ____|                 
         \ \  /\  / /| |__) | (___   ___  __ _ _ __  
          \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \ 
           \  /\  /  | |     ____) | (__| (_| | | | |
            \/  \/   |_|    |_____/ \___|\__,_|_| |_|

        WordPress Security Scanner by the WPScan Team 
                       Version 2.8
          Sponsored by Sucuri - https://sucuri.net
   @_WPScan_, @ethicalhack3r, @erwan_lr, pvdl, @_FireFart_
_______________________________________________________________

[+] URL: https://www.homelab.it/
[+] Started: Sat Oct 31 23:37:39 2015

[+] robots.txt available under: 'https://www.homelab.it/robots.txt'
.. snip
.. snip
[+] Interesting header: SERVER: \m/_(>_<)_\m/

[+] WordPress version 4.3.1 identified from meta generator

[+] WordPress theme in use: dazzling

[+] Name: dazzling
 |  Location: https://www.homelab.it/href='wp-content/themes/dazzling/
 |  Style URL: https://www.homelab.it/href='wp-content/themes/dazzling/style.css
 |  Referenced style.css: href='wp-content/themes/dazzling/style.css
 |  Description: 

[+] Enumerating plugins from passive detection ...
[+] No plugins found

[+] Finished: Sat Oct 31 23:37:46 2015
[+] Requests Done: 55
[+] Memory used: 9.266 MB
[+] Elapsed time: 00:00:06
claudio@KaliCla:~$

Fantastico! Wpscan va un po in ciampanelle e non rileva alcun plugins!
Questo scoraggerà sicuramente qualche script kiddie nel continuare ad analizzare il nostro blog.

Consigli

Questa sezione verrà aggiornata via via che troverò qualche novità da riportare.

  1. Consiglio assolutamente di rendere statico wordpress soltanto nel caso in cui usiate i permalinks, in questo modo non rischierete di perdere i riferimenti ai link indicizzati nei vari motori di ricerca.
  2. Nel caso usiate il plugin YOAST SEO vi consiglio di modificare i link dell’attributo canonical in tutti i file html sostituendo l’url relativo della pagina con l’indirizzo del permalink. Potete utilizzare il mio script wp_dynamic2static.sh o prenderlo come riferimento.

Conclusione

Bene siamo arrivati alla conclusione dell’articolo “From Dynamic to Static e WordPress non lo buchi più”, spero che sia stato utile o  fonte d’ispirazione per un progetto ancora più raffinato.
Vi state chiedendo se anche io sono passato definitivamente a wordpress statico?
Beh..non vi resta che scoprirlo 😉
Per qualsiasi informazione o segnalazione non esitate a contattarmi tramite e-mail, social o commentando qui di seguito.