Aller au contenu

Centre de confidentialité

Extension payante. Conservation, recherche, export, effacement et journal inaltérable, pour les données que vos formulaires détiennent.

Ce document décrit ce que le module garantit et ce qu’il refuse. Le guide utilisateur en donne la version courte, dans la section « Répondre à une demande de confidentialité ».

Ce qu’il ne prétend pas

Il ne rend pas votre site conforme, et il le dit sur son propre écran.

Il ne sait rien de vos autres extensions, ne donne aucun conseil juridique, et ne supprime pas de compte WordPress. Le dire est utile : un « centre de confidentialité » qui laisserait croire qu’il couvre tout ferait manquer ce qu’on croit fait.

L’adresse est comparée exactement

Un LIKE %camille@example.test% paraît plus serviable et fait deux dégâts opposés. Il attrape camille@example.test.attaquant.example, dont le titulaire n’a rien demandé — on exporterait ses données à quelqu’un d’autre. Et il attrape pas-camille@example.test, qui est une autre personne.

Sur un export, se tromper de personne est une divulgation. Sur un effacement, c’est une perte. Les deux sont irréversibles, et aucune ne se voit au moment où elle se produit.

La casse est normalisée : personne ne considère que Camille@Example.test est quelqu’un d’autre, et exiger la casse exacte ferait échouer des demandes légitimes.

Un terme qui n’est pas une adresse ne cherche rien. Sans ce refus, « camille » — ou pire, un espace — balaierait toutes les valeurs du site.

La portée est dans la requête, pas dans l’affichage

Un opérateur qui ne gère que ses formulaires ne doit pas pouvoir apprendre, par un décompte ou un identifiant, qu’une personne a répondu ailleurs. La portée est calculée une fois, à l’entrée, et passée partout ; aucun appel ne la recalcule et aucun ne l’élargit.

Une portée vide ne rend rien. Elle ne rend surtout pas tout — c’est l’erreur classique du IN () construit à la main, et elle ouvrirait le site entier à qui n’a aucun formulaire.

Chaque module déclare ce qu’il détient

Le centre ne balaie pas les tables. Il le pourrait — elles sont toutes préfixées slf_ — mais il faudrait qu’il connaisse la colonne portant l’adresse dans chacune : vingt fois le même savoir au mauvais endroit. Un module ajouté demain serait oublié, et l’oubli ne se verrait pas. Un export incomplet a l’air complet.

Chaque module se déclare donc, par solis_forms_pro_personal_data_providers. Celui qui ne le fait pas n’apparaît ni à l’export ni à l’effacement — et c’est constatable, puisque le registre est listé à l’écran.

L’effacement par soumission existait déjà : delete() purge les pièces jointes et diffuse solis_forms_entry_deleting, auquel tous les modules sont abonnés. Le centre s’y branche plutôt que de le refaire. Ce qu’il ajoute est l’effacement par personne — ce qu’un module détient sans soumission attachée : une opposition SMS, un contact de CRM, une ligne de portail.

« Effacé » et « anonymisé » ne disent pas la même chose

Les confondre coûterait cher le jour où quelqu’un demande la preuve de ce qui a été fait. Une écriture comptable anonymisée reste dans les livres ; une soumission effacée n’y est plus.

Un module qui doit garder une trace rend ce qu’il a anonymisé, et dit ce qu’il conserve et pourquoi. Le centre ne tranche pas à sa place : il rapporte.

Un échec partiel ne défait pas le reste

On pourrait vouloir tout annuler pour garder un état cohérent. Ce serait une erreur ici. Une personne a demandé l’effacement de ses données ; qu’un service distant soit injoignable n’est pas une raison de garder les dix autres copies.

Chaque module est appelé, son résultat consigné, et la demande reste rejouable pour ce qui a échoué. Un module qui lève — une extension tierce, un bogue — est consigné comme un échec et n’emporte pas la demande.

Rejouer est sans danger

Les modules rendent « rien à faire » sur ce qu’ils ont déjà traité. Une seconde exécution reprend ce qui avait échoué sans défaire ce qui avait réussi, et le journal garde les deux passages — c’est précisément ce qu’on veut pouvoir montrer.

La confirmation est un geste à part

Ouvrir une demande et l’exécuter sont deux gestes, deux nonces, deux lignes de journal. On pourrait les réunir derrière une confirmation JavaScript ; cela mettrait un effacement irréversible à un clic de distance, sur un écran qu’on parcourt.

Et confirmer retient qui a confirmé, ce qu’une boîte de dialogue ne sait pas faire.

À l’exécution, l’adresse est redemandée. Elle n’est pas conservée, et son empreinte ne se renverse pas. C’est une contrainte volontaire : la table ne porte pas ce qu’on s’apprête à effacer.

La table des demandes ne porte aucune adresse

Seulement une empreinte, sous le sel du site.

