Réalisation :
Les techniques décrites ci-dessous m'ont permis de me débarrasser du spam.
Introduction
Même si la majeure partie du spam (près de 90%) est filtrée en amont par les outils
anti-spam des fournisseurs d’accès internet et de messagerie, il semble que 10% environ finit par
atterrir dans votre boîte de réception.
Ainsi, lorsque j'ai mis en ligne la première version de ma page de contact, je me
suis très vite retrouvé envahi par des spams divers et variés.
Mettre en place un captcha ?
Je ne sais pas si vous partagez mon sentiment, mais je trouve les captchas
particulièrement pénibles à utiliser lorsque je dois valider un formulaire sur un site web.
Souhaitant offrir à l'utilisateur une expérience fluide, j'ai choisi de me passer
de captcha et j'ai mis en place 3 couches de protection simples qui se sont révélées efficaces,
sachant qu'on parle ici d'un formulaire basique.
Formulaire Bootstrap 5
Préparation du projet :
Créer un dossier avec 4 fichiers
Un formulaire de contact Bootstrap simple dans le fichier contact.php :
📝 Note : On peut éviter de préciser l'attribut' "label for=" si on inclut le input dans le label
Voici le résultat :
Technique du "pot de miel"
Mille mercis à Benoit de nouvelle-techno.fr qui a réalisé un excellent tutoriel et une vidéo sur le sujet.
Citons l'auteur :
La technique du pot de miel : comment ça marche ?
La plupart des spams sont envoyés par des robots, scripts automatiques qui parcourent les sites à la
recherche de formulaires. Pour un formulaire de contact, les robots se sentent obligés de remplir
tous les champs proposés.
Le Honeypot consiste à insérer dans le formulaire un champ supplémentaire, masqué pour les
visiteurs normaux. Ceux-ci ne le rempliront pas étant donné qu'ils ne le voient pas. Un robot,
lui, le détectera et le complétera.
Ici nous allons masquer le champ "sujet" :
On pourrait utiliser <input type="hidden">
Bootstrap propose la classe .d-none pour masquer du
contenu html,
ce qui revient à appliquer la propriété css display:
none;
Mais si un spambot était suffisamment malin pour repérer <input type="hidden"> ou pour surveiller les classes standard de
Bootstrap ou des classes nommées .hidden, .mask, .hide etc. ?
Nous pouvons tenter de déjouer la malveillance du robot en définissant une
classe anodine .sujet qui aura la même fonction. Ajoutons la dans style.css et appliquons la aux label et input :
/* style.css */
.sujet {
display: none;
}
📝 Note : On applique l'attribut required aux
input mais pas pour le sujet qui ne doit pas être rempli.
On mettra aussi l'attribut autocomplete sur "off".
Traitement des données
Le fichier envoi.php va s'en occuper côté serveur en php
// vérification de la présence des données
if (
isset($_POST["name"]) && !empty($_POST["name"]) &&
isset($_POST["email"]) && !empty($_POST["email"]) &&
isset($_POST["message"]) && !empty($_POST["message"])
) {
// nettoyage des données
// strip_tags — Supprime les balises HTML et PHP d'une chaîne
$nom = strip_tags($_POST["name"]);
$email = strip_tags($_POST["email"]);
// htmlspecialchars — Convertit les caractères spéciaux en entités HTML
$message = htmlspecialchars($_POST["message"]);
// Vérifier sujet : s'il est rempli, fin du script
if (isset($_POST["sujet"]) && !empty($_POST["sujet"])) {
$sujetSpam = strip_tags($_POST["sujet"]);
die();
}
// Si le sujet n'a pas été rempli, on valide l'email et on continue :
if (filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) {
// wordwrap() pour limiter les lignes à 60 caractères
$message = wordwrap($message, 60);
// envoi de l'email
$to = "votre-email";
$retour = mail($to, "Envoi depuis votre page Contact : $nom", "message : " . $message, "From : " . $email);
if ($retour) {
echo "Merci {$nom}, Votre message a bien été envoyé";
}
if (!$retour) {
echo "Désolé, {$nom}, la transmission de votre message a échoué";
}
} // validate form
} // isset
Comment tester l'envoi d'email en local ?
Utilisez Laragon comme serveur
local apache / php. Il dispose d'un intercepteur d'emails qui permet de vérifier vos envois et de
les stocker.
Générer le formulaire en javascript
On peut utiliser javascript pour apporter une couche supplémentaire de sécurité ; si les champs de
formulaire ne sont pas présents sur la page mais sont générés à l'ouverture, peu de robots seront
capables d'analyser le javascript comme ils le font pour le html.
Ajouter à la suite de ce code :
<script src="contact.js"></script>
pour charger le script :
// script contact.js
addForm = function () {
const formContainer = document.getElementById('form_container');
const sendBtn = document.getElementById('sendBtn');
const form = document.createElement('form');
form.setAttribute('method', 'post');
form.setAttribute('action', 'envoi.php');
form.setAttribute('accept-charset', 'UTF-8');
form.setAttribute('autocomplete', 'on');
// name
const label_name = document.createElement('label');
label_name.setAttribute('for', 'name')
label_name.setAttribute('class', 'form-label')
label_name.insertAdjacentHTML('beforeend', 'Veuillez taper votre nom :');
const input_name = document.createElement('input');
input_name.setAttribute('name', 'name');
input_name.setAttribute('placeholder', 'ex : jean');
input_name.setAttribute('class', 'form-control');
input_name.required = true;
// email
const label_email = document.createElement('label');
label_email.setAttribute('for', 'email');
label_email.setAttribute('class', 'form-label')
label_email.insertAdjacentHTML('beforeend', 'Veuillez taper votre adresse email :');
const input_email = document.createElement('input');
input_email.setAttribute('name', 'email');
input_email.setAttribute('type', 'email');
input_email.setAttribute('pattern', '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$');
input_email.setAttribute('placeholder', 'ex : jean@jiggle.com');
input_email.setAttribute('value', 'contact@jpg-design.com');
input_email.setAttribute('class', 'form-control');
input_email.required = true;
// sujet
const label_sujet = document.createElement('label');
label_sujet.setAttribute('for', 'sujet');
label_sujet.insertAdjacentHTML('beforeend', 'Veuillez taper votre sujet :');
label_sujet.setAttribute('class', 'form-label sujet');
const input_sujet = document.createElement('input');
input_sujet.setAttribute('name', 'sujet');
input_sujet.setAttribute('placeholder', 'ex : sujet');
input_sujet.setAttribute('class', 'form-control sujet');
// propriétés spécifiques au champ caché :
// Enlever l'autocomplétion
input_sujet.setAttribute('autocomplete', 'off');
// Enlever l'attribut required
input_sujet.required = false;
// message
const label_msg = document.createElement('label');
label_msg.setAttribute('for', 'message');
label_msg.setAttribute('class', 'form-label')
label_msg.insertAdjacentHTML('beforeend', 'Veuillez taper votre message :');
const input_msg = document.createElement('textarea');
input_msg.setAttribute('name', 'message');
input_msg.setAttribute('placeholder', 'Votre message ici... (Les liens seront traités comme spam)');
input_msg.setAttribute('class', 'form-control');
input_msg.setAttribute('required', '');
// add form
formContainer.appendChild(form);
// add inputs
form.appendChild(label_name);
form.appendChild(input_name);
form.appendChild(label_email);
form.appendChild(input_email);
form.appendChild(label_sujet);
form.appendChild(input_sujet);
form.appendChild(label_msg);
form.appendChild(input_msg);
form.appendChild(sendBtn);
}
// test loading page
if (window.addEventListener) {
window.addEventListener("load", addForm, false);
}
Filtrage des liens et mots-clés
Pour filtrer les liens on va utiliser une expression régulière
Pour filtrer les mots-clés on va créer un tableau rempli de ce qu'on veut éviter,
puis le parcourir à la recherche d'un mot-clé typique d'un spam.
...
// Vérifier la présence de liens : si oui, fin du script
if ( preg_match('/http(s?):\/\//ism',$message) ) {
die();
}
// Vérifier la présence de mots-clés
$bads = array('casino','gambling','ranks','Bitcoin','viagra','v i a g r a');
// boucle pour parcourir le tableau de bad words
foreach ($bads as $bad) {
// stripos — Recherche la position de la première occurrence dans une chaîne, sans tenir compte de la casse.
// si la recherche dans $message donne un résultat, fin du script
if ( stripos($message, $bad) !== false ) {
break;
die();
}
}
// Si toutes les conditions sont remplies, envoi de l'email
if ( filter_var($_POST["email"], FILTER_VALIDATE_EMAIL) && stripos($message, $bad) === false ) {
$message = wordwrap($message,60);
// envoi de l'email
$to = "votre-email";
$retour = mail($to, "Envoi depuis votre page Contact : $nom", "message : " . $message, "From : " . $email);
...
Téléchargement du zip contenant le projet complet