
Guida completa per l'hardening delle installazioni WordPress: copre modifiche all'utente amministratore, applicazione di HTTPS, sicurezza dei plugin, permessi dei file e configurazione del server per siti aziendali statici.
Questo documento è scritto con l'obiettivo di essere adatto per applicazioni web sviluppate con WordPress che non interagiscono con gli utenti. È principalmente destinato a pagine di marca aziendale, varie visualizzazioni statiche, pagine di reclutamento e siti simili.
Per i siti web in cui gli utenti si registrano e utilizzano liberamente il sito, come le comunità aperte, alcuni elementi di questo documento potrebbero non essere applicabili. Tienilo presente mentre leggi.
Questo documento non include tutto il contenuto necessario per proteggere WordPress.
Tuttavia, include informazioni generali e dettagliate a un livello che consente valutazioni del rischio di sicurezza e risposte alle vulnerabilità basate sulla guida.
Se trovi questo utile, per favore lascia una "stella"🌟 per supportare ulteriori miglioramenti.
Quando installi WordPress, il nome utente admin predefinito è "admin" a meno che non lo cambi durante il processo di installazione. Il nome dell'account "admin" è ampiamente noto, quindi dovrebbe essere cambiato con un nome diverso. Se continui a usare "admin" come nome utente admin, un attaccante potrebbe tentare un attacco di forza bruta usando "admin" per ottenere l'accesso al tuo sito WordPress.
Se un attaccante ottiene l'accesso all'account admin di WordPress, avrà il pieno controllo del sito web. Il nome utente admin predefinito di WordPress dovrebbe essere cambiato con un nome diverso.
Verifica:
Correzione:
Nota:
Per impostazione predefinita, WordPress ha cinque ruoli utente - "Amministratori", "Editori", "Autori", "Collaboratori", "Iscritti"
Questi ruoli ti permettono di controllare quali attività gli utenti possono svolgere sul tuo sito web assegnando le autorizzazioni appropriate. Se i ruoli e le autorizzazioni degli utenti non sono gestiti correttamente, gli utenti potrebbero ottenere accesso non necessario a funzionalità critiche, rappresentando un rischio significativo per la sicurezza.
Verifica:
Correzione:
Nota:
WordPress include una funzionalità di registrazione utente integrata. Questa funzionalità è disabilitata per impostazione predefinita, ma può essere attivata da un amministratore.
Se questa funzionalità è abilitata, chiunque può registrarsi e potenzialmente accedere al pannello di amministrazione di WordPress, il che può portare a problemi di sicurezza. Per la maggior parte dei siti web che non intendono operare come comunità aperte, la funzionalità di registrazione utente non è necessaria e dovrebbe rimanere disabilitata.
Verifica:
Usando un browser web

Usando curl
(response) HTTP/1.1 302 Moved Temporarily cache-control: no-cache, no-store, must-revalidate, max-age=0 content-type: text/html; charset=UTF-8 server: Apache content-length: 0 ... location: https://yourwordpress.com/wp-login.php?registration=disabled
**Rimedio:**
- Se la registrazione degli utenti è abilitata, disabilitarla.
- Deseleziona “Chiunque può registrarsi”

