Table des matières :
- Cartographier l’API backoffice PrestaShop et ses points d’entrée (PS 8.1–9.1, PHP 8.1+)
- AuthN/AuthZ : réduire la portée des jetons, isoler le réseau, verrouiller CORS/CSRF
- Mettre un WAF devant PrestaShop : ModSecurity CRS, rate limiting et règles « anti-bruit »
- Journalisation côté application et reverse proxy : des événements exploitables, pas des fichiers illisibles
- Pipeline SIEM : normalisation (ECS), corrélation, détection et réponse
- Checklist de déploiement en production (et pièges fréquents)
Cartographier l’API backoffice PrestaShop et ses points d’entrée (PS 8.1–9.1, PHP 8.1+)
Sur un PrestaShop moderne (8.x et surtout 9.x, basé sur Symfony 6.4), « l’API backoffice » ne se limite pas au Webservice historique /api. En pratique, vous avez au moins trois familles d’endpoints qui manipulent des données sensibles : (1) les routes Symfony du backoffice (y compris les actions AJAX), (2) les contrôleurs legacy (encore très présents) appelés via index.php?controller=... avec paramètre ajax=1, et (3) les endpoints exposés par des modules (front controllers /module/<module>/<action> et parfois des contrôleurs admin). Ce dernier point est la source n°1 de surprises : le core applique des patterns, mais chaque module amène sa propre surface d’attaque.
Pour éviter de « sécuriser à l’aveugle », l’objectif de la cartographie est d’obtenir une liste exploitable : URL, méthode(s), authentification attendue, rôle requis, sensibilité des données, et propriétaire (core vs module). Un format simple (tableur ou YAML interne) suffit, tant qu’il est tenu à jour.
Voici un canevas minimal (utile aussi pour briefer un SOC ou un prestataire WAF) :
| Famille | Exemples d’URL | Auth attendue | Données typiques | Risques dominants | Contrôles prioritaires |
|---|---|---|---|---|---|
| Backoffice Symfony | /adminXXXX/... (JSON/AJAX inclus) |
Cookie session employé + CSRF | commandes, clients, config | brute force, vol de session, élévation de privilèges | allowlist IP/VPN, 2FA (si dispo), rate-limit login, logs d’admin actions |
| Legacy admin | /adminXXXX/index.php?controller=...&ajax=1 |
idem | actions « historique » (imports, grilles) | contournements modules, paramètres peu filtrés | WAF en observation + exclusions fines, tests de régression |
| Webservice | /api/... |
clé API (Basic) | clients/commandes/stocks | exfiltration, modification, DoS applicatif | restriction IP/VPN/mTLS, rotation clés, quotas et logs dédiés |
| Modules front | /module/x/y |
variable (souvent public…) | tracking, paiement, export | endpoint oublié, auth manquante | inventaire, tests d’accès anonymes, WAF + logs, correction module |
Comment lister concrètement les routes (PS 9 / Symfony)
Sur PrestaShop 9.x, commencer par les routes Symfony permet déjà de repérer une grande partie des points d’entrée admin :
# depuis la racine PrestaShop (PS 9.x)
php bin/console debug:router | grep -i admin
php bin/console debug:router --format=json > /tmp/routes.json
Ensuite, complétez par un inventaire « filesystem » côté modules, car de nombreux endpoints ne ressortent pas toujours proprement dans une simple liste de routes (front controllers, contrôleurs legacy, endpoints AJAX custom). Une approche pragmatique :
- lister tous les modules installés et leurs contrôleurs :
modules/*/controllers/front/*.phpmodules/*/controllers/admin/*.php- rechercher des patterns d’actions AJAX et de contrôleurs legacy :
grep -R "ajax" admin*/controllers -ngrep -R "FrontController" modules -n
Le Webservice /api reste critique car il sert souvent à des intégrations (ERP, WMS, PIM) avec des clés longues durées. Par défaut, l’authentification est un modèle « clé API » (généralement en HTTP Basic). Cela marche, mais ça n’implique ni rotation automatique, ni limitation de débit, ni scoping fin par IP/réseau dans le core. Si votre backoffice est derrière un reverse proxy et que vous laissez /api ouvert sur Internet, vous avez une porte d’entrée parfaite pour : exfiltration (clients/commandes), modification de stock/prix, et DoS applicatif par crawl agressif. (Pour recadrer le fonctionnement et les limites, la doc officielle reste un bon point d’ancrage : Documentation Webservice PrestaShop)
Enfin, PrestaShop 9 introduit une trajectoire plus « API-first » côté admin (cf. PrestaShop 9 : nouveautés techniques Symfony 6.4, API et performances). Ce mouvement augmente mécaniquement la valeur des endpoints backoffice : plus de requêtes JSON, plus d’automatisation, plus de scripts internes. La conséquence sécurité est simple : vous devez traiter le backoffice comme un produit exposé, avec un modèle de menace (STRIDE/OWASP), des contrôles réseau, un WAF, et une journalisation consommable par un SIEM.
AuthN/AuthZ : réduire la portée des jetons, isoler le réseau, verrouiller CORS/CSRF
Le durcissement le plus rentable n’est pas « un plugin magique », c’est de réduire l’exposition. Pour le backoffice, l’objectif est qu’un acteur Internet « standard » ne puisse même pas atteindre l’URL admin. À minima : renommer le dossier admin (mesure d’hygiène, pas une barrière) et surtout mettre une allowlist IP au niveau reverse proxy (Nginx/Apache/Ingress Kubernetes) pour /adminXXXX/ et pour les endpoints admin des modules.
Une nuance opérationnelle : si vous avez des équipes nomades ou une agence, l’allowlist IP doit être pensée avec un chemin d’accès maîtrisé (VPN, bastion, ZTNA), sinon elle finit désactivée « temporairement » et le risque revient.
Exemple Nginx (à adapter) :
# PS 9.1 / PHP-FPM 8.2+ derrière Nginx
location ^~ /admin123/ {
allow 203.0.113.10/32; # VPN / bastion
allow 198.51.100.0/24; # réseau bureau
deny all;
# Optionnel : Basic Auth supplémentaire au niveau proxy
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd_admin;
}
Le Webservice /api et les endpoints « machine-to-machine » doivent suivre le même principe : pas d’accès public si vous pouvez l’éviter. Une intégration ERP/Odoo, par exemple, n’a aucune raison de venir d’IP aléatoires : vous filtrez par IP (ou mieux, par réseau privé via VPN), vous exigez TLS strict, et idéalement vous ajoutez une authentification forte au niveau proxy (mTLS). Sur les projets où l’intégration est bi-directionnelle, ce cloisonnement devient non négociable (voir Intégration e-commerce Odoo : architecture bi-directionnelle et synchronisation stocks).
Côté application, il faut accepter une limite du core : PrestaShop ne fournit pas un modèle standard d’API « admin » avec OAuth2 scopes, rotation, introspection, et rate-limit applicatif. Donc, si vous exposez des endpoints custom (modules), vous devez compenser :
- AuthZ systématique : vérifier le contexte employé (Employee) et les permissions (rôles) dans le contrôleur, et ne jamais se contenter d’un « endpoint non documenté ».
- CSRF sur les actions stateful : toute action qui modifie des données (création, suppression, modification) doit exiger un jeton, même si « c’est juste un AJAX du backoffice ».
- CORS restrictif : ne jamais laisser
Access-Control-Allow-Origin: *sur un endpoint qui lit des données sensibles ou qui accepte des credentials. Dans la plupart des cas backoffice, CORS n’a même pas vocation à être activé. - Cookies durcis : vérifier
Secure,HttpOnly,SameSite. Un réglage trop permissif (ou incohérent avec votre reverse proxy) peut transformer un incident XSS en compromission de session.
Un oubli fréquent : derrière un load balancer, une mauvaise configuration des trusted proxies peut fausser l’IP « réelle » et casser vos contrôles (rate-limit, allowlist, détection brute force). Résultat typique : tout le trafic semble venir de l’IP du proxy, ou au contraire un attaquant peut injecter un header X-Forwarded-For et contourner une politique basée sur l’IP. Dans Symfony, cela se traite via la configuration des proxys de confiance et des headers acceptés — à valider en test, notamment si vous empilez CDN + reverse proxy + Ingress.
Mini-scenario (réaliste) : un module « export commandes » expose /module/export/orders et suppose que « comme c’est un module, c’est pour l’admin ». En production, l’URL est accessible sans cookie employé (contrôleur front public), et retourne un CSV. Sans cartographie + test d’accès anonyme + logs, ce type d’erreur reste invisible.
Mettre un WAF devant PrestaShop : ModSecurity CRS, rate limiting et règles « anti-bruit »
Un WAF ne remplace ni des patchs ni des revues de code, mais il amortit les attaques opportunistes (bots, scans CVE, injections triviales) et vous donne du temps. L’option la plus portable en auto-hébergement reste ModSecurity + OWASP CRS, côté Nginx/Apache. Le projet OWASP Core Rule Set (CRS) documente clairement son objectif : fournir un jeu de règles génériques de détection d’attaques (SQLi, XSS, traversal…) pour ModSecurity et WAF compatibles (documentation : OWASP CRS).
Dans PrestaShop, la difficulté n’est pas d’activer le WAF, c’est de gérer les faux positifs. Les backoffices génèrent beaucoup de payloads (HTML/JSON), certains modules envoient des champs riches (WYSIWYG), et les imports peuvent contenir des séquences « suspectes » au regard du CRS. La bonne approche est :
- activer le CRS en mode détection (log only),
- observer 7–14 jours (cycles réels : imports, campagnes, backoffice en journée),
- exclure/whitelister au cas par cas des paramètres précis (ex. un champ
description), jamais des routes entières, - documenter chaque exception : route + paramètre + règle CRS + justification + date.
Un levier utile pour limiter le bruit est de jouer sur la paranoia level (niveau d’agressivité) du CRS : démarrer plus bas, puis augmenter progressivement une fois les faux positifs maîtrisés, au lieu de basculer « block » brutalement.
Au-delà des signatures, le levier le plus efficace sur /admin et /api est le rate limiting au niveau reverse proxy. Nginx documente le module limit_req et son usage pour limiter la cadence de traitement des requêtes selon une clé (souvent l’IP) : limit_req documentation. Exemple concret :
# zone de limitation par IP
limit_req_zone $binary_remote_addr zone=ps_admin:10m rate=2r/s;
limit_req_zone $binary_remote_addr zone=ps_api:10m rate=5r/s;
location ^~ /admin123/ {
limit_req zone=ps_admin burst=10 nodelay;
# ... (allowlist, auth_basic, etc.)
}
location ^~ /api/ {
limit_req zone=ps_api burst=20;
}
Le « burst » est ce qui évite de casser le backoffice lors de rafales légitimes (chargement de grilles, autocomplétions). Pour dimensionner sans casser la prod, un test simple consiste à :
- ouvrir le backoffice,
- réaliser des actions « bruyantes » (recherche produits, pagination commandes, autocomplétion),
- observer les
429(Too Many Requests) côté Nginx et ajusterrate/burstjusqu’à disparition des erreurs en usage normal.
Sur /admin/login, vous pouvez descendre plus agressivement et coupler avec fail2ban (sur logs Nginx) pour bannir temporairement les IP qui enchaînent des 401/403. Et oui : si vous êtes derrière Cloudflare/Akamai/AWS WAF, le même principe s’applique (règles de débit + règles gérées). Le point non négociable : ne faites pas confiance aux headers d’IP client tant que votre proxy de confiance n’est pas verrouillé.
Journalisation côté application et reverse proxy : des événements exploitables, pas des fichiers illisibles
Sans journalisation propre, « WAF + durcissement » se transforme en boîte noire : vous bloquez des choses, mais vous ne savez pas quoi, qui, comment, ni si l’attaque a réussi ailleurs. Le core PrestaShop a des logs, mais ils ne sont pas conçus comme un audit trail SIEM-ready (structure variable, corrélation difficile, PII parfois présente). Sur PrestaShop 9.x (Symfony), vous avez l’avantage de Monolog : c’est le point d’entrée naturel pour produire des événements structurés.
Concrètement, l’objectif est de sortir des logs JSON avec :
- un
request_idstable (corrélation proxy ↔ PHP ↔ éventuellement DB), - des champs minimaux (URI, méthode, user agent, IP client réelle, identifiant employé quand disponible),
- une séparation claire des canaux :
security,auth,webservice,module, - et des niveaux cohérents (info/notice/warning/error) pour éviter « tout en error ».
Exemple (simplifié) d’une configuration Monolog orientée prod :
# config/packages/prod/monolog.yaml (PS 9.1)
monolog:
handlers:
main:
type: stream
path: "php://stderr"
level: info
formatter: monolog.formatter.json
security:
type: stream
path: "php://stderr"
level: notice
channels: ["security", "auth"]
formatter: monolog.formatter.json
Côté reverse proxy, ajoutez un access log JSON qui inclut le même identifiant. Exemple Nginx (format minimal, à enrichir selon votre SIEM) :
log_format siem_json escape=json
'{'
'"@timestamp":"$time_iso8601",'
'"request_id":"$request_id",'
'"remote_addr":"$remote_addr",'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":$status,'
'"bytes":$body_bytes_sent,'
'"referrer":"$http_referer",'
'"user_agent":"$http_user_agent"'
'}';
access_log /var/log/nginx/access_siem.json siem_json;
Ensuite, vous injectez le request_id côté reverse proxy ($request_id) et vous le propagez à PHP via un header (ex. X-Request-Id). Côté Symfony, un subscriber kernel.request copie ce header dans le contexte du logger. Cette corrélation est ce qui rend les analyses SIEM praticables : un blocage WAF, un pic de 500, une requête anormale, et un changement de configuration admin doivent pouvoir se relier. Pour la partie PHP, évitez display_errors en production et centralisez les erreurs dans un flux maîtrisé (voir Gestion d’erreur PHP : bonnes pratiques et configuration développement/production).
Dernier point : loguer oui, mais pas n’importe quoi. En backoffice, vous manipulez PII (clients) et parfois des secrets (tokens, clés). Les règles simples :
- ne jamais logger un mot de passe (y compris champs « nouveau mot de passe »),
- masquer les tokens et clés (hash partiel + dernier(s) caractères),
- éviter de logger des payloads complets sur
/apisi ça contient des données clients, - définir une rétention, un contrôle d’accès et une purge automatisée.
La sécurité ne se fait pas en empilant des fichiers prod.log de 200 Go sur le disque système ; elle se fait avec des événements exploitables, minimisés, et accessibles aux bonnes personnes.
Pipeline SIEM : normalisation (ECS), corrélation, détection et réponse
Un SIEM n’est utile que si vos données sont normalisées et si vous avez des détections actionnables. Le pattern robuste en e-commerce : logs Nginx (JSON) + logs applicatifs Symfony/Monolog (JSON) + événements WAF → collecteur (Filebeat/Vector/Fluent Bit) → stockage (Elastic/Splunk/Graylog/Wazuh) → règles de corrélation + alerting. Si vous utilisez Elastic, l’alignement sur ECS (Elastic Common Schema) simplifie fortement les dashboards et requêtes transverses (mêmes champs pour IP, user agent, URL, code réponse).
À minima, votre schéma SIEM doit contenir : @timestamp, event.dataset (nginx, prestashop, modsecurity), http.request.method, url.path, client.ip, user_agent.original, http.response.status_code, et un champ d’identité (ex. user.id pour l’employé backoffice, api.key.id pour une clé webservice). Sans ça, vos requêtes de détection deviennent du « grep distribué » ingérable. Pour une boutique à trafic modéré, une volumétrie réaliste est de l’ordre de 1–5 Go/jour de logs JSON combinés si vous logguez proprement (beaucoup plus si vous logguez les bodies — ce qu’il faut éviter).
Deux exemples de détections simples (et très rentables) une fois la normalisation en place :
- Spike d’erreurs backoffice corrélé WAF : hausse de
http.response.status_code:500sur/admin+ événements ModSecurity sur les mêmesclient.ipdans une fenêtre de 5 minutes. - Comportement « bot » sur API : ratio anormal de 401/403 sur
/api+ user agent identique + forte cadence → blocage temporaire + rotation de clé si suspicion de fuite.
Côté règles, visez des scénarios e-commerce concrets, pas des alertes « CPU > 80% ». Exemples qui déclenchent réellement des incidents :
- Brute force backoffice : >20 tentatives sur
/admin*/loginen 5 minutes depuis la même IP, ou distribution sur /24 avec user agents identiques. - Énumération webservice : burst de 401/403 sur
/api+ variations rapides de clés → rotation forcée + blocage WAF. - Exploitation module : pic de 404/500 sur
/module/<x>/<y>+ règles WAF SQLi/XSS qui matchent → isoler le module, mettre en maintenance ciblée. - Changement de configuration : création d’un nouvel employé admin, modification des permissions, changement des endpoints webservice → alerte haute priorité.
La réponse (playbooks) doit être écrite avant l’incident : qui révoque une clé ? où est stockée la liste des IP autorisées ? comment on désactive un module sans casser le front ? comment on bascule en mode maintenance ? Sur PrestaShop, la réponse doit intégrer vos contraintes de performance et de cache (Varnish/ESI, CDN). Si votre incident response déclenche un purge-all Varnish en pleine attaque, vous vous auto-infligez un DoS (voir Varnish Cache : configuration ESI pour optimiser le cache par fragments).
Checklist de déploiement en production (et pièges fréquents)
Pré-requis réalistes pour appliquer ce qui précède : accès root/DevOps sur le reverse proxy (Nginx/Apache/Ingress), capacité à déployer un WAF (ou un WAF managé), et un pipeline de logs centralisé. Côté PrestaShop, les exemples ci-dessus visent PS 9.1 (Symfony 6.4) avec PHP 8.2+ ; adaptez si vous êtes en 1.7/8 avec une part legacy plus lourde (et vérifiez la compatibilité plateforme dans PrestaShop 9.1 : compatibilité PHP 8.1–8.5, CLI et nouveautés développeurs).
Checklist opérationnelle (à exécuter idéalement sur un environnement de staging cloné prod) :
- Réseau : allowlist IP sur
/admin*et endpoints admin des modules ;/apilimité à des IP d’intégration. - TLS : HSTS côté proxy, redirection HTTPS, désactivation des suites faibles ; si possible mTLS pour
/api. - Rate limit :
limit_reqsur/admin/login,/admin*/et/apiavec des bursts calibrés. - WAF : ModSecurity + OWASP CRS en mode détection, puis activation progressive ; exclusions paramétriques documentées.
- Auth : rotation des clés webservice, droits minimaux par ressource ; suppression des clés orphelines.
- Logs : Nginx access log JSON + Monolog JSON + logs WAF,
request_idpropagé ; rétention et purge. - SIEM : règles de brute force, 4xx/5xx anormaux, changements de permissions, activité
/module/*suspecte.
Deux contrôles « qualité » à ajouter juste avant la mise en prod (souvent oubliés, mais rapides) :
- Test d’accès anonyme : depuis un poste hors VPN, vérifier que
/adminXXXX/est inaccessible (403/401 côté proxy) et que/apine répond pas (ou uniquement depuis IP autorisées). - Test de corrélation : exécuter une action simple (connexion backoffice) et vérifier dans le SIEM que le
request_idapparaît dans le log Nginx et le log applicatif, avec la même IP client.
Pièges fréquents : (1) mettre un WAF « en blocage » dès J1 → faux positifs → backoffice inutilisable, (2) activer un rate limit sans burst → Ajax admin cassé → tickets internes, (3) loguer des bodies API « pour debug » → fuite de PII dans la stack SIEM, (4) oublier la configuration des proxys de confiance → IP spoofable → détections et bans incohérents. Si vous voulez une vue plus globale des risques et des priorités 2026 (au-delà de l’API backoffice), recoupez avec Sécurité PrestaShop 2026 : risques majeurs et protections professionnelles.




