Initial release. Fonctionnel quand même.

This commit is contained in:
Zatalyz 2024-09-11 15:06:40 +02:00
parent fe3a68e777
commit b155d38da8
5 changed files with 369 additions and 2 deletions

View file

@ -1,3 +1,56 @@
# contactphpantibot
# Formulaire de contact en php avec mesures antispam (contactphpantibot)
Ceci est un "hook" php afin d'ajouter un formulaire de contact en php sur des sites basiques. Il intègre quelques protections pour limiter le spam, sans passer par des captchas ou des ressources coûteuses en temps de calcul. Ça reste assez basique.
Ouais, du php. Na.
J'ai découpé en plusieurs fichiers, sans doute que ça peut se condenser. Je ne suis pas dev, il est donc possible que ce soit très foireux. Toute aide pour améliorer est bienvenue.
## Installation
Copiez les fichiers php là où vous voulez sur votre site. Vérifiez les chemins (je pourrais améliorer ça dans la config ; on verra à l'usage). Incluez "form.php" où vous voulez sur une de vos pages php :
<?php
include 'form.php';
?>
Le script utilise la fonction mail de php, donc faut avoir php ET de quoi envoyer des mails sur votre serveur. Moi, j'utilise msmtp (https://alinea.ninm.net/dokuwiki/pratique:informatique:mail_relai ), c'est bien, simple et facile.
## Configuration
Modifiez le fichier "form_config.php" pour adapter à vos propres paramètres. En particulier le mail...
## Personnalisation et multilingue
Modifiez "form_lang.php" pour personnaliser les messages, voir ajouter des langues.
"form_struct.php" concerne le formulaire "presque" html. Ajoutez les classes de votre site web.
Non, je n'inclue pas de css, faut que ce soit cohérent avec les sites, donc : débrouillez-vous.
C'est aussi là qu'il y a la liste des questions parce que si c'est multilingue, c'est mieux de traduire vos questions, justement.
## Mesures antispams
### Honeypot
Il y a une case qui peut être cochée mais invisible (en principe) pour les êtres humains. Je ne sais pas trop si les lecteurs d'écran risquent de la voir donc j'ai ajouté une description.
Les bots les plus basiques vont soit tout cocher, soit rien. Or il faut laisser décoché la case "je suis un bot" et cocher "je suis un être humain" pour que l'envoi fonctionne.
### Pas de liens
S'il y a un lien, ça va bloquer l'envoi. Parce que c'est rare qu'un premier contact légitime vous envoie un lien...
### Mots-clés bloqués
Il y a une liste (basique) de mots-clés qui vont empecher l'envoi d'un mail. Ça demande à être complété. C'est très basique aussi bien sûr.
### Question personnalisée
Ça reste assez redoutable pour pas mal de bots. Créez les votres, c'est comme ça que c'est le plus efficace.
#### Efficacité
Soyons honnête : ça va filtrer les plus basiques. Mais c'est déjà ça. On ne fera pas face aux IA avec ça, mais qui va brûler des tonnes d'énergie pour vous envoyer un message proposant des élargissements de péniches ? Oui, on sait, ils le feront… Mais en attendant d'avoir de meilleurs parades, ça limitera un petit peu le spam dans votre boîte.
## Todo
Il faudrait ajouter
- Une gestion des délais en associant le token csrf du formulaire avec un timestamp et refuser la requete si elle est faite trop rapidement
- Une détection des ip faisant des erreurs afin de les bannir
Il faut aussi vérifier l'accessibilité. Ça semble acceptable "là", à voir quand on est inclus dans un site.
Et puis ajouter un menu déroulant avec options à choisir pour celles qui veulent trier un peu ("devis", "papote", "formation", etc). À inclure en première ligne du message.

125
form.php Normal file
View file

@ -0,0 +1,125 @@
<?php
session_start();
// Générer ou récupérer le token CSRF
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// Rendre le script plus facile à personnaliser avec des variables.
$config = include('form_config.php');
// Et multilingue !
include 'form_lang.php';
// Fonction pour échapper les sorties
function escape($data) {
return htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
}
// Générer ou récupérer la question antispam
if (!isset($_SESSION['current_question']) || isset($_POST['new_question'])) {
// Choisir une question aléatoire
$questions_list = array_keys($txt['questions']);
$current_question = $questions_list[array_rand($questions_list)];
$correct_answers = $txt['questions'][$current_question];
// Mettre à jour la session avec la question et les réponses acceptées
$_SESSION['current_question'] = $current_question;
$_SESSION['correct_answers'] = $correct_answers;
} else {
// Utiliser la question stockée dans la session
$current_question = $_SESSION['current_question'];
$correct_answers = $_SESSION['correct_answers'];
}
// La partie PHP du formulaire
// Récuperation des valeurs en GET ou en POST
$try = isset($_GET['try']) ? $_GET['try'] : (isset($_POST['try']) ? $_POST['try'] : '');
$nobotv = isset($_GET['nobotv']) ? $_GET['nobotv'] : (isset($_POST['nobotv']) ? $_POST['nobotv'] : '');
$nobotc = isset($_GET['nobotc']) ? $_GET['nobotc'] : (isset($_POST['nobotc']) ? $_POST['nobotc'] : '');
$nobots = isset($_GET['nobots']) ? $_GET['nobots'] : (isset($_POST['nobots']) ? $_POST['nobots'] : '');
$nobot = time() . '_' . rand(50000, 60000);
// Vérifier si la demande est pour une nouvelle question
if (isset($_POST['new_question'])) {
// Réinitialiser la question actuelle
$_SESSION['current_question'] = null;
$_SESSION['correct_answers'] = null;
// Rediriger pour éviter la soumission de formulaire inutile
header("Location: form.php");
exit();
}
if ($try == 'send') {
// Vérifier le token CSRF
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
echo $txt['csrf_error'];
include 'form_struct.php';
exit();
}
// Ici Le visiteur soumet le formulaire
if (($nobotc != md5($nobotv)) || ($nobotv == '') || ($nobots != '')) {
echo $txt['antispam_error'];
include 'form_struct.php';
} else {
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$name = strip_tags(trim($_POST["name"]));
$email = filter_var(trim($_POST["email"]), FILTER_VALIDATE_EMAIL);
$subject = strip_tags(trim($_POST["subject"]));
$message = strip_tags(trim($_POST["message"]));
$user_answer = isset($_POST['answer']) ? trim($_POST['answer']) : '';
// Validation des données
if (empty($name) || empty($email) || empty($subject) || empty($message) || empty($user_answer)) {
echo $txt['required_fields'];
include 'form_struct.php'; // Réaffiche le formulaire en cas d'erreur
} else {
// Vérifier la présence de liens : si oui, fin du script
if (preg_match('/http(s?):\/\//ism', $message)) {
echo $txt['link_error'];
include 'form_struct.php'; // Réaffiche le formulaire en cas d'erreur
exit(); // Arrêter l'exécution si un lien est trouvé
}
// Vérifier la présence de mots-clés
$bad_words = $config['bad_words'];
foreach ($bad_words as $bad) {
if (stripos($message, $bad) !== false) {
echo $txt['bad_word_error'];
include 'form_struct.php'; // Réaffiche le formulaire en cas d'erreur
exit(); // Arrêter l'exécution si un mot interdit est trouvé
}
}
// Vérifier la réponse à la question antispam
if (isset($_POST['submit_form'])) {
if (!empty($user_answer) && in_array(strtolower($user_answer), array_map('strtolower', $correct_answers))) {
echo $txt['good_answer'];
unset($_SESSION['current_question']);
unset($_SESSION['correct_answers']);
// Envoi de l'e-mail
$to = $config['email']; // Remplacez par votre adresse e-mail
$subject_prefix = $config['subject_prefix'];
$subjectreal = "$subject_prefix : $subject";
$headers = "From: $name <$email>";
if (mail($to, $subjectreal, $message, $headers)) {
echo $txt['email_success'];
} else {
echo $txt['email_error'];
}
} else {
echo $txt['bad_answer'];
include 'form_struct.php'; // Réaffiche le formulaire en cas d'erreur
}
}
}
}
}
} else {
// Ici on affiche le formulaire, c'est l'affichage par défaut
include 'form_struct.php';
}
?>
<div><a href="index.php">Retour au site</a></div>