Une table de demandes de suppression qui porterait les adresses de ceux qui ont demandé la suppression serait exactement le fichier qu’ils voulaient voir disparaître — et il survivrait à l’effacement de leurs soumissions. Le libellé affiché se reconstruit depuis les soumissions tant qu’elles existent. Après exécution, il n’y a plus rien à afficher, et c’est le but.

Le journal ne se reprend pas

Append-only, comme le plan l’exige. Pas de state, pas d’updated_at, et aucune méthode pour reprendre ou supprimer une ligne — la contrainte vit dans la classe plutôt que dans une permission de base de données, qu’un hébergement mutualisé n’accorde pas.

Un journal qu’on peut corriger ne prouve rien. C’est sa seule raison d’être : le jour où quelqu’un demande ce qui a été fait de ses données, c’est la seule réponse qui vaille, et elle doit être antérieure à la question.

Effacer une demande ne touche pas son journal. Les deux tables ne sont liées par aucune contrainte : supprimer une demande emporterait la preuve qu’on a fait ce qu’on devait.

L’export n’atterrit jamais dans vos téléversements

C’eût été le plus simple : écrire le ZIP dans uploads/ et en donner l’adresse. Ce serait un fichier contenant tout d’une personne, sur une adresse devinable, servie par le serveur web, et que personne ne penserait à effacer. Les demandes de confidentialité produiraient ainsi exactement la fuite qu’elles servent à réparer.

L’archive est composée dans le répertoire temporaire du système, servie en flux derrière une capacité, et effacée immédiatement.

Sans ZipArchive — qui n’est pas garanti chez tous les hébergeurs — tout est servi en un seul CSV, avec une colonne de plus nommant le module. Refuser l’export pour cette raison priverait d’une obligation à laquelle il faut répondre.

La conservation est par formulaire, et éteinte par défaut

Une purge active par défaut effacerait, au premier passage nocturne, des réponses que personne n’a décidé de perdre — et sur un site installé depuis deux ans, toutes d’un coup.

En deçà de sept jours, rien n’est appliqué. Le plancher laisse le temps de s’apercevoir qu’on a tapé 1 au lieu de 100, et cette semaine ne coûte rien à qui voulait vraiment purger. Un délai sous le plancher vaut rien, jamais « tout de suite » : lire une saisie fautive comme un ordre d’effacement immédiat serait la pire des deux lectures.

Anonymiser garde les agrégats

C’est la moitié qu’on oublie. Vider la soumission entière serait plus simple et détruirait ce qui n’identifie personne : la date, le formulaire, le montant payé, la réponse à « quel service vous intéresse ». Une boutique qui anonymise ses clients ne doit pas perdre son chiffre d’affaires, et une enquête ne doit pas perdre ses réponses — c’est ce qu’une enquête anonymisée est censée devenir.

Le remplacement se fait par type de champ, pas par libellé. Un champ « Votre nom » peut s’appeler nom, name, client, qui ; deviner d’après le libellé marcherait neuf fois sur dix, et la dixième laisserait une identité en place sans que personne ne s’en aperçoive.

TraitementTypes
Remplacés par un jetonemail, name, phone, address, url, hidden, signature, file
Conservés tels quelsselect, radio, checkbox, number, date, time, country, payment, calculated
Vidéstout le reste, texte libre compris

Le texte libre est vidé plutôt que remplacé par un jeton : aucune règle ne peut en extraire l’identité, et un commentaire porte souvent un nom — parfois celui de quelqu’un d’autre. Le garder reviendrait à ne rien anonymiser.

Un type non classé est vidé. L’oubli va vers l’effacement, qui se voit à la relecture, plutôt que vers la conservation, qui ne se voit jamais.

Le jeton est stable et propre au site : deux réponses de la même personne restent rapprochables sans qu’on sache de qui, et le jeton ne vaut rien sur une autre installation.

Le terme est contrôlé deux fois

Le filtre de date du dépôt écarte silencieusement une valeur qu’il ne sait pas lire : une faute de format n’y produit pas d’erreur, elle produit « aucun filtre ». Sur une purge, cela voudrait dire effacer toutes les soumissions du formulaire, sans qu’aucune exception ne le signale.

L’exécuteur revérifie donc chaque date lui-même, avec une comparaison qui ne dépend d’aucun format — deux chaînes que nous avons composées toutes les deux. La correction du format seule aurait suffi aujourd’hui ; ce contrôle tient le jour où quelqu’un change ce que la requête accepte.

Le passage est borné

Un site installé depuis deux ans qui active une conservation de trente jours a, le lendemain, des dizaines de milliers de soumissions arrivées à terme. Les traiter d’un coup dépasse le temps d’exécution, meurt au milieu, et recommence le lendemain au même endroit — indéfiniment.

Ce qui reste attend le lendemain, et le retard se résorbe en quelques jours. Un effacement en retard est sans conséquence ; un effacement qui ne s’achève jamais en a.

Ce que la V1 ne fait pas

Pas de gestion universelle de toutes les extensions WordPress, pas de conseil juridique, pas de suppression d’un compte WordPress sans module dédié. Le flux s’arrête aux données que vos formulaires détiennent.