Le blocage des fichiers zip par le WAF (Web Application Firewall) de Cloudflare est un casse-tête pour de nombreux développeurs WordPress, surtout lors de la mise à jour de plugins et de thèmes. Heureusement, il existe une solution pour contourner ce problème sans compromettre la sécurité de votre site WordPress.
Pourquoi le WAF bloque-t-il les fichiers Zip ?
Cloudflare met régulièrement à jour ses Managed Rules pour renforcer la sécurité. Un upload de fichier zip peut être un vecteur pour des attaques malveéillantes, comme l’installation de shells. Ainsi, Cloudflare bloque ces requêtes pour protéger votre site.
Dans notre cas, par contre, cela nous empêche de faire nos mises à jour et c’est quand même plus simple de mettre à jour les plugins et thèmes payants avec un fichier zip, plutôt que de passer par SFTP ou wp-cli.
Solution #1: créer une exception dans le WAF de Cloudflare
Évidemment, nous n’allons pas désactiver ces règles qui fonctionnent si bien et ajoutent une couche de protection à notre site. Non, nous allons simplement créer une exception aux Managed Rules, que nous placerons avant tous les autres set de règles pour qu’elle soit prise en compte en priorité.
Étape 1: rendez-vous dans le Dashboard de Cloudflare
Allez dans Security > WAF > Managed Rules.
Voici ce que vous obtenez:
Cliquez ensuite sur le bouton Add exception à droite.
Aujourd’hui, je régle un petit souci d’affichage sur un une boutique WooCommerce propulsée par Astra.
Le problème est le suivant: lorsqu’on visite le site depuis un mobile, il est possible de faire bouger le contenu de la page vers la gauche, ce qui dévoile une hideuse colonne grise/verte sur le côté droit. Pourtant, aucun élément ne semble dépasser la largeur du contenu.
L’identification de l’élément causant ce défaut d’affichage n’est pas vraiment une chose aisée, car visuellement, on ne distingue rien qui dépasse.
Vanilla JS à la rescousse
Voici un petit script en Vanilla JS qui permet de trouver l’élément qui dépasse la largeur du viewport de votre page:
/**
* Identify elements that exceed the viewport width
* by Matt Biscay
* https://www.skyminds.net/?p=614036
*/
document.addEventListener("DOMContentLoaded", function() {
const elements = document.querySelectorAll('body *');
elements.forEach(element => {
if (element.offsetWidth > document.documentElement.clientWidth) {
console.log('Element causing overflow: ', element);
}
});
});
CSS: couvrez cet overflow que je ne saurais voir
Une fois votre ou vos éléments identifiés, il ne vous reste plus qu’à cacher son overflow-x :
Sur Orion, j’ai installé ma WordPress Rocket Stack qui est configurée avec la stack suivante:
Ubuntu Server 22.04 LTS
MySQL 8+
NginX 1.25+
PHP 8.3
Redis
Nginx FastCGI Cache
Fail2ban
LetsEncrypt avec acme.sh
Hébergement
L’hébergement est la base de votre site, c’est tout simplement la fondation sur laquelle va reposer votre code.
Vous avez tout intérêt à avoir un très bon hébergeur : il doit être rapide dès le départ et offrir de bonnes garanties en termes de performance et de sécurité.
Si vous avez un site WordPress ou WooCommerce, je ne peux que vous recommander Kinsta, WPEngine ou Nexcess. Tous trois sont de très bons hébergeurs, particulièrement orientés vers la performance avec des ressources garanties et un support technique réactif et efficace en cas de besoin.
Personnellement, j’utilise un serveur dédié chez OVH parce que j’héberge pas mal de sites et j’ai besoin d’avoir un contrôle fin sur la configuration de chacun des services.
Ubuntu Server
J’étais auparavant sous Debian mais j’ai finalement opté pour Ubuntu Server 22.04 LTS pour ce nouveau serveur.
L’avantage d’Ubuntu est de pouvoir disposer des mises à jour plus rapidement que sous Debian.
Aujourd’hui, nous allons tester la compatibilité avec PHP 8 de tous les sites WordPress du serveur, en ligne de commande et de manière automatisée.
Fin du support pour PHP 7.4 pour novembre 2022
Le cycle de vie de PHP 7.4 est atteint et il n’y aura plus de mises à jour ni de support à la fin du mois de novembre 2022. Cela signifie qu’il est désormais temps de passer sous PHP 8 pour bénéficier des dernières améliorations techniques de PHP et des mises à jour de sécurité.
Et mieux vaut s’y prendre un peu plus tôt, notamment si vous possédez une boutique WooCommerce pour ne pas être pris au dépourvu en pleine période de fêtes (Black Friday, Noël…).
Tester votre code avec php-parallel-lint
Nous allons créer un nouveau projet composer avec php-parallel-lint qui se chargera de scanner notre code et de remonter toutes les incompatibilités, fonctions obsolètes ou problématiques susceptibles de donner des avertissements ou des erreurs lors du basculement vers PHP 8.
Commençons par installer php-parallel-lint:
composer create-project php-parallel-lint/php-parallel-lint php-parallel-lint --no-dev
# ou avec un user :
# sudo su -l www-data -s /bin/bash -c "composer create-project php-parallel-lint/php-parallel-lint php-parallel-lint --no-dev"
Résultat de la commande:
Creating a "php-parallel-lint/php-parallel-lint" project at "./php-parallel-lint"
Installing php-parallel-lint/php-parallel-lint (v1.3.2)
- Installing php-parallel-lint/php-parallel-lint (v1.3.2): Extracting archive
Created project in /Users/matt/Downloads/project-ACTAGIS-php8-202208/php-parallel-lint
Loading composer repositories with package information
Updating dependencies
Lock file operations: 4 installs, 0 updates, 0 removals
- Locking nette/tester (v2.4.2)
- Locking php-parallel-lint/php-console-color (v1.0.1)
- Locking php-parallel-lint/php-console-highlighter (v1.0.0)
- Locking squizlabs/php_codesniffer (3.7.1)
Writing lock file
Installing dependencies from lock file
Nothing to install, update or remove
2 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
Installons maintenant l’outil qui permet de coloriser le code dans notre console:
Voici une méthode simple pour exporter tous les utilisateurs d’une base de données WordPress, pour les réimporter sur un autre site, à l’aide de l’excellent wp-cli.
Nous ferons référence à la base de données source en tant que source-db et à la base de données cible en tant que target-db et je supposerai que vous avez accès aux deux instances WordPress via wp-cli.
Étape 1 : exporter les utilisateurs WordPress
Cette étape fonctionnera avec la base de données source-db:
wp db export --tables = $(wp db tables 'wp_*_users') users.sql
notez l’astérisque dans le nom de la table – utilisez-le pour éviter de taper le nom exact de la table pour votre base de données WordPress spécifique
notez le nom du fichier exporté, users.sql – cela permet d’identifier clairement le contenu de ce fichier.
Étape 2 : exporter la méta utilisateur WordPress
Cette étape fonctionnera avec la base source-db. Comme ci-dessus, notez le caractère générique et le nom de fichier:
wp db export --tables = $(wp db tables 'wp_*_usermeta') usermeta.sql
Étape 3 : sauvegarde facultative des utilisateurs et usermeta
Cette étape fonctionnera avec la base target-db. Il s’agit d’une sauvegarde facultative des utilisateurs et des tables usermeta avant d’importer les nouvelles données:
répétez l’étape 1 (mais en travaillant avec target-db)
nommez le fichier backup-users.sql
répétez l’étape 2 (mais en travaillant avec target-db)
nommez le fichier backup-usermeta.sql
Cela nous donne donc:
wp db export --tables = $(wp db tables 'wp_*_users') backup-users.sql
wp db export --tables = $(wp db tables 'wp_*_usermeta') backup-usermeta.sql
Cette année, j’ai eu la chance et le grand honneur d’être invité au CloudFest 2022 pour le hackathon: 3 jours de code sur un projet web en mode open-source pour faire avancer les choses.
Le Cloudfest
Le Cloudfest est une convention de développeurs et d’acteurs du web qui peuvent être extrêmement variés: cela va des hébergeurs web aux plateformes comme Codeable mais aussi avec de gros acteurs comme Airbus, Intel, Automattic, HP, Cpanel, Plesk…
Les conférences sont très variées: intelligence artificielle, business… et il est très facile de rencontrer des gens très connus sur le web pour se faire un réseau.
Le Cloudfest se tient à Rust, en Allemagne, à l’Europa Park.
Le Hackathon
Cette année le hackathon proposait plusieurs projets mais j’ai opté pour travailler sur wp-cli pour ajouter une nouvelle commande qui permettre de sécuriser 80% des attaques contre les instances WordPress, en appliquant simplement les meilleures pratiques de sécurité courantes.
Concrètement, nous avons identifié les problèmes de sécurité courants et nous avons développé une extension de l’interface de ligne de commande WordPress (wp-cli) pour offrir une alternative sécurisée et facile à utiliser aux plugins de sécurité WordPress généralement non sécurisés.
Avec la simple commande wp secure all, les meilleures pratiques courantes sont appliquées automatiquement à votre instance WordPress, et en moins de 60 secondes, vous attenuez la grande majorité des vecteurs d’attaque WordPress actuels : permissions de fichiers et de dossiers, entêtes de sécurité, bloquer l’accès aux fichiers sensibles…
WordPress publie par défaut une page d’attachement pour chaque fichier média que vous publiez sur votre site.
En règle générale, un fichier média (image, vidéo, autre) appartient à un article: on attache souvent ce type de fichier lors de la rédaction des articles, pour les insérer dans le corps des billets.
Le problème est donc que WordPress crée gentiment une page dédiée à chaque média publié. Cette page ne contient aucun contenu, à part afficher le média en question, ce qui n’est pas vraiment idéal au point de vue SEO puisque l’on se retrouve avec énormément de pages mais qui ne seront jamais indexées au vu du contenu inexistant. S’il est donc superflu d’avoir ces pages, autant s’en séparer!
C’est là que nous allons tenter d’être malin : pourquoi ne pas rediriger ces pages d’attachement vers l’article auquel ces fichiers media appartiennent?
Jusqu’à très récemment, j’utilisais le module Origin de The SEO Framework, qui fonctionne très bien si le média a été uploadé sur un article. La page d’attachement est alors automatiquement redirigée vers l’article.
Le hic, c’est que si le fichier média a été uploadé directement depuis la page WP Admin > Média, alors nous sommes redirigé vers /wp-admin, ce qui ne fait aucun sens.
Voici donc la solution que j’utilise sur le site:
<?php
/*
Script Name: Redirect attachment page to parent post.
Script URI: https://www.skyminds.net/?p=32314
Description: Redirects attachment to parent post (if it exists), or redirects to the homepage otherwise.
Version: 2.6.0
Author: Matt Biscay
Author URI: https://mattbiscay.com
*/
add_action( 'template_redirect', 'sky_redirect_attachment_to_post' );
function sky_redirect_attachment_to_post(){
// if not an attchment, bail out early
if( !is_attachment() ) {
return;
}
// check if parent post is defined
if( isset( $post ) && isset( $post->post_parent ) && is_numeric( $post->post_parent ) && ( $post->post_parent != 0 ) ) :
// redirect to parent post
wp_redirect( esc_url( get_permalink( $post->post_parent ) ) ); exit;
else: // media has been uploaded through the Media page or is unattached to a specific post
// redirect to homepage
wp_redirect( esc_url( home_url( '/' ) ) ); exit;
// or redirect to the media itself
// wp_redirect( esc_url( wp_get_attachment_url() ) ); exit;
endif;
}
Ce code est à copier-coller dans le fichier functions.php de votre thème enfant. Vous pouvez également l’enregistrer en tant que plugin.
Une fois activé, si vous visionnez l’adresse d’une page d’attachement, vous devriez être redirigé sur l’article parent.
Notez que je vous ai mis 2 possibilités pour la redirection lorsque le media n’a pas été attaché à un article: une redirection vers la page d’accueil ou alors vers le fichier média directement.
A vous de choisir ce qui vous correspond le mieux :)
Lors de mon dernier projet WooCommerce, j’ai remarqué que la page panier de WooCommerce n’était pas vraiment réactive sous Safari (iPhone, iOS) : le tableau ne s’empile pas comme il le devrait et toutes les colonnes sont comprimées. Les dernières colonnes sont hors du viewport.
Safari sous iOS (iPhone) semble être le seul concerné – je n’ai pas réussi à reproduire ce comportement sur FireFox, Chrome ou Opera.
Le site en question utilise Astra, qui est vraiment bien éprouvé, ainsi qu’Elementor comme constructeur de page.
Voici comment rendre le tableau du panier WooCommerce réactif, en utilisant quelques lignes de CSS.
Forcer la réactivité du panier WooCommerce
J’ai opté pour une solution propre, en CSS, en ne ciblant que les iPhones puisqu’ils sont les seuls concernés (Safari + résolution d’écran).
Voici donc le code utilisé pour rendre le panier WooCommerce réactif:
Le Centre de Kriya Yoga France n’avait pas de site staging, ce site de développement et de test qui permet de tester, développer ou mettre à jour de nouvelles extensions, sans affecter le site principal.
Une des extensions a eu besoin d’être débugguée par ses concepteurs mais pour des raisons de confidentialité, il nous est apparu intéressant et plus sécurisé de donner accès à un site de développement, fraîche copie du site original, pour le débuggage.
Si vous avez besoin de créer un site staging pour votre site WordPress et que votre hébergeur ne le propose pas, voici comment faire.
Étape 1 : créer un sous-domaine au niveau DNS
Nous choisissons la solution la plus simple: servir le site STAGING depuis un sous-domaine. Il suffit de créer un nouvel enregistrement DNS sous la forme:
staging IN A xxx.xxx.xxx.xxx
où staging represente le sous-domaine et xxx.xxx.xxx.xxx représente l’adresse IPv4 du serveur.
Étape 2 : créer le server block sous NginX
Le domaine étant déjà actif, j’ai uniquement rajouté ce server block:
### STAGING ###
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name staging.kriyayoga.fr;
root /home/www/kriyayoga/staging/public_html;
set $root /home/www/kriyayoga/staging/public_html;
index index.php index.htm index.html;
error_log /var/log/nginx/kriyayoga_staging_error.log;
#SSL
ssl_certificate /etc/nginx/ssl/kriyayoga.fr/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/kriyayoga.fr/privkey.pem;
include snippets/mime-types.conf;
#Exclusions
include snippets/exclusions.conf;
#Security
include snippets/security.conf;
#Static Content
include snippets/static-files.conf;
#Fastcgi cache rules
include snippets/fastcgi-cache.conf;
include snippets/limits.conf;
include snippets/nginx-cloudflare.conf;
#Gzip
include snippets/gzip.conf;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
try_files $uri =404;
include snippets/fastcgi-params.conf;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
#Skip cache based on rules in snippets/fastcgi-cache.conf.
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
#Define memory zone for caching. Should match key_zone in fastcgi_cache_path above.
fastcgi_cache kriyayoga;
#Define caching time.
fastcgi_cache_valid 60m;
#increase timeouts
fastcgi_read_timeout 6000;
fastcgi_connect_timeout 6000;
fastcgi_send_timeout 6000;
proxy_read_timeout 6000;
proxy_connect_timeout 6000;
proxy_send_timeout 6000;
send_timeout 6000;
#these lines should be the ones to allow Cloudflare Flexible SSL
#to be used so the server does not need to decrypt SSL if you wish
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-NginX-Proxy true;
}
#Protect WooCommerce upload folder from being accessed directly.
#You may want to delete this config if you are using "Redirect Only" method for downloadable products.
#Place this config towards the end of "server" block in NginX configuration.
location ~* /wp-content/uploads/woocommerce_uploads/ {
if ( $upstream_http_x_accel_redirect = "" ) {
return 403;
}
internal;
}
}
Testez la nouvelle configuration:
nginx -t
Puis redémarrez NginX:
service nginx reload
Note: il est important de noter que je n’ai pas besoin de créer de certificat SSL puisque mes certificats sont wildcard par défaut. Si ce n’est pas le cas chez vous, pensez à en générer pour votre sous-domaine.
Il n’est pas rare de voir des sites WordPress infectés par des scripts shell, qui peuvent exploiter certaines failles du core WordPress, de plugins ou de thèmes.
Ces attaques de WordPress sont courantes et concernent les sites qui n’ont pas été protégés par un antivirus ou un plugin de sécurité comme iThemes Security.
Il peut donc arriver que certains malwares infestent votre site WordPress, de manière automatisée si certaines composantes (core, plugins, themes) ne sont pas mis à jour régulièrement.
La technique détaillée ci-dessous vous permet d’identifier et de supprimer ces fichiers dans vos dossiers WordPress.
Important: avant de commencer, faites une sauvegarde du site: fichiers et base de données.
Étape 1 : suppression des fichiers potentiellement infectés
Sur l’installation en question, ces fichiers n’appartiennent pas à WordPress ou sont infectés. Nous les supprimons à vue:
Local possède une version d’Adminer dans les options de chaque site qui est très utile puisqu’elle permet d’accéder rapidement à la base de données, ou d’importer une petite base de données.
Par contre, s’il s’agit d’importer une base qui fait plus de 80 Mo, mieux vaut se tourner vers un outil un peu plus robuste: le shell.
Accéder au shell depuis Local
Et cela tombe bien : Local possède son propre shell, accessible sur simple clic-droit sur le nom du site sur lequel vous souhaitez ouvrir une fenêtre de terminal:
Voici la marche à suivre : Démarrez votre site > sélectionnez le nom du site > clic-droit > Open Site Shell
Importer une base de données dans un site Local
Lorsque j’ai besoin de répliquer un site rapidement, je copie la base données dans le répertoire app du site Local en question.
Pour mon site de test (skyminds-2020 ), le chemin sur ma machine est /Users/matt/Local Sites/skyminds2020/app . C’est dans ce dossier que je place le fichier SQL à importer.
Ensuite, dans le shell Local, il suffit de faire un simple import SQL dans la base qui, par défaut, s’appelle local (et ce, pour tous vos sites Local, bien qu’elles soient toutes indépendantes).
L’utilisateur est root et le mot de passe root également, ce qui est plutôt pratique.
Le shell est par défaut sous app/public donc on remonte d’un cran dans l’arborescence pour pointer vers notre fichier (qui se trouve dans /app ).
Voilà ce que cela nous donne:
mysql -u root -proot local < ../db.sql
Résultat:
mysql: [Warning] Using a password on the command line interface can be insecure.
bash-3.2$
L’importation de la base de données SQL ne prend que quelques secondes, alors que cela se plante allégrement lorsque l’on utilise Adminer.
A utiliser sans modération, cela ne vaut vraiment pas le coup de s’embêter avec une interface graphique (ou à placer le fichier dans le répertoire d’adminer comme il l’indique sur la page d’importation).
C’est assez rare comme demande mais je me suis exécuté : comment faire pour retirer le lien qui permet de supprimer les extensions désactivées dans l’interface d’administration, même si l’on est administrateur ?
Et bien c’est assez simple, il suffit de filtrer le tableau des liens. Voici deux exemples simples pour mettre cela en place.
Retirer le lien de suppression pour toutes les extensions
Voici le code qui vous permet de retirer le lien “Supprimer” qui se trouve en dessous de chaque extension sur la page Extensions:
<?php
/*
Plugin Name: Disable Plugin Deletion
Plugin URI: https://www.skyminds.net/wordpress-retirer-option-supprimer-extensions/
Description: Disable all plugins' deletion links on the plugins page.
Version: 1.0
Author: Matt Biscay
Author URI: https://mattbiscay.com
*/
add_filter( 'plugin_action_links', 'sky_disable_plugin_deletion', 10, 4 );
function sky_disable_plugin_deletion( $actions, $plugin_file, $plugin_data, $context ) {
// Remove delete link for all installed plugins
unset( $actions['delete'] );
return $actions;
}
Retirer le lien de suppression pour des extensions spécifiques
Voici le code qui vous permet de retirer le lien “Supprimer” qui se trouve en dessous des extensions dont vous spécifiez le chemin (dossier et nom du fichier de l’extension à charger) sur la page Extensions:
<?php
/*
Plugin Name: Disable Plugin Deletion
Plugin URI: https://www.skyminds.net/wordpress-retirer-option-supprimer-extensions/
Description: Disable all plugins' deletion links on the plugins page.
Version: 1.0
Author: Matt Biscay
Author URI: https://mattbiscay.com
*/
add_filter( 'plugin_action_links', 'sky_disable_plugin_deletion_selected', 10, 4 );
function sky_disable_plugin_deletion_selected( $actions, $plugin_file, $plugin_data, $context ) {
// Remove delete link for specific plugins
if ( array_key_exists( 'delete', $actions ) && in_array( $plugin_file,
[
'akismet/akismet.php',
'redirection/redirection.php',
]
) ) {
unset( $actions['delete'] );
}
return $actions;
}
Dans ce dernier snippet, pensez à modifier le nom du répertoire et celui du plugin de manière à ce qu’ils coïncident avec les plugins qui ne doivent pas être supprimés.
A l’origine, c’était pour un de mes clients pour éviter que ses collaborateurs ne suppriment des plugins par inadvertance.
Je m’en sers également sous LocalWP pour éviter de supprimer un plugin en cours de développement – on ne sait jamais !