# wp_after_insert_post et les extensions de sécurité qui valident trop tard

> Ce hook s'exécute une fois le contenu déjà enregistré. Comprendre pourquoi cela change tout pour une extension censée bloquer une injection.

- Auteur : Clément Hadrot
- Publié le : 2022-07-24
- Mis à jour le : 2022-07-24
- Catégorie : Sécurité
- URL : https://wpmoderne.dev.wordpress-developpement.fr/securite/wp-after-insert-post-validation-trop-tardive/

## L’essentiel

- Le hook se déclenche après l'écriture en base, pas avant
- Bloquer ici n'empêche pas la persistance initiale
- Le filtre content_save_pre reste le bon niveau pour intercepter

`do_action( 'wp_after_insert_post', $post_id, $post, $update, $post_before )` : cette ligne, présente dans `wp-includes/post.php`, définit exactement le moment où ce hook intervient. Un développeur d'extension qui cherche à y accrocher un contrôle de sécurité doit d'abord comprendre ce que « après » signifie concrètement pour l'état de la base de données.

Introduit en WordPress 5.6, `wp_after_insert_post` a été conçu pour résoudre un problème précis : donner un point d'ancrage fiable une fois qu'un article, ses métadonnées et ses taxonomies sont tous enregistrés de façon cohérente. C'est une excellente nouvelle pour synchroniser un moteur de recherche externe ou déclencher un webhook. C'est un piège pour qui espère y bloquer un contenu indésirable.

## Ce que « après » implique réellement

Au moment où `wp_after_insert_post` se déclenche, la ligne existe déjà dans la table `wp_posts`, ses métadonnées sont déjà dans `wp_postmeta`, et ses termes sont déjà associés dans `wp_term_relationships`. Retourner un `WP_Error` depuis un callback accroché à ce hook ne fait rien : l'action ne transmet son retour à personne, et rien n'empêche l'écriture déjà effectuée d'exister.

Une extension de sécurité qui tenterait de « refuser » un contenu à ce stade n'a qu'une option : supprimer ou modifier après coup, avec `wp_delete_post()` ou `wp_update_post()`. Le contenu malveillant aura donc, ne serait-ce que quelques millisecondes, existé en base, potentiellement été indexé par un cache d'objets, ou transmis à un webhook accroché plus tôt dans la même requête.

## Une fenêtre d'exposition, même courte, compte

Sur un site à fort trafic ou exposant une API REST publique, cette fenêtre n'est pas seulement théorique. Un contenu injecté via `wp_insert_post()` peut être lu par une requête REST concurrente avant même que le nettoyage tardif n'ait eu lieu. Pire, si une extension tierce a elle-même accroché un traitement sur `save_post` ou `wp_insert_post` à une priorité plus faible, elle aura pu propager la donnée non filtrée avant que le contrôle de sécurité intervienne.

- `wp_after_insert_post` s'exécute après `save_post`, lui-même déjà postérieur à l'écriture.
- Aucune donnée renvoyée par un callback accroché ici ne remonte au code appelant.
- Une suppression a posteriori laisse une trace dans les journaux de révisions et le cache d'objets.

> L'essentiel à retenir : Le hook se déclenche après l'écriture en base, pas avant ; Bloquer ici n'empêche pas la persistance initiale ; Le filtre content_save_pre reste le bon niveau pour intercepter

## Où placer le contrôle pour qu'il ait un effet réel

Le filtre `content_save_pre` s'exécute avant l'écriture, directement sur le contenu qui sera inséré. C'est là qu'un filtrage ou un rejet a un sens, puisqu'il peut modifier la valeur avant qu'elle n'atteigne la requête SQL d'insertion.

```
add_filter( 'content_save_pre', function ( $content ) {
    if ( preg_match( '/<script\b/i', $content ) ) {
        // On assainit avant l'écriture, pas après.
        $content = wp_kses_post( $content );
    }
    return $content;
} );
```

Pour un rejet complet plutôt qu'un simple nettoyage, la fonction `wp_insert_post_data` offre un filtre plus adapté encore, car il reçoit à la fois les données à écrire et les données brutes soumises, avant tout appel SQL.

### Distinguer validation et réaction

Il existe malgré tout un usage légitime de `wp_after_insert_post` pour un module de sécurité : la détection et l'alerte, pas le blocage. Un scanner qui journalise une tentative suspecte, avertit un administrateur ou déclenche une mise en quarantaine asynchrone trouve ici un point d'ancrage cohérent, à condition de ne jamais prétendre avoir empêché l'écriture initiale.

## Un exemple de confusion fréquente

Un module analysé lors d'une revue de code appelait `remove_action` sur lui-même après un rejet, pensant ainsi « annuler » l'insertion. En réalité, l'article restait présent dans `wp_posts` avec le statut défini au moment de l'appel initial à `wp_insert_post()`, souvent `publish`. La seule chose annulée était l'exécution future du même callback sur la même requête, ce qui n'a strictement aucun rapport avec la présence du contenu en base.

> Retenue d'expérience : un hook nommé « after » l'est vraiment, dans son sens le plus littéral. Il ne s'agit jamais d'un point d'interception, seulement d'un point d'observation ou de réaction.

## Notion à retenir

La chronologie des hooks autour de l'écriture d'un contenu WordPress suit un ordre strict : les filtres sur le contenu brut, puis l'insertion SQL, puis `save_post`, puis enfin `wp_after_insert_post`. Un développeur d'extension de sécurité doit choisir son point d'ancrage en fonction de l'effet recherché : filtrer et bloquer en amont, observer et réagir en aval. Confondre les deux ne provoque pas d'erreur visible immédiate, ce qui rend cette classe de défaut particulièrement difficile à repérer sans lecture attentive du code source de `wp-includes/post.php`.