64
form_config.php Normal file
View file

@ -0,0 +1,64 @@
<?php
// Configuration
return [
'email' => 'mail@example.org', // Mettez votre adresse
'subject_prefix' => 'Message via site', // Personnalisez le début des sujets envoyés via le formulaire
'languages' => ['fr', 'en'], // Liste des langues disponibles
'bad_words' => [
// Liste de mots-clés qui vont bloquer l'envoi de formulaire (filtre un peu)
'casino',
'gambling',
'ranks',
'Bitcoin',
'viagra',
'v i a g r a',
'gagner de l\'argent',
'offre spéciale',
'cliquez ici',
'pharmacie',
'vente en gros',
'meilleur prix',
'produit miracle',
'augmentez vos revenus',
'assurance',
'crédit facile',
'urgent',
'gagnez de l\'argent rapidement',
'offre limitée',
'compléments alimentaires',
'dépêchez-vous',
'réduction exclusive',
'investissement sans risque',
'données confidentielles',
'vérifiez votre compte',
'richesse rapide',
'prêt personnel',
'récompenses',
'gagnez maintenant',
'promotion',
'faites des économies',
'enquête rémunérée',
'investir maintenant',
'déclaration de revenus',
'fonds d\'investissement',
'revenu passif',
'annonce',
'super offre',
'affaire en or',
'confirmation de compte',
'sécurisé',
'certificat gratuit',
'remboursement',
'ne manquez pas',
'cadeau gratuit',
'réponse immédiate',
'email marketing',
'satisfaction garantie',
'augmenter vos ventes',
'réclamez votre prix',
'assistance gratuite',
'téléchargez maintenant',
'sans frais',
]
];
?>

84
form_lang.php Normal file
View file

@ -0,0 +1,84 @@
<?php
// La liste des termes. Pour appeler dans le script : $txt['blabla']
$lang = array(
'fr' => array(
// Champs du formulaire
'name' => "Nom",
'mail' => "Courriel",
'subject' => "Sujet",
'message' => "Message",
// messages d'erreurs
'required_fields' => "Tous les champs sont obligatoires.",
'link_error' => "Les liens sont interdits dans le message.",
'bad_word_error' => "Votre message contient des termes utilisé par les spammeurs, il n'a pas été envoyé.",
'email_success' => "Votre message a été envoyé avec succès.",
'email_error' => "Une erreur s'est produite lors de l'envoi du message. Veuillez réessayer. Si cela se répète, c'est simplement que le formulaire est cassé, ne vous acharnez pas.",
'antispam_error' => "Vous avez agi comme un bot ; si vous n'en êtes pas un, cochez la bonne case.",
'submit_button' => "Envoyer",
'new_question_button' => "Demander une autre question",
'human' => "Je confirme que je suis un être humain, et que ma demande de contact est directement en rapport avec le site et n'est pas destiné à vendre quelque chose ou une quelconque pratique de type spam.",
'bot' => "Je suis un bot et je ne veux pas envoyer de courrier, je coche la case.",
'good_answer' => "Félicitations ! Votre réponse est correcte.",
'bad_answer' => "Réponse incorrecte. Essayez encore, mais pas trop vite ni trop de fois...",
'questions' => [
// personnalisez ces questions afin de complexifier la vie des spammeurs !
// Vous pouvez en ajouter autant que vous voulez.
"Quelle est la couleur du ciel ?" => ["bleu", "gris", "orange"],
"Donnez la couleur d'une cerise." => ["rouge", "vert", "blanc"],
"Combien de pattes a un chien en bonne santé ?" => ["quatre", "4", "quatres"],
"Donnez le nom d'une saison en climat tempéré" => ["hiver", "automne", "été", "printemps"],
"Vous êtes plutôt thé, chocolat, ou autre ?" => ["thé", "chocolat", "autre"],
"Quelle couleur obtient-on en mélangeant du bleu et du jaune ?" => ["vert"],
"Quel animal de la savane a des rayures noires et blanches ?" => ["zèbre", "le zèbre", "les zèbres"],
"Quel est le jour de la semaine actuel ?" => ["lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"],
],
),
'en' => array(
// Form fields
'name' => "Name",
'mail' => "Email",
'subject' => "Subject",
'message' => "Message",
// error messages
'required_fields' => "All fields are required",
'link_error' => "Links are forbidden in the message",
'bad_word_error' => "Your message contains terms used by spammers, it has not been sent",
'email_success' => "Your message has been sent successfully",
'email_error' => "An error has occurred while trying to send this message. Please try again. If this happens again, it's simply that the form is broken, so don't bother.",
'antispam_error' => "You've acted like a bot; if you're not, tick the right box.",
'submit_button' => "Send",
'new_question_button' => "Ask another question",
'human' => "I confirm that I am a human being, and that my contact request is directly related to the site and is not intended to sell anything or any spam-like practice.",
'bot' => "I'm a bot and I don't want to send any mail, I tick the box.",
'good_answer' => "Congratulations! Your answer is correct.",
'bad_answer' => "Incorrect answer. Try again, but not too fast or too many times...",
'questions' => [
// Customise these questions to make life more difficult for spammers!
// You can add as many as you like.
'What colour is the sky?' => ["blue", "grey", "orange"],
'What colour is a cherry?' => ["red", "green", "white"],
'How many legs does a healthy dog have?' => ["four", "4"],
'Name a season in a temperate climate' => ["winter", "autumn", "summer", "spring"],
'Do you prefer tea, chocolate or something else?' => ["tea", "chocolate", "something else"],
'What colour do you get if you mix blue and yellow?' => ["green"],
'Which savannah animal has black and white stripes?' => ["zebra", "the zebra", "zebras"],
'What is the current day of the week?' => ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
],
)
);
// Quelques déclarations pour que ça marche
// Définir la langue par défaut
$language = isset($_GET['lang']) ? $_GET['lang'] : 'fr';
// Si la langue n'existe pas dans le tableau, on utilise 'fr' par défaut
if (!array_key_exists($language, $lang)) {
$language = 'fr';
}
// Crée une variable pour stocker les textes dans la langue sélectionnée
$txt = $lang[$language];
?>

41
form_struct.php Normal file
View file

@ -0,0 +1,41 @@
<h2>Contact</h2>
<p>Vous voulez me spammer ? Heu, me contacter ? Laissez-moi un gentil mot :)</p>
<form action="form.php" method="POST">
<!-- Token CSRF -->
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
<input type="hidden" name="try" value="send">
<input type="hidden" name="nobotv" value="<?php echo $nobot; ?>">
<!-- Si j'ajoute "required", ça foire pour demander à changer de question... donc on va faire sans, de toute façon c'est ensuite testé par php. -->
<label for="name"><?php echo $txt['name']; ?></label>
<input type="text" id="name" name="name"><br><br>
<label for="email"><?php echo $txt['mail']; ?></label>
<input type="email" id="email" name="email" ><br><br>
<label for="subject"><?php echo $txt['subject']; ?></label>
<input type="text" id="subject" name="subject" ><br><br>
<label for="message"><?php echo $txt['message']; ?></label><br>
<textarea id="message" name="message" rows="4" cols="50" ></textarea><br><br>
<h3>Mesure anti spambot</h3>
<p>Parce que je n'aime pas le spam, je n'aime pas les messages douteux, et j'aime pas trop qu'on me cause en fait. Attention à vos réponses, je vous bannis de mes serveurs si vous êtes méchants. </p>
<!-- Questions -->
<p><label for="answer"><?php echo htmlspecialchars($current_question); ?></label>
<input type="text" id="answer" name="answer"><input type="submit" name="new_question" value="Demander une autre question">
</p>
<!-- Antibot -->
<input id="nobotc" type="checkbox" name="nobotc" value="<?php echo md5($nobot); ?>" />
<label for="nobotc"><?php echo $txt['human']; ?></label>
<p style="position: absolute; visibility: hidden; left: -5000; top: -5000">
<br><input id="nobots" type="checkbox" name="nobots" value="<?php echo time(); ?>" />
<label for="nobots"><?php echo $txt['bot']; ?></label>
</p>
<br><br>
<input type="submit" name="submit_form" value="<?php echo $txt['submit_button']; ?>">
</form>