## 4. Assicurati che l'Editor dei File dei Plugin sia Disabilitato
Se un attaccante riesce ad accedere a un account amministratore di WordPress, può prendere il pieno controllo del tuo sito web.
Possono modificare il codice del tuo tema e dei plugin tramite la funzione "Editor" integrata, caricare script dannosi, deturpare il tuo sito, inviare spam ai tuoi utenti e altro ancora.
Gli hack comuni tramite questi editor includono iniezioni SQL, hack di spam SEO e spam SEO giapponese.
**Verifica:**
- Verifica che l'editor dei file sia disabilitato.
- Controlla se puoi accedere all'editor tramite Aspetto > Editor o Plugin > Editor dei Plugin.
**Rimedio:**
- Se l'editor dei file è abilitato, disabilitarlo seguendo questi passaggi:
1. Accedi al tuo file wp-config.php usando File Manager o FTP
2. Apri il file wp-config.php per la modifica.
3. Scorri fino in fondo al file (se stai usando il wp-config.php predefinito).
4. Trova la seguente riga:
`
/* That’s all, stop editing! Happy publishing. */
`
5. Sopra questa riga, aggiungi il seguente codice:
`
define('DISALLOW_FILE_EDIT', true);
`
6. Salva le modifiche e chiudi l'editor.
7. Torna alla dashboard di WordPress e conferma che le opzioni dell'editor non siano più disponibili.
## 5. Assicurati che i Plugin Non Utilizzati e Non Necessari siano Disattivati
Molte vulnerabilità in WordPress derivano da problemi di sicurezza dei plugin.
I plugin sono open-source, il che rende più facile per gli attaccanti trovare e sfruttare le vulnerabilità.
È fondamentale tenere aggiornati i plugin che utilizzi e disattivare eventuali plugin inutilizzati per prevenire potenziali sfruttamenti.
**Verifica:**
- Controlla che i plugin non utilizzati e non necessari siano disattivati.
- Verifica che i plugin non utilizzati e non necessari siano disattivati.
**Rimedio:**
- Disattiva eventuali plugin non utilizzati e non necessari seguendo questi passaggi:
**Usa Plugin Check - PCP:**
- PCP: https://wordpress.org/plugins/plugin-check
1. Installa e attiva Plugin Check (PCP):
- Vai alla dashboard di amministrazione di WordPress
- Vai su Plugin > Aggiungi nuovo.
- Cerca "Plugin Check" e installalo, attivalo.
2. Esegui un controllo dei plugin:
- Nella dashboard di WordPress, vai al menu Plugin Check.
- Seleziona i plugin che vuoi controllare ed esegui la scansione.
3. Analizza i risultati della scansione:
- PCP analizzerà il codice del plugin e fornirà un report, includendo:
- Conformità agli standard del codice: Quanto il plugin aderisce agli standard di codifica di WordPress.
- Problemi di sicurezza: Potenziali vulnerabilità o codice dannoso.
- Problemi di prestazioni: L'impatto sulle prestazioni del sito web.
- Problemi di compatibilità: Se il plugin è compatibile con altri plugin e temi.
4. Identifica i plugin problematici:
- Se il report evidenzia vulnerabilità di sicurezza significative, codice dannoso o numerose violazioni degli standard di codifica, il plugin è probabilmente "sospetto."
- Fai attenzione ai plugin che effettuano richieste esterne non necessarie o eseguono query eccessive sul database.
5. Risolvi i problemi:
- Risolvi i problemi identificati aggiornando o trovando i problemi nelle pagine dei plugin.
- Evita di usare plugin con gravi problemi di sicurezza. Trova plugin alternativi quando necessario.
**Gestione dei plugin:**
1. Selezione dei plugin:
- Usa repository ufficiali, controlla recensioni e valutazioni, verifica la credibilità dello sviluppatore
- Non usare plugin sconosciuti e non verificati
2. Aggiornamenti regolari
- Mantieni i plugin aggiornati per assicurarti di avere le ultime patch di sicurezza.
3. Disattiva ed elimina i plugin non utilizzati
- Anche i plugin inattivi possono rappresentare un rischio per la sicurezza, quindi rimuovili se non vengono utilizzati.
- Riduci al minimo i plugin: usa solo i plugin essenziali
## 6. Assicurati che WordPress sia Configurato per Usare Solo HTTPS, Inclusa l'Amministrazione di WordPress
Oggi, la maggior parte dei siti web è configurata per funzionare su SSL (HTTPS).
Tuttavia, alcuni server web potrebbero ancora essere erroneamente impostati per gestire sia connessioni HTTP che HTTPS.
Questo può consentire l'accesso a WordPress tramite entrambi i protocolli, il che è un rischio per la sicurezza. WordPress, inclusa l'amministrazione, dovrebbe essere forzato a usare esclusivamente HTTPS.
**Verifica:**
- Verifica che WordPress, inclusa l'amministrazione, sia configurato per essere accessibile solo tramite HTTPS.
- Controlla la configurazione VirtualHost del server web per assicurarti che non ci siano VirtualHost HTTP impostati.
**Rimedio:**
- Se l'accesso HTTP è possibile, prima rivedi e modifica le impostazioni del server web.
- Se il server ha VirtualHost HTTP, reindirizzali a HTTPS o cancella i VirtualHost HTTP.
- Se necessario, abilita la funzione "FORCE_SSL_ADMIN" per imporre l'accesso HTTPS per l'amministrazione di WordPress.
**Passaggi per abilitare FORCE_SSL_ADMIN:**
1. Accedi al tuo file wp-config.php usando File Manager o FTP.
2. Apri il file wp-config.php per la modifica.
3. Scorri fino in fondo al file (se stai usando il wp-config.php predefinito).
4. Trova la seguente riga:
`
/* That’s all, stop editing! Happy publishing. */
`
5. Sopra questa riga, aggiungi il seguente codice:
`
define('FORCE_SSL_ADMIN', true);`
`
6. Salva le modifiche e chiudi l'editor.
Torna alla dashboard di WordPress e accedi di nuovo per assicurarti che l'amministrazione di WordPress sia accessibile solo tramite HTTPS.
**Reindirizza HTTP a HTTPS sul server web:**
1. Per Apache:
```
<VirtualHost *:80>
ServerName yourwordpress.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
```
2. Per Nginx:
```
server {
listen 80;
server_name yourwordpress.com;
location / {
return 301 https://$host$request_uri;
}
}
```
Assicurandoti che WordPress e l'amministrazione di WordPress siano accessibili solo tramite HTTPS, puoi migliorare significativamente la sicurezza del tuo sito web, proteggendo i dati e prevenendo accessi non autorizzati.
## 7. Assicurati che le Restrizioni di Accesso IP (ACL) siano Applicate
Verifica che le restrizioni di accesso IP siano applicate
Per operare WordPress in modo sicuro, è essenziale applicare restrizioni di accesso IP a determinate URL, inclusa l'amministrazione di WordPress, per impedire a utenti, computer e bot indesiderati di accedervi. Ciò implica consentire l'accesso solo da indirizzi IP autorizzati, come gli IP degli amministratori. Inoltre, disabilitare funzionalità non utilizzate è necessario per ridurre al minimo le superfici di attacco.
Le URL da proteggere includono l'amministrazione di WordPress, la registrazione degli utenti (wp-signup.php), l'API REST JSON e la funzionalità XML-RPC.
Questa guida alla sicurezza è rivolta a siti web orientati ai servizi come blog aziendali, pagine di reclutamento, siti di marca e siti promozionali dove l'interazione con l'utente è minima e i contenuti sono principalmente esposti.
Implementando restrizioni di accesso IP, puoi ridurre significativamente il rischio di accesso non autorizzato e migliorare la sicurezza complessiva del tuo sito web.
### 7.1. Assicurati che le restrizioni di accesso IP siano applicate all'amministrazione di WordPress.
Il percorso di accesso all'amministrazione di WordPress è fisso nella forma di wp-login.php o /wp-admin, rendendolo facilmente accessibile a utenti non autorizzati.
Assicurati che sia applicata una restrizione di accesso IP per prevenire l'accesso non autorizzato alla pagina di amministrazione.
**Verifica:**
- Verifica che le restrizioni di accesso IP siano applicate per l'amministrazione di WordPress.
- Nella maggior parte dei casi, questo è configurato sul server web (Apache, Nginx).
**Rimedio:**
- Se le restrizioni di accesso IP non sono applicate, implementale.
- Di seguito sono riportati i metodi per applicare le restrizioni di accesso IP utilizzando i server web Apache e Nginx.
**Applicazione della restrizione di accesso IP all'amministrazione di WordPress:**
- /wp-admin, wp-login.php
1. Per Apache:
```
# Files Directive
<Files "wp-login.php">
Require ip 10.10.77.49 # Replace with your IP address
</Files>
# FilesMatch Directive
<FilesMatch "^wp-login\.php$">
Require all granted
</FilesMatch>
# Directory, Files Mixing Directive
<Directory /www/vhosts/yourwordpress>
Require all granted
AllowOverride None
<Files "wp-login.php">
Require ip 10.10.77.49 # Replace with your IP address
</Files>
</Directory>
# Location Directive
<Location "/wp-admin">
Require ip 10.10.77.49 # Replace with your IP address
</Location>
<Location "/wp-login.php">
Require ip 10.10.77.49 # Replace with your IP address
</Location>
```
2. Per Nginx:
```
location /wp-admin {
allow 10.10.77.49; # Replace with your IP address
deny all;
}
location ~* \wp-login.php {
allow 10.10.77.49; # Replace with your IP address
deny all;
}
```
### 7.2. Limita l'Accesso IP o Disabilita la Funzionalità JSON REST API
WordPress fornisce due funzionalità REST (xmlrpc, json rest api) ed è abilitata per impostazione predefinita all'installazione di WordPress.
L'API REST fornisce endpoint per i tipi di dati di WordPress, consentendo l'interazione remota con il sito per attività come la query di post o dati, la modifica di risorse, l'editing e la cancellazione.
Per la maggior parte di WordPress, la funzionalità REST API non è essenziale.
Abilitarla potrebbe esporre WordPress ad attacchi DDoS e potrebbe portare a consumo di risorse e rallentamenti del sito.
**Verifica:**
- Verifica che la funzionalità JSON REST API sia abilitata. (Predefinito: Abilitato)```
# curl -i -k https://yourwordpress.com/wp-json
(response)
HTTP/1.1 200 OK
cache-control: no-cache, no-store, must-revalidate, max-age=0
content-type: text/html; charset=UTF-8
...
Server: Apache
{"name":"mywordress","description":"".......................
Rimedio:
Installa il plugin "Disable REST API", attivalo:
Restrizione dell'accesso IP per JSON REST API:
<Location "/wp-json">
Require ip 10.10.77.49 # Replace with your IP address
</Location>
location ~ ^/wp-json/ {
allow 10.10.77.49; # Replace with your allowed IP address
deny all;
}
Analogamente all'API JSON REST, è consigliabile disabilitare l'API XML-RPC poiché non è necessaria per la maggior parte delle installazioni WordPress.
Se l'API REST è necessaria, si consiglia di utilizzare invece l'API JSON REST.
XML-RPC presenta due principali debolezze:
Attacchi di forza bruta:
Attacchi Denial of Service tramite Pingback:
Se XML-RPC è abilitato, può ancora essere sfruttato per tali attacchi.
Audit:
(response) HTTP/1.1 405 Method Not Allowed Date: Mon, 25 Jun 2018 08:30:24 GMT Server: Apache Allow: POST Content-Length: 42 Content-Type: text/plain; charset=UTF-8
XML-RPC server accepts POST requests only.
**Rimedio:**
- Disabilitare la funzionalità XML-RPC utilizzando un plugin
**Installare il plugin "Disable XML-RPC-API" e attivarlo:**
1. Vai alla dashboard di amministrazione di WordPress
2. Vai su Plugin > Aggiungi nuovo.
3. Cerca "[Disable XML-RPC-API](https://wordpress.org/plugins/disable-xml-rpc-api/)" e installalo, poi attivalo.
4. L'API XML-RPC è ora disabilitata.
**Informazioni sugli attacchi pingback XML-RPC:**
1. Verificare che XML-RPC sia abilitato
```
# curl -i -k https://yourwordpress.com/xmlrpc.php
(response)
HTTP/1.1 405 Method Not Allowed
Date: Mon, 25 Jun 2018 08:30:24 GMT
Server: Apache
Allow: POST
Content-Length: 42
Content-Type: text/plain; charset=UTF-8
XML-RPC server accepts POST requests only.
```
2. Ricerca dei metodi XML-RPC disponibili
```
(request)
POST /xmlrpc.php HTTP/1.1
Host: yourwordpress.com
Content-Length: 135
<?xml version="1.0" encoding="utf-8"?>
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>
(response)
HTTP/1.1 200 OK
cache-control: no-cache, no-store, must-revalidate, max-age=0
...
Server: Apache
Content-Length: 4272
Content-Type: text/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param>
<value>
<array><data>
<value><string>system.multicall</string></value>
<value><string>system.listMethods</string></value>
<value><string>system.getCapabilities</string></value>
<value><string>demo.addTwoNumbers</string></value>
<value><string>demo.sayHello</string></value>
<value><string>pingback.extensions.getPingbacks</string></value>
<value><string>pingback.ping</string></value>
<value><string>mt.publishPost</string></value>
...
<value><string>wp.getUsersBlogs</string></value>
</data></array>
</value>
</param>
</params>
</methodResponse>
```
3. Eseguire pingback
- Il successo di un attacco pingback e i metodi di verifica specifici non sono descritti.
```
(request)
POST /xmlrpc.php HTTP/1.1
Host: yourwordpress.com
Content-Length: 303
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param>
<value><string>call-back url for pingback result</string></value>
</param>
<param>
<value><string>https://yourwordpress.com/</string></value>
</param>
</params>
</methodCall>
(response)
HTTP/1.1 200 OK
...
Server: Apache
Content-Length: 370
Content-Type: text/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>0</int></value>
</member>
<member>
<name>faultString</name>
<value><string></string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
```
### 7.4. Disabilitare WP-Cron o limitare la funzionalità
In WordPress, WP-Cron (wp-cron.php) viene utilizzato per automatizzare attività come la pubblicazione programmata di post, i controlli di aggiornamento di plugin/temi e l'invio di email di notifica.
WP-Cron funziona essenzialmente controllando l'elenco delle attività programmate ogni volta che una pagina viene caricata.
Il problema sorge quando c'è un carico pesante di pagine.
Poiché le attività di WP-Cron vengono eseguite con ogni caricamento di pagina, accessi ripetuti multipli comportano corrispondenti invocazioni di WP-Cron. Di conseguenza, le risorse di sistema potrebbero diventare scarse, causando un rallentamento o addirittura un arresto del sito.
Questo è un evento reale e viene spesso sfruttato negli attacchi di vulnerabilità mirati a WordPress.
Se WP-Cron non è necessario, è consigliabile disattivarlo.
Se necessario, limitare l'accesso solo agli host locali.
**Verifica:**
- Verificare che WP-Cron sia abilitato. (Predefinito: abilitato)```
# curl -i -k https://yourwordpress.com/wp-cron.php
(response)
HTTP/1.1 200 ok
Date: Mon, 25 Jun 2018 08:30:24 GMT
Server: Apache
Allow: POST
Content-Length: 42
Content-Type: text/plain; charset=UTF-8
Rimedio:
Passaggi per disabilitare WP-Cron:
/* That’s all, stop editing! Happy publishing. */define('DISABLE_WP_CRON', true);# Files Directive
<Files "wp-cron.php">
Require ip 127.0.0.1 # localhost only
</Files>
# FilesMatch Directive
<FilesMatch "^wp-cron\.php$">
Require ip 127.0.0.1 # localhost only
</FilesMatch>
# Location Directive
<Location "/wp-cron.php">
Require ip 127.0.0.1 # localhost only
</Location>
location = /wp-cron.php {
allow 127.0.0.1;
deny all;
access_log off;
log_not_found off;
}
Passaggi per attivare "ALTERNATE_WP_CRON":
/* That’s all, stop editing! Happy publishing. */define( 'ALTERNATE_WP_CRON', true );# Files Directive
<Files "wp-cron.php">
Require ip 127.0.0.1 # localhost only
</Files>
# FilesMatch Directive
<FilesMatch "^wp-cron\.php$">
Require ip 127.0.0.1 # localhost only
</FilesMatch>
# Location Directive
<Location "/wp-cron.php">
Require ip 127.0.0.1 # localhost only
</Location>
location = /wp-cron.php {
allow 127.0.0.1;
deny all;
access_log off;
log_not_found off;
}
Esempio: Utilizzare il cron di sistema (crontab):
.. ... */10 * * * * curl http://yourwordpress.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
*/10 * * * * cd /var/www/yourwordpress.com/htdocs; php /var/www/yourwordpress.com/htdocs/wp-cron.php?doing_wp_cron > /dev/null 2>&1
*/10 * * * * cd /var/www/example.com/htdocs; wp cron event run --due-now > /dev/null 2>&1
**Informazioni sull'esecuzione di un attacco DoS tramite wp-cron.php:**
- Inviare un volume elevato di richieste a wp-cron.php
- Ciò comporta il consumo eccessivo di risorse da parte dello script, sovraccaricando il server


## 8. Configurazione di sistema per un WordPress sicuro.
Garantire un funzionamento sicuro di WordPress richiede una corretta configurazione del server web e l'hardening dei componenti backend.
Ecco diversi elementi essenziali che devono essere verificati e implementati.
### 8.1. Assicurarsi di utilizzare versioni di WordPress e PHP non a fine vita (EOL)
Per mantenere un'installazione WordPress sicura, è essenziale utilizzare versioni di WordPress e PHP che non siano a fine vita (EOL).
Le versioni EOL non sono più supportate e non ricevono aggiornamenti di sicurezza, lasciando il tuo sito vulnerabile a problemi di sicurezza non corretti.
L'utilizzo di versioni supportate garantisce che eventuali vulnerabilità scoperte vengano risolte tempestivamente, proteggendo il tuo sito da potenziali attacchi.
Ecco cosa devi fare per verificare e aggiornare le versioni di WordPress e PHP:
**Audit:**
- Verificare che le versioni correnti di WordPress e PHP non siano EOL.
**Rimedio:**
- Installare ed eseguire versioni non EOL di WordPress e PHP.
- A partire da maggio 2024, la versione supportata di WordPress è 6.5 e successive. Le versioni supportate di PHP sono 8.1, 8.2 e 8.3.
- Se utilizzi un servizio di web hosting, sfrutta le loro funzionalità di cambio versione per assicurarti di eseguire versioni supportate sia di WordPress che di PHP.
**Stato EOL a maggio 2024**
1. PHP: [supported-versions](https://www.php.net/supported-versions.php)
- Versioni attualmente supportate: 8.1, 8.2, 8.3
2. WordPress: [current-releases](https://wordpress.org/download/releases/)
- Versioni attualmente supportate: serie 6.5
**Esempio: PHP in RockyLinux 8.5**
- In RockyLinux 8.5, le versioni PHP predefinite disponibili sono 7.2, 7.3 e 7.4.
```
# dnf module list php
Rocky Linux 8 - AppStream
Name Stream Profiles Summary
php 7.2 [d] common [d], devel, minimal PHP scripting language
php 7.3 common [d], devel, minimal PHP scripting language
php 7.4 common [d], devel, minimal PHP scripting language
Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled
# dnf module enable php:7.4
==============================================================================================
Package Architecture Version Repository Size
==============================================================================================
Enabling module streams:
httpd 2.4
php 7.4
Transaction Summary
==============================================================================================
Is this ok [y/N]: y
Complete!
```
- PHP 7 ha già raggiunto la fine del ciclo di vita (EOL); si consiglia di aggiornare a PHP 8.
- PHP 8 può essere installato dal repository REMI.
- Ecco un esempio di come abilitare e installare PHP 8.2 utilizzando REMI:
```
# Install PHP 8.2 in Rocky Linux 8
# dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
# dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
# dnf -y install yum-utils
# dnf module reset php
# dnf module install php:remi-8.2
Last metadata expiration check: 0:00:39 ago on Tue 13 Dec 2022 07:19:26 AM UTC.
Dependencies resolved.
=======================================================================================================================================
Package Architecture Version Repository Size
=======================================================================================================================================
Installing group/module packages:
php-cli x86_64 8.2.0-1.el8.remi remi-modular 5.4 M
php-common x86_64 8.2.0-1.el8.remi remi-modular 1.3 M
php-fpm x86_64 8.2.0-1.el8.remi remi-modular 1.9 M
php-mbstring x86_64 8.2.0-1.el8.remi remi-modular 574 k
php-xml x86_64 8.2.0-1.el8.remi remi-modular 254 k
Installing dependencies:
httpd-filesystem noarch 2.4.37-51.module+el8.7.0+1059+126e9251 appstream 41 k
libxslt x86_64 1.1.32-6.el8 baseos 249 k
oniguruma5php x86_64 6.9.8-1.el8.remi remi-safe 212 k
Installing weak dependencies:
nginx-filesystem noarch 1:1.14.1-9.module+el8.4.0+542+81547229 appstream 23 k
Installing module profiles:
php/common
Enabling module streams:
httpd 2.4
nginx 1.14
php remi-8.2
Transaction Summary
=======================================================================================================================================
Install 9 Packages
# dnf update
# dnf install php
Last metadata expiration check: 0:00:23 ago on Tue 13 Dec 2022 07:29:55 AM UTC.
Dependencies resolved.
=======================================================================================================================================
Package Architecture Version Repository Size
=======================================================================================================================================
Installing:
php x86_64 8.2.0-1.el8.remi remi-modular 1.8 M
Installing dependencies:
apr x86_64 1.6.3-12.el8 appstream 128 k
apr-util x86_64 1.6.1-6.el8.1 appstream 104 k
httpd x86_64 2.4.37-51.module+el8.7.0+1059+126e9251 appstream 1.4 M
httpd-tools x86_64 2.4.37-51.module+el8.7.0+1059+126e9251 appstream 108 k
libsodium x86_64 1.0.18-2.el8 epel 162 k
mailcap noarch 2.1.48-3.el8 baseos 38 k
mod_http2 x86_64 1.15.7-5.module+el8.6.0+823+f143cee1 appstream 153 k
rocky-logos-httpd noarch 86.3-1.el8 baseos 24 k
Installing weak dependencies:
apr-util-bdb x86_64 1.6.1-6.el8.1 appstream 23 k
apr-util-openssl x86_64 1.6.1-6.el8.1 appstream 26 k
php-opcache x86_64 8.2.0-1.el8.remi remi-modular 633 k
php-pdo x86_64 8.2.0-1.el8.remi remi-modular 166 k
php-sodium x86_64 8.2.0-1.el8.remi remi-modular 105 k
Transaction Summary
=======================================================================================================================================
Install 14 Packages
Total download size: 4.8 M
Installed size: 14 M
Is this ok [y/N]: y
# php -v
PHP 8.2.0 (cli) (built: Dec 6 2022 14:26:47) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.2.0, Copyright (c) Zend Technologies
with Zend OPcache v8.2.0, Copyright (c), by Zend Technologies
```
**Nota:**
- Per istruzioni dettagliate sull'installazione di PHP dal repository REMI: [rpms.remirepo.net](https://rpms.remirepo.net/)
- Documentazione sulla compatibilità tra WordPress e PHP: [php-compatibility-and-wordpress-versions](https://make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versions/)
### 8.2. Assicurarsi che siano abilitate solo le estensioni PHP necessarie per WordPress
Assicurati che siano abilitate solo le estensioni PHP necessarie per il tuo sito WordPress.
Estensioni non necessarie possono aumentare la superficie di attacco del tuo sito e potrebbero esporre WordPress a vulnerabilità di sicurezza.
Abilitando solo le estensioni richieste, puoi minimizzare i rischi potenziali e migliorare la sicurezza complessiva.
Di seguito sono elencate le estensioni necessarie per il corretto funzionamento di un sito WordPress. **(Non un elenco a fini di hardening della sicurezza)**
| Estensione | Descrizione |
|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| json | Utilizzato per le comunicazioni con altri server e l'elaborazione di dati in formato JSON. |
| mysqli | Si connette a MySQL per le interazioni con il database. |
| curl | Esegue operazioni di richiesta remota. |
| dom | Utilizzato per convalidare il contenuto dei widget di testo e per configurare automaticamente IIS7+. |
| exif | Lavora con i metadati memorizzati nelle immagini. |
| fileinfo | Utilizzato per rilevare il tipo MIME dei file caricati. |
| hash | Utilizzato per l'hashing, incluse password e pacchetti di aggiornamento. |
| igbinary | Migliora le prestazioni come sostituto drop-in per il serializzatore PHP standard. |
| imagick | Fornisce una migliore qualità delle immagini per i caricamenti multimediali. Vedi WP_Image_Editor per i dettagli. Ridimensionamento più intelligente delle immagini (per immagini più piccole) e supporto per anteprime PDF, quando Ghost Script è disponibile. |
| intl | Abilita operazioni consapevoli della localizzazione, incluse ma non limitate a formattazione, traslitterazione, conversione di codifica, operazioni del calendario, ordinamento conforme, individuazione dei confini del testo e lavoro con identificatori di locale, fusi orari e grafemi. |
| mbstring | Utilizzato per gestire correttamente il testo UTF8. |
| openssl | Connessioni basate su SSL ad altri host. |
| pcre | Aumenta le prestazioni della corrispondenza di pattern nelle ricerche del codice. |
| xml | Utilizzato per l'analisi XML, ad esempio da un sito di terze parti. |
| zip | Utilizzato per decomprimere plugin, temi e pacchetti di aggiornamento di WordPress. |
| bc | Per la matematica a precisione arbitraria, che supporta numeri di qualsiasi dimensione e precisione fino a 2147483647 cifre decimali. |
| filter | Utilizzato per filtrare in modo sicuro l'input dell'utente. |
| image | Se Imagick non è installato, la libreria grafica GD viene utilizzata come alternativa funzionalmente limitata per la manipolazione delle immagini. |
| iconv | Utilizzato per convertire tra set di caratteri. |
| shmop | Shmop è un insieme di funzioni facili da usare che permette a PHP di leggere, scrivere, creare ed eliminare segmenti di memoria condivisa Unix. |
| simplexml | Utilizzato per l'analisi XML. |
| sodium | Convalida le firme e fornisce byte casuali in modo sicuro. |
| xmlreader | Utilizzato per l'analisi XML. |
| zlib | Compressione e decompressione Gzip. |
Le estensioni essenziali possono essere trovate qui: [WordPress Hosting Handbook: PHP Extensions](https://make.wordpress.org/hosting/handbook/server-environment/#php-extensions)
**Audit:**
- Verificare che siano abilitate solo le estensioni PHP necessarie per il tuo sito WordPress.
**Rimedio:**
- Rimuovere eventuali estensioni non necessarie. A volte, le estensioni vengono installate insieme ai plugin, che potrebbero non essere richiesti per il tuo sito.
- Per controllare le estensioni PHP attualmente abilitate, puoi esaminare il file **php.ini** o utilizzare la funzione **phpinfo()** per elencare tutte le estensioni attive.

- Alcune estensioni, se non necessarie, dovrebbero essere disabilitate per prevenire potenziali problemi di sicurezza.
- Ad esempio, estensioni come exif, fileinfo, imap, soap, pdo_sqlite e opcache potrebbero essere sfruttate se lasciate abilitate senza un uso appropriato.
- Se stai utilizzando un servizio di web hosting, molti provider offrono interfacce facili da usare per modificare le impostazioni PHP, inclusa l'abilitazione o la disabilitazione delle estensioni PHP. Utilizzando queste funzionalità, puoi gestire le estensioni in modo efficace.
### 8.3. Assicurarsi della sicurezza dei plugin con funzionalità di caricamento file
I plugin con funzionalità di caricamento file possono rappresentare un rischio significativo per la sicurezza se non adeguatamente protetti. Le vulnerabilità nelle funzioni di caricamento file possono permettere agli attaccanti di caricare web shell, portando potenzialmente al completo compromesso del sistema. Pertanto, è fondamentale assicurarsi che qualsiasi funzionalità di caricamento file includa meccanismi di convalida e sanitizzazione.
Perché è importante?
1. Convalida dell'estensione:
- Il server dovrebbe convalidare le estensioni dei file rispetto a una whitelist di tipi consentiti per prevenire il caricamento di file dannosi.
2. Controllo del tipo MIME:
- Il tipo MIME del file dovrebbe essere verificato per assicurarsi che corrisponda al tipo previsto, aggiungendo un ulteriore livello di sicurezza.
3. Restrizioni del percorso di upload:
- Assicurarsi che non ci siano percorsi esposti che permettano l'accesso diretto ai file caricati senza convalida.
Le vulnerabilità di caricamento file sono particolarmente pericolose perché forniscono un percorso diretto per gli attaccanti per caricare codice eseguibile ed eseguire comandi arbitrari. Le vulnerabilità di caricamento file sono spesso più facili da identificare e sfruttare rispetto ad altre falle di sicurezza come SQL injection.
**Audit:**
- Identificare i plugin con funzionalità di caricamento file sul tuo sito WordPress.
- Verificare che questi plugin implementino controlli di convalida adeguati per i file caricati, inclusa la convalida dell'estensione e del tipo MIME.
**Rimedio:**
- Se un plugin con funzionalità di caricamento file manca di una convalida adeguata, migliora la sua sicurezza o rimuovi il plugin.
- Di seguito sono riportati esempi di plugin WordPress popolari con funzionalità di caricamento file e come gestiscono la convalida dei file.
**Esempio: Plugin WordPress popolari con gestione del caricamento file**
1. Contact Form 7
- Contact Form 7 è uno dei plugin per moduli più utilizzati in WordPress. Include funzionalità di base per il caricamento file con convalida dell'estensione e del tipo MIME.**Codice per le estensioni consentite e il controllo del tipo MIME:**
function wpcf7_allowed_file_extensions() { // Default allowed file extensions $allowed_file_extensions = array( 'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'csv', 'rtf', 'html', 'zip' ); return $allowed_file_extensions; }
function wpcf7_handle_upload( $file ) { $allowed_mime_types = wpcf7_allowed_file_extensions(); $file_type = wp_check_filetype( $file['name'] );
// Check if the file type is allowed
if ( ! in_array( $file_type['ext'], $allowed_mime_types ) ) {
return new WP_Error( 'wpcf7_upload_failed', __( 'File type is not allowed.', 'contact-form-7' ) );
}
// Handle the file upload
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
// Check if the upload was successful
if ( isset( $upload['error'] ) ) {
return new WP_Error( 'wpcf7_upload_failed', $upload['error'] );
}
return $upload;
}
In Contact Form 7, la funzione `wpcf7_allowed_file_extensions()` restituisce un elenco di estensioni di file consentite, e la funzione `wpcf7_handle_upload()` controlla se l'estensione del file è in questo elenco prima di procedere con il caricamento.
2. WPForms
- WPForms gestisce i caricamenti di file controllando i tipi di file consentiti.
**Codice di esempio per WPForms:**
```
function wpforms_get_file_types() {
// Return an array of allowed file types
return array( 'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx' );
}
function wpforms_process_file_upload( $file ) {
$allowed_file_types = wpforms_get_file_types();
$file_type = wp_check_filetype( $file['name'] );
if ( ! in_array( $file_type['ext'], $allowed_file_types ) ) {
return new WP_Error( 'wpforms_upload_failed', __( 'File type is not allowed.', 'wpforms' ) );
}
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
if ( isset( $upload['error'] ) ) {
return new WP_Error( 'wpforms_upload_failed', $upload['error'] );
}
return $upload;
}
```
3. WooCommerce
- WooCommerce definisce e controlla anche le estensioni di file consentite direttamente nel suo codice di gestione del caricamento.
**Codice di esempio per WooCommerce:**
```
function woocommerce_handle_upload( $file ) {
$allowed_file_types = array( 'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx' );
$file_type = wp_check_filetype( $file['name'] );
if ( ! in_array( $file_type['ext'], $allowed_file_types ) ) {
return new WP_Error( 'woocommerce_upload_failed', __( 'File type is not allowed.', 'woocommerce' ) );
}
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
if ( isset( $upload['error'] ) ) {
return new WP_Error( 'woocommerce_upload_failed', $upload['error'] );
}
return $upload;
}
```
**Esempio: Plugin di fileupload malevolo**
- Un plugin malevolo potrebbe sembrare innocuo ma sfruttare l'estensione fileinfo per aggirare i controlli di sicurezza:
```
<?php
/*
Plugin Name: Simple Malicious Upload
Description: A plugin with hidden malicious file upload capability.
Version: 1.0
*/
function simple_file_upload_menu() {
add_menu_page('File Upload', 'File Upload', 'manage_options', 'file-upload', 'simple_file_upload_page');
}
add_action('admin_menu', 'simple_file_upload_menu');
function simple_file_upload_page() {
?>
<h1>File Upload</h1>
<form method="post" enctype="multipart/form-data">
<input type="file" name="uploaded_file" />
<input type="submit" name="upload_file" value="Upload" />
</form>
<?php
if (isset($_POST['upload_file'])) {
simple_handle_file_upload();
}
}
function simple_handle_file_upload() {
if (!empty($_FILES['uploaded_file']['tmp_name'])) {
$file_tmp = $_FILES['uploaded_file']['tmp_name'];
$file_name = basename($_FILES['uploaded_file']['name']);
// Using fileinfo to check MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $file_tmp);
finfo_close($finfo);
// Insecure handling: allows any PHP files to be uploaded
if ($mime_type === 'text/plain' || $mime_type === 'application/x-php') {
$upload_dir = wp_upload_dir();
$upload_file = $upload_dir['path'] . '/' . $file_name;
// Move the uploaded file to the uploads directory
if (move_uploaded_file($file_tmp, $upload_file)) {
echo "File uploaded successfully.";
} else {
echo "File upload failed.";
}
} else {
echo "Invalid file type.";
}
}
}
?>
```
**Spiegazione dello sfruttamento:**
- Il plugin malevolo permette di caricare file PHP se il loro tipo MIME è `application/x-php.`
- Un attaccante può caricare una web shell PHP utilizzando questa funzionalità.
- Una volta caricato, l'attaccante accede all'URL del file ed esegue comandi arbitrari.
**Esempio: Codice di una web shell PHP**```
<?php
if (isset($_GET['cmd'])) {
echo "<pre>";
system($_GET['cmd']);
echo "</pre>";
}
?>
Dimostrazione dell’Attacco
http://yourwordpress.com/wp-content/uploads/webshell.php.http://yourwordpress.com/wp-content/uploads/webshell.php?cmd=ls, l’attaccante può eseguire comandi arbitrari.Nota:
Assicurarsi che le funzioni e le impostazioni PHP siano configurate correttamente può migliorare significativamente la sicurezza del tuo sito WordPress. Impostazioni mal configurate possono esporre il sito a varie vulnerabilità, tra cui esecuzione di codice remoto, divulgazione di informazioni e dirottamento di sessione. È fondamentale rafforzare PHP disabilitando o configurando correttamente queste funzioni.
Perché è importante?
Esecuzione di Codice Remoto:
allow_url_fopen e funzioni come exec possono abilitare l’esecuzione di codice remoto, portando a un potenziale compromesso del sistema.Divulgazione di Informazioni:
display_errors e expose_php possono far trapelare informazioni sensibili sulla configurazione del server, facilitando agli attaccanti la ricerca di vulnerabilità.Sicurezza delle Sessioni:
session.cookie_secure e session.cookie_httponly, proteggono i cookie di sessione dall’accesso tramite script lato client o dalla trasmissione su canali non sicuri.Audit:
Rimedio:
allow_url_fopen:
file_get_contents(), fopen(), include() e require() possono recuperare dati da posizioni remote tramite FTP o HTTP.allow_url_fopen per varie funzionalità.; (Opzionale) Disabilitare allow_url_fopen, se non necessario
allow_url_fopen = Off
display_errors:
; Disabilitare la visualizzazione degli errori PHP sul sito WordPress
display_errors = Off
expose_php:
; Impedire l'esposizione della versione PHP negli header di risposta HTTP
expose_php = Off
session.cookie_secure
session.cookie_secure:
Esempio: Di seguito è riportato un Elenco di Funzioni PHP Disabilitate in un WordPress Reale``` system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail
### 8.5. Assicurarsi che il Server Web Venga Eseguito come Utente Non Root - Utente e Gruppo Unici e Non Privilegiati per l'Applicazione del Server
Nella maggior parte dei casi, i server web vengono eseguiti come utenti quali "www-data" (Debian/Ubuntu) o "apache" (RHEL/CentOS).
Questi utenti sono account di servizio dedicati senza privilegi speciali sul server e vengono utilizzati per designare l'utente e il gruppo che i processi worker del server web assumeranno.
Se questi utenti hanno privilegi di sistema o vengono eseguiti come root, devono essere modificati.
**Audit:**
- Verificare l'utente che esegue il processo del server web. (Nello specifico, si tratta del processo worker del server web.)
1. Per Apache:
```
# ps -ef | grep httpd
root 2257 1 0 Apr08 ? 00:00:01 /usr/local/apache/bin/httpd -k start
apache 5678 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
apache 5679 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
apache 5680 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
apache 5681 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
```
2. Per Nginx:
```
# ps -ef | grep nginx
root 626653 1 0 Apr08 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 626654 626653 0 Apr08 ? 00:00:00 nginx: worker process
nginx 626655 626653 0 Apr08 ? 00:00:00 nginx: worker process
nginx 626656 626653 0 Apr08 ? 00:00:00 nginx: worker process
nginx 626657 626653 0 Apr08 ? 00:00:00 nginx: worker process
```
**Rimedio:**
- Il server web dovrebbe essere eseguito come account dedicato e non privilegiato.
- Nella maggior parte dei casi, viene utilizzato uno di questi account comunemente usati come "www-data", "apache", "nginx", "nobody" o "daemon".
1. Per Apache:
```
# vim /etc/httpd/httpd.conf
..
...
User www-data
Group www-data
..
...
```
2. Per Nginx:
```
# vim /etc/nginx/nginx.conf
..
...
user daemon;
```
**Nota:**
- Gli utenti di processo per il server web non dovrebbero avere privilegi di login tramite shell. ```
# cat /etc/passwd | grep -i www-data
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
WordPress è costruito con PHP, quindi è necessaria una corretta configurazione del sistema per eseguire correttamente il codice PHP.
L'esecuzione del codice PHP in WordPress è gestita da PHP-FPM, un FastCGI Process Manager. Per garantire il funzionamento sicuro di PHP-FPM, dovrebbe essere eseguito sotto un account di servizio dedicato e non privilegiato.
Generalmente, l'account del processo del server web e l'account di PHP-FPM sono impostati sullo stesso account. Tuttavia, per una maggiore sicurezza, è meglio eseguirli sotto account separati.
Ecco due motivi:
Isolamento dei processi:
Principio del minimo privilegio:
Audit:
root 1234 1 0 12:00 ? 00:00:01 php-fpm: master process (/etc/php-fpm.conf) php-fpm 5678 1234 0 12:00 ? 00:00:00 php-fpm: pool www php-fpm 5679 1234 0 12:00 ? 00:00:00 php-fpm: pool www php-fpm 5680 1234 0 12:00 ? 00:00:00 php-fpm: pool www php-fpm 5681 1234 0 12:00 ? 00:00:00 php-fpm: pool www
**Rimedi:**
- PHP-FPM dovrebbe essere eseguito con un account non privilegiato e dedicato.
- Nella maggior parte dei casi, l'account utilizzato è "php-fpm".
**Modifica dell'account del processo per PHP-FPM:**```
# vim /etc/php-fpm.d/www.conf # Adjust the path based on your PHP version
...
user = php-fpm
group = php-fpm
listen.owner = php-fpm
listen.group = php-fpm
L'account di processo PHP-FPM non dovrebbe avere privilegi di accesso shell.```
php-fpm❌999:999:PHP-FPM:/run/php:/usr/sbin/nologin
**Nota:**
- Dal punto di vista della sicurezza, è preferibile utilizzare account utente diversi per i processi PHP-FPM e per i processi del server web.
- Quando viene chiesto quale sia meglio per la sicurezza, dovrebbero essere diversi. Evita di utilizzare lo stesso account di esecuzione in questo contesto.
### 8.7. Garantire una Configurazione Sicura della Directory Home di WordPress
Per gestire un server web in modo sicuro, è fondamentale configurare correttamente la proprietà e i permessi della directory home di WordPress.
Nella maggior parte dei casi, la proprietà e i permessi dei file e delle directory di WordPress sono impostati per corrispondere all'account del processo del server web.
Questa configurazione consente al server web di accedere ai file nella webroot e di operare senza errori.
Tuttavia, questa configurazione non è sicura.
Ad esempio, se il processo del server web è 'apache' e sia la directory webroot che i file sono di proprietà di 'apache', ciò potrebbe portare a gravi vulnerabilità.
Gli attaccanti potrebbero sfruttare queste vulnerabilità per ottenere accesso non autorizzato a file e directory critici all'interno della directory home di WordPress.
**Esempio di Vulnerabilità Comune:**
- Se sia l'account del processo del server web che la directory home (webroot) e i file sono di proprietà di 'apache':
- In caso di vulnerabilità nel sito web e di accesso esterno al sistema (come una webshell), gli attaccanti possono eseguire varie azioni all'interno della webroot:
1. Creare, modificare o eliminare file o directory all'interno della webroot.
2. Manipolare i log di accesso web, inclusa modifica, eliminazione o creazione. (tranne in alcuni ambienti)
3. Possono ottenere accesso ai cookie di sessione attivi degli utenti loggati. (in ambienti particolarmente vulnerabili)
Per mitigare questi rischi, è fondamentale regolare adeguatamente la proprietà e i permessi della directory home di WordPress.
**Rimedio:**
- Imposta il proprietario della directory home (webroot) e dei file su 'root:root'. (Evita di impostarlo uguale all'account del processo del server web)
- L'UMASK predefinito per directory e file è 022. (Directory: 755, File: 644)
- Per le directory che richiedono permessi di scrittura, come il caricamento di file tramite il servizio web, imposta il proprietario di quelle directory sull'account del processo del server web.
**Directory che Generalmente Richiedono Permessi di Scrittura in WordPress:**```
/wp-content/uploads
/wp-content/cache
/wp-content/wflogs (when using security plugins like Wordfence)
/wp-content/upgrade (used during the WordPress upgrade process)
Dopo aver configurato la proprietà e i permessi della directory home secondo le misure di correzione sopra indicate, l'output della directory home di WordPress è il seguente:
Esempio: WordPress Home Directory``` drwxr-xr-x 5 root root 4096 May 27 2024 . drwxr-xr-x 3 root root 4096 May 27 2024 .. -rw-r--r-- 1 root root 418 May 27 2024 index.php -rw-r--r-- 1 root root 19935 May 27 2024 license.txt -rw-r--r-- 1 root root 7346 May 27 2024 readme.html -rw-r--r-- 1 root root 7106 May 27 2024 wp-activate.php drwxr-xr-x 9 root root 4096 May 27 2024 wp-admin -rw-r--r-- 1 root root 351 May 27 2024 wp-blog-header.php -rw-r--r-- 1 root root 2328 May 27 2024 wp-comments-post.php -rw-r--r-- 1 root root 4973 May 27 2024 wp-config-sample.php -rw-r--r-- 1 root root 2755 May 27 2024 wp-config.php drwxr-xr-x 8 root root 4096 May 27 2024 wp-content -rw-r--r-- 1 root root 3940 May 27 2024 wp-cron.php drwxr-xr-x 25 root root 12288 May 27 2024 wp-includes -rw-r--r-- 1 root root 2496 May 27 2024 wp-links-opml.php -rw-r--r-- 1 root root 3300 May 27 2024 wp-load.php -rw-r--r-- 1 root root 51556 May 27 2024 wp-login.php -rw-r--r-- 1 root root 8403 May 27 2024 wp-mail.php -rw-r--r-- 1 root root 24568 May 27 2024 wp-settings.php -rw-r--r-- 1 root root 30869 May 27 2024 wp-signup.php -rw-r--r-- 1 root root 4620 May 27 2024 wp-trackback.php -rw-r--r-- 1 root root 3065 May 27 2024 xmlrpc.php
**Nel caso in cui gli account dei processi php-fpm e del server web siano diversi (Permessi Separati)**
Se l'account del processo del server web è "apache" e l'account del processo php-fpm è "php-fpm".
Modifica i permessi delle directory che richiedono accesso in scrittura in WordPress (ad esempio, /wp-content/uploads).
- Proprietario: php-fpm
- Gruppo: apache
- Permessi della directory: 775 (755 se necessario)
**Struttura dei file e delle directory**
Imposta i permessi di scrittura per le directory necessarie in modo che entrambi gli account "php-fpm" e "apache" possano scrivere.```
ex) /service/wordpress/www
├── index.php (root:root, 644)
├── license.txt (root:root, 644)
├── readme.html (root:root, 644)
├── wp-activate.php (root:root, 644)
├── wp-admin/ (root:root, 755)
├── wp-blog-header.php (root:root, 644)
├── wp-comments-post.php (root:root, 644)
├── wp-config-sample.php (root:root, 644)
├── wp-config.php (root:root, 644)
├── wp-content/ (root:root, 755)
│ ├── plugins/ (root:root, 755)
│ ├── themes/ (root:root, 755)
│ ├── uploads/ (php-fpm:apache, 775)
│ │ ├── 2024/ (php-fpm:apache, 775)
│ │ └── ... (php-fpm:apache, 775)
│ └── ... (root:root, 755)
├── wp-cron.php (root:root, 644)
├── wp-includes/ (root:root, 755)
├── wp-links-opml.php (root:root, 644)
├── wp-load.php (root:root, 644)
├── wp-login.php (root:root, 644)
├── wp-mail.php (root:root, 644)
├── wp-settings.php (root:root, 644)
├── wp-signup.php (root:root, 644)
├── wp-trackback.php (root:root, 644)
└── xmlrpc.php (root:root, 644)
Summary
Impostandolo in questo modo, è possibile separare i permessi del web server e di PHP-FPM, applicare correttamente le impostazioni di proprietà e permessi della directory home e migliorare la sicurezza.
Questo metodo si applica non solo a WordPress ma anche a qualsiasi struttura di web server che serve contenuti web.
Assicurarsi che l'esecuzione di PHP sia disabilitata nelle directory in cui i file possono essere caricati è fondamentale per mantenere un ambiente sicuro.
Le directory di upload con permessi di scrittura sono potenziali bersagli per gli attaccanti che possono caricare script dannosi, come web shell, che possono essere eseguiti per compromettere il server.
Perché è importante?
Mitigare gli attacchi di web shell:
Ridurre la superficie d'attacco:
Conformità con le best practice di sicurezza:
Verifica:
Risoluzione:
/wp-content/uploads.Passaggi di configurazione
<Location "/wp-content/uploads">
SetHandler application/octet-stream
</Location>
location /wp-content/uploads {
default_type application/octet-stream;
}
<Location "/wp-content/uploads">
php_flag engine off
# or alternatively
php_value engine 0
</Location>
<Location "/wp-content/uploads">
<FilesMatch "\.php$">
SetHandler none
Require all denied
</FilesMatch>
</Location>
<Directory "/var/www/html/yourwordpress/wp-content/uploads">
# Disable PHP execution
<FilesMatch "\.php$">
SetHandler none
Require all denied
</FilesMatch>
</Directory>
location /wp-content/uploads {
location ~ \.php$ {
fastcgi_pass off;
}
}
location /wp-content/uploads {
location ~ \.php$ {
deny all;
}
}
Queste configurazioni assicurano che anche se un file PHP viene caricato nella directory /wp-content/uploads, non possa essere eseguito, prevenendo così potenziali attacchi.
Spiegazione delle direttive di configurazione
SetHandler application/octet-stream:
php_flag engine off / php_value engine 0:
SetHandler none:
Nota:
/wp-content/uploads sarà ancora accessibile e visualizzato correttamente utilizzando un tag :```
Applicando queste configurazioni, migliori significativamente la directory di upload da potenziali vulnerabilità di esecuzione di script.
### 8.9. Assicurati che il Server Web Risponda Solo agli Host Header Basati sul Dominio
Per proteggere il tuo server web, è essenziale assicurarsi che risponda solo alle richieste indirizzate al tuo nome di dominio e non alle richieste effettuate direttamente all'indirizzo IP del server.
Ciò può essere ottenuto attraverso una corretta configurazione delle direttive VirtualHost.
Nella maggior parte dei casi, i servizi web sono accessibili tramite un nome di dominio, come `https://yourwordpress.com`. Il server web riceve questa richiesta e serve il contenuto appropriato. Per applicare questo comportamento, dobbiamo configurare il server web in modo che risponda solo alle richieste con l'header Host corretto.
**Potenziali Rischi per la Sicurezza nel Consentire l'Accesso Basato su IP**
1. Enumerazione dei Servizi:
- Gli aggressori possono utilizzare gli indirizzi IP per enumerare i servizi in esecuzione sul server, aumentando il rischio di scoprire e sfruttare vulnerabilità.
2. Esposizione di Informazioni Sensibili:
- Server mal configurati potrebbero esporre directory, file o altre informazioni sensibili quando vengono acceduti tramite IP, che non dovrebbero essere accessibili pubblicamente.
3. Bypass dei Controlli di Sicurezza:
- L'accesso basato su IP potrebbe aggirare le misure di sicurezza che vengono applicate solo per l'accesso basato sul dominio, portando potenzialmente a un accesso non autorizzato.
**Verifica:**
- Verifica che il server web sia configurato per rispondere solo alle richieste basate sul dominio e non all'accesso diretto tramite indirizzo IP.
**Rimedio:**
- Configura il server web per gestire le richieste solo in base al dominio specificato e per negare o reindirizzare altre richieste in modo appropriato.
**Passaggi di Configurazione**
1. Configurazione VirtualHost Predefinita
- Crea un VirtualHost predefinito che intercetti tutte le richieste non specificate e restituisca un 403 Forbidden o le reindirizzi.
**Per Apache:**
```
<VirtualHost _default_:80>
DocumentRoot /var/www/html/yourwordpress
...
<Location />
Require all denied
</Location>
<VirtualHost _default_:443>
DocumentRoot /var/www/html/yourwordpress
...
SSLEngine on
SSLCertificateFile /path/to/ssl/certificate.crt;
SSLCertificateKeyFile /path/to/ssl/private.key;
...
<Location />
Require all denied
</Location>
</VirtualHost>
```
**Per Nginx:**
```
server {
listen 80 default_server;
return 403;
}
server {
listen 443 ssl default_server;
...
ssl_certificate /path/to/ssl/certificate.crt;
ssl_certificate_key /path/to/ssl/private.key;
...
return 403;
}
```
2. Configurazione VirtualHost Basato sul Dominio
- Assicurati di avere un VirtualHost configurato per il tuo dominio.
**Per Apache:**
```
<VirtualHost *:80>
ServerName yourwordpress.com
...
Redirect permanent / https://yourwordpress.com/
</VirtualHost>
<VirtualHost *:443>
ServerName yourwordpress.com
DocumentRoot /var/www/html/yourwordpress
...
SSLEngine on
SSLCertificateFile /etc/httpd/to/ssl/certificate.crt;
SSLCertificateKeyFile /etc/httpd/to/ssl/private.key;
...
</VirtualHost>
```
**Per Nginx:**
```
server {
listen 443 ssl;
server_name yourwordpress.com;
root /var/www/html/wordpress;
...
ssl_certificate /path/to/ssl/certificate.crt;
ssl_certificate_key /path/to/ssl/private.key;
...
```
3. Test
- Ecco la creazione del VirtualHost Predefinito e del VirtualHost Basato sul Dominio per `yourwordpress.com`.
- Dopo aver creato il VirtualHost Predefinito e quello Basato sul Dominio per `yourwordpress.com`, l'accesso per richieste non basate sul dominio (https://ip) viene negato (errore 403), ottenendo una schermata di errore 403.
```
$ curl -i -k http(s)://10.10.66.88
HTTP/1.1 403 Forbidden
Server: nginx
Date: Mon, 03 Jun 2024 23:23:13 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
$ curl -i -k https://yourwordpress.com
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 03 Jun 2024 23:32:12 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 9
Connection: keep-alive
Hello, yourwordpress.com
```
- Se c'è necessità di comunicazione tra server o all'interno della stessa sottorete IP, è possibile configurare il VirtualHost Predefinito per consentire l'accesso da indirizzi IP specifici.
Implementando queste configurazioni, il server web risponde solo alle richieste indirizzate al tuo dominio.
### 8.10. Configurazione WebServer Completata
Ecco un esempio di configurazione completa del server web che include le linee guida di sicurezza. Adatta e utilizza questa configurazione in base al tuo ambiente server web WordPress.
1. Apache
```
<VirtualHost _default_:80>
DocumentRoot /var/www/html/yourwordpress
ErrorLog /var/log/httpd/http.ip.error.log
CustomLog /var/log/httpd/http.ip.access.log combined
<Location />
Require all denied
</Location>
<VirtualHost _default_:443>
DocumentRoot /var/www/html/current/public
ErrorLog /var/log/httpd/https.ip.error.log
CustomLog /var/log/httpd/https.ip.access.log combined
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.crt
SSLCertificateKeyFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.key
SSLSessionTimeout 1d
SSLSessionCache shared:MozSSL:10m
SSLSessionTickets off
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
<Location />
Require all denied
</Location>
</VirtualHost>
<VirtualHost *:80>
ServerName yourwordpress.com
Redirect permanent / https://yourwordpress.com/
</VirtualHost>
<VirtualHost *:443>
ServerName yourwordpress.com
Protocols h2 http/1.1
DocumentRoot /var/www/html/current/public
ErrorLog /var/log/httpd/https.yourwordpress.com.error.log
CustomLog /var/log/httpd/https.yourwordpress.com.access.log combined
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.crt
SSLCertificateKeyFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.key
SSLSessionTimeout 1d
SSLSessionCache shared:MozSSL:10m
SSLSessionTickets off
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
<Directory /var/www/html/current/public>
Options -Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Deny PHP execution in uploads directory
<Directory "/var/www/html/current/public/wp-content/uploads">
<FilesMatch "\.php$">
SetHandler none
Require all denied
</FilesMatch>
</Directory>
# PHP Serving
ProxyPassMatch ^/(?!wp-content/uploads/.*\.php$)(.*\.php(/.*)?)$ unix:/var/run/php-fpm/php-fpm.sock|fcgi://localhost/var/www/html/current/public
#ProxyPassMatch ^/(?!wp-content/uploads/.*\.php$)(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/html/current/public
# Favicon
<Location "/favicon.ico">
ErrorDocument 404 "Not Found"
SetEnvIf Request_URI "^/favicon\.ico$" no_log
</Location>
# Robots.txt
<Location "/robots.txt">
Require all granted
SetEnvIf Request_URI "^/robots\.txt$" no_log
</Location>
# Restrict access to wp-cron.php
<Files "wp-cron.php">
Require all denied
Require ip 127.0.0.1
</Files>
# Restrict access to wp-json
<Location "/wp-json/">
Require all denied
Require ip 127.0.0.1
Require ip 10.10.77.49
Require ip 10.10.71.20
</Location>
# Restrict access to wp-admin
<Location "/wp-admin">
Require all denied
Require ip 10.10.77.49
Require ip 10.10.71.20
</Location>
<Files "wp-login.php">
Require all denied
Require ip 10.10.77.49
Require ip 10.10.71.20
</Files>
# Deny access to hidden files
<FilesMatch "^\.">
Require all denied
</FilesMatch>
</VirtualHost>
```
2. Nginx
```
server {
listen 80 default_server;
listen 443 default_server ssl http2;
error_log /var/log/nginx/http.ip.error.log;
access_log /var/log/nginx/http.ip.access.log main;
ssl_certificate /etc/nginx/conf.d/cert/yourwordpress.com_ssl.crt;
ssl_certificate_key /etc/nginx/conf.d/cert/yourwordpress.com_ssl.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
location / {
deny all;
}
}
server {
listen 443 ssl http2;
server_name yourwordpress.com;
root /var/www/html/wordpress;
error_log /var/log/nginx/https.yourwordpress.com.error.log;
access_log /var/log/nginx/https.yourwordpress.com.access.log main;
ssl_certificate /etc/nginx/conf.d/cert/yourwordpress.com_ssl.crt;
ssl_certificate_key /etc/nginx/conf.d/cert/yourwordpress.com_ssl.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Restrict to access Wordpress Cron
location = /wp-cron.php {
allow 127.0.0.1;
deny all;
access_log off;
log_not_found off;
}
# Restrict to access json rest-api
location ~ ^/wp-json/ {
allow 127.0.0.1; # Allow localhost
allow 10.10.77.49; # Allow myip
allow 10.10.71.20; # Allow myip
deny all;
access_log off;
log_not_found off;
}
# Restrict to access Wordpress Admin
location = /wp-admin {
allow 10.10.77.49; # Allow myip
allow 10.10.71.20; # Allow myip
deny all;
}
location ~* \wp-login.php {
allow 10.10.77.49; # Allow myip
allow 10.10.71.20; # Allow myip
deny all;
}
# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~ /\. {
deny all;
}
# Deny access to any files with a .php extension in the uploads directory
location /wp-content/uploads {
location ~ \.php$ {
deny all;
}
}
# Other example
# location ~* /(?:uploads|files)/.*\.php$ {
# deny all;
# }
# Rewrite rules, sends everything through index.php and keeps the appended query string intact
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Serving PHP
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\\.php)(/.+)$;
# fastcgi_pass 127.0.0.1:9000; # With php-cgi (or other tcp sockets):
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; # With php-fpm (or other unix sockets):
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
}
```
## 9. Assicurati degli Aggiornamenti di Sicurezza di WordPress
WordPress affronta le vulnerabilità di sicurezza rilasciando nuove versioni di aggiornamento quando vengono scoperte vulnerabilità.
Ad esempio, se viene trovata una vulnerabilità di sicurezza in WordPress 6.5.2, verrà affrontata e distribuita nella versione 6.5.3.
Poiché gli aggiornamenti di sicurezza non vengono gestiti versione per versione, è necessario effettuare aggiornamenti regolari delle versioni per affrontare le vulnerabilità di sicurezza.
Fai riferimento alle informazioni ufficiali sul rilascio di WordPress per gli aggiornamenti:
[WordPress Releases](https://wordpress.org/download/releases/)
**Rimedio:**
- Aggiorna regolarmente WordPress.
- WordPress non gestisce gli aggiornamenti (inclusi quelli di sicurezza) versione per versione.
- A partire dal 20 maggio 2024, solo la versione 6.5 è in manutenzione.
**Nota:**
- Le versioni Beta, Nightly builds e altri checkout Subversion non sono supportati.
- Evita di utilizzare prodotti fork o versioni che non siano rilasci ufficiali di WordPress.
- Documentazione per le versioni supportate: [Supported Versions](https://wordpress.org/documentation/article/supported-versions/)
## 10. Assicurati Controlli Regolari delle Vulnerabilità di Sicurezza per WordPress
WordPress è un software di sistema di gestione dei contenuti (CMS).
Poiché è un software preconfezionato, le vulnerabilità di sicurezza si verificano principalmente nei suoi componenti (file core, plugin, temi, ecc.).
A differenza delle applicazioni web sviluppate su misura per requisiti specifici, l'identificazione e la risoluzione delle vulnerabilità di sicurezza devono essere effettuate utilizzando metodi adatti a WordPress.
Se un sito web costruito con WordPress non è stato personalizzato in modo estensivo e conserva la natura di WordPress, le vulnerabilità di sicurezza possono essere facilmente controllate utilizzando WPScan.
WPScan è un software parzialmente a pagamento, ma il livello gratuito di base non ha limitazioni funzionali. Consente controlli regolari e risposte alle vulnerabilità di sicurezza di WordPress.
**Rimedio:**
- Esegui controlli regolari delle vulnerabilità utilizzando WPScan (o strumenti simili in grado di scansionare WordPress).
- Se vengono trovate vulnerabilità, verifica ed esegui le azioni necessarie per eliminarle. Nella maggior parte dei casi, ciò viene risolto tramite aggiornamenti.
- Fai riferimento alla [documentazione utente di WPScan](https://github.com/wpscanteam/wpscan/wiki/WPScan-User-Documentation).
- Ulteriori informazioni sulle vulnerabilità comuni spesso trovate in WordPress: [Extending WordPress Common Security Vulnerabilities](https://learn.wordpress.org/tutorial/extending-wordpress-common-security-vulnerabilities/)
**WPScan:**```
_______________________________________________________________
__ _______ _____
\ \ / / __ \ / ____|
\ \ /\ / /| |__) | (___ ___ __ _ _ __ ®
\ \/ \/ / | ___/ \___ \ / __|/ _` | '_ \
\ /\ / | | ____) | (__| (_| | | | |
\/ \/ |_| |_____/ \___|\__,_|_| |_|
WordPress Security Scanner by the WPScan Team
Version 3.8.22
Sponsored by Automattic - https://automattic.com/
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________
[+] URL: https://yourwordpress.com/ [192.168.10.100]
[+] Started: Fri May 24 14:39:06 2024
Interesting Finding(s):
[+] Headers
..
...
| - content-security-policy: upgrade-insecure-requests
| Found By: Headers (Passive Detection)
| Confidence: 100%
..
...
[+] XML-RPC seems to be enabled: https://yourwordpress.com/xmlrpc.php
| Found By: Link Tag (Passive Detection)
| Confidence: 30%
| References:
| - http://codex.wordpress.org/XML-RPC_Pingback_API
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
| - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/
..
...
[+] Finished: Fri May 24 14:39:48 2024
[+] Requests Done: 187
[+] Cached Requests: 7
[+] Data Sent: 56.32 KB
[+] Data Received: 605.595 KB
[+] Memory used: 276.602 MB
[+] Elapsed time: 00:00:42
| no | Ruolo | Descrizione | Personale amministrativo |
|---|
| 1 | Amministratore | Ha pieno accesso a tutte le funzionalità di WordPress e può gestire tutti i contenuti del sito. | Amministratori di sistema |
| 2 | Editore | Può gestire e pubblicare i post di altri utenti, oltre a modificare e pubblicare contenuti. | Responsabili dei servizi operativi, Collaboratori interni di contenuti |
| 3 | Autore | Può scrivere e pubblicare i propri post, e ha il permesso di modificare i propri post. | Collaboratori interni di contenuti |
| 4 | Collaboratore | Può scrivere contenuti ma non pubblicarli. I post vengono revisionati e pubblicati da un amministratore. | Collaboratori interni di contenuti |
| 5 | Iscritto | Può accedere al sito e gestire il proprio profilo personale, ma non può scrivere o modificare contenuti. | Collaboratori interni di contenuti |
; Assicurarsi che i cookie di sessione vengano inviati su HTTPS
session.cookie_secure = On
session.cookie_httponly
session.cookie_httponly:
; Rendere i cookie di sessione inaccessibili a JavaScript
session.cookie_httponly = On
open_basedir:
; Limitare l'accesso ai file PHP alla directory specificata
open_basedir = "/path/to/your/web/root"
Esempio:
open_basedir = "/var/www/html:/tmp"
Questa configurazione permette a PHP di accedere solo ai file all’interno della directory /var/www/html e della directory temporanea /tmp.
disable_functions:
; Disabilitare funzioni PHP potenzialmente pericolose
disable_functions = "system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail"
Spiegazione delle Funzioni Disabilitate:
system, exec, shell_exec, passthru:
mysql_list_dbs:
ini_alter:
dl:
symlink, link:
chgrp:
leak:
popen:
apache_child_terminate:
virtual:
mb_send_mail: