# Procédure — Piège à bots `wp-admin.php` (v2)

Date : 2026-08-16
Périmètre : mutualisé distant (re-info.fr). Les bots n'ont accès qu'au mutualisé.
Fichier concerné : `/home/alan/SITE/WEB/wp-admin.php` (racine web sur le mutualisé).

> Statut : **architecture validée, code NON rédigé**. Aucune modification appliquée.

---

## 1. Règles absolues (le piège ne doit JAMAIS être une faille)

- `ini_set('display_errors','0'); error_reporting(0);` en tête de fichier.
- **Zéro fonction dangereuse** : pas de `eval`, `exec`, `system`, `shell_exec`,
  `popen`, `proc_open`, `unlink`, `rename`, `include`/`require` construit
  depuis une variable. Contenu 100 % statique dans le fichier.
- Chemin de log **100 % fixe** : aucune donnée utilisateur dans un chemin de
  fichier (anti path traversal). Seuls noms variables : `trapped_bots_YYYY-MM-DD.log`.
- **Aucun input utilisateur réfléchi dans un header HTTP** (anti header-injection).
  Cookies maison à valeurs aléatoires uniquement : `bin2hex(random_bytes(16))`.
- Toute donnée du bot ré-affichée dans les fausses pages : **`htmlspecialchars()`**
  obligatoire (anti XSS même dans les leurres).
- Caps sur tout ce qui est loggé : corps brut `php://input` limité à **16 Ko**
  (lecture fractionnée puis abandon), headers ≤ 2000 chars, UA ≤ 512 chars.
  `json_encode(..., JSON_INVALID_UTF8_SUBSTITUTE | JSON_UNESCAPED_SLASHES)`
  pour garantir des lignes JSON valides même avec binaire.
- **Pas de session PHP native** (pas de fichiers de session sur le mutualisé).
  Suivi du bot : cookie maison `bt_<hash>` (compteur de passages + score) lu/écrit
  manuellement + comptage par IP depuis le log.
- Réseau optionnel et **non bloquant** : géoloc via `file_get_contents` +
  `stream_context` avec **timeout 2 s max**, échec silencieux. Pas de `curl_*`
  (non garanti sur le mutualisé).
- **Anti-DoS du mutualisé** : sommeil total du tarpit ≤ 3 s par requête, en
  tranches de 500 ms avec `flush()`. Padding servi par morceaux (jamais plus de
  2 Mo en mémoire d'un coup), gzip si `zlib` dispo sinon brut.
- Rotation bornée : log par jour + purge auto des fichiers de log de plus de
  **30 jours** (le quota disque mutualisé ne sature jamais).
- `random_bytes()` avec repli sur `mt_rand()` si indisponible.
- Échec d'écriture des logs **silencieux** : on répond le leurre quand même.
- **Aucun vrai secret dans le fichier** : tout est évidemment faux (pas de clé
  API réelle, pas de BDD, pas de compte).

---

## 2. Fonctionnalités du piège (un seul fichier, auto-contenu)

### a) Fausse page de login WordPress
- HTML complet ressemblant à wp-login réel, CSS inline.
- Message d'erreur « identifiants incorrects » après chaque POST.
- Variante journalière : couleur / version affichée (6.4 / 6.7) / plugins listés
  dépendent de `hash('sha256', $ip . date('Y-m-d'))` → signature change chaque
  jour, impossible à blacklister.

### b) Fausse admin
- Après un POST login (tout est « accepté ») : pose d'un cookie et affichage
  d'un tableau de bord fake (plugins RevSlider/Elementor, users, compteurs).
- Chaque action suivante (upload, install, édition de thème) → succès feint + log.

### c) Fausses API
- `/wp-json/wp/v2/users` → 5 users fake (wpscan s'y précipite).
- `admin-ajax.php` → réponses JSON par action (`?page=ajax&action=...`).
- `xmlrpc.php` → réponse `system.multicall` valide → les botnets de brute force
  y envoient leurs listes de mots de passe → collectées.

### d) Chaîne de redirections tarpit
- 302 successifs `?s=1` → `?s=2` … **max 6** (borné, jamais de boucle infinie).
- Chaque saut pose un cookie et est loggé.

### e) Padding & ralentissement progressif
- Réponse de 1–2 Mo aux bots.
- Récidivistes (cookie + log IP/24h) : 1er passage normal, 2e : délai 1 s,
  3e et + : délai 3 s + padding plus gros.

### f) Protection des humains
- Présence de `Sec-Fetch-*`, `Accept-Language`, timing du handshake →
  considéré comme navigateur : réponse immédiate et petite, aucune redirection.

---

## 3. Collecte d'informations (même log JSONL, champs existants conservés)

- `credentials` : login/password tentés (chaque POST login/xmlrpc)
- `raw_body` : corps brut tronqué 16 Ko (payloads sqlmap/wpscan)
- `cookies` : cookies reçus (dont ceux de nos pièges précédents → suivi multi-requêtes)
- `sec_fetch` : en-têtes Sec-Fetch (classement bot vs navigateur)
- `timing_ms` : durée du handshake (les scanners sont rapides)
- `score` : 0–100 (UA vide/anonyme +15, pas d'Accept-Language +15, pas de
  Sec-Fetch +20, vitesse +10, POST login +10, IP récidiviste +20…) →
  détermine l'intensité du tarpit
- `geo` : pays/ISP/ASN/VPN via ip-api.com (1 appel max, échec silencieux)
- `rdns` : reverse DNS (optionnel, échec silencieux)

Format JSONL **identique à l'actuel** → le viewer VPS et la copie périodique
continuent de fonctionner sans changement.

---

## 4. Ce qu'il ne faut PAS faire (volontairement exclu)

- Aucun vrai stockage de mots de passe (log d'analyse uniquement).
- Aucune réponse qui exécute du code ou inclut du contenu téléchargé.
- Aucun sleep long / gros bloc mémoire (ne jamais servir d'arme DoS contre le
  mutualisé lui-même).
- Aucun second fichier accessible par les bots : **tout reste dans wp-admin.php**.
- Pas de dépendance à un fichier partagé (auto-contenu).

---

## 5. Déploiement prévu (à faire après rédaction + validation)

1. Rédiger `wp-admin.php` complet dans `/tmp/opencode/wp-admin.php` (relecture
   ligne par ligne — pas de `php -l` possible sur le VPS, pas de PHP local).
2. Faire valider le code (ou lancer `php -l` sur le mutualisé si dispo).
3. Remplacer le fichier sur le mutualisé (FTP `put` **sans `-E`**, jamais de
   `mirror --delete`).
4. Étendre le `.htaccess` du mutualisé pour rediriger les URLs leurres
   supplémentaires vers `wp-admin.php` (proposition en annexe).

---

## 6. Annexe — Règles .htaccess proposées (à déployer avec le fichier)

```apache
# Rediriger TOUS les scans WordPress vers le piège (bloc existant étendu)
RewriteRule ^wp-login\.php$ /wp-admin.php [R=302,L]
RewriteRule ^wp-admin/?$ /wp-admin.php [R=302,L]
RewriteRule ^xmlrpc\.php$ /wp-admin.php [R=302,L]
RewriteRule ^wp-content/plugins/revslider/ /wp-admin.php [R=302,L]

# Nouveaux leurres
RewriteRule ^wp-(login|admin|content|includes|cron|json|config) /wp-admin.php [R=302,L]
RewriteRule ^wp-content/ /wp-admin.php [R=302,L]
RewriteRule ^wp-includes/ /wp-admin.php [R=302,L]
RewriteRule ^(phpmyadmin|adminer|pma|backup\.zip|db\.sql|config\.php(\.bak|~|\.old|\.save)?|\.git|\.env|\.htpasswd) /wp-admin.php [R=302,L]
```

**Attention** : les règles `wp-content/` et `wp-includes/` sont plus larges —
vérifier qu'aucun chemin réel du site ne commence par ces préfixes avant déploiement.
