Halte aux spams sur ta page de contact Prestashop

Hey, j’ai une question pour toi ! Est-ce que tu aimes recevoir des spams en série par ton formulaire de contact Prestashop ? Oui c’est usant et pas toujours simple de faire stopper le vilain qui fait ça… On va y remédier…

Qui spam ma boutique ?
La plupart du temps il s’agit de machines zombies ou de robots qui viennent valider votre formulaire pour vous vendre du viagra… ou autre chose… Quand vous recevez 1 spam par jour ça va… mais quand c’est 1 chaque heure ou 5 minutes… ça devient vraiment usant. Vous pouvez pratiquer des exclusions via .htaccess ou par IP  depuis votre back-office, mais ce n’est pas toujours la solution la plus fiable et efficace.

Que faire alors ?
On va faire un truc tout simple… dans un premier temps ça devrait suffire, on va ajouter une question dans le formulaire de contact… et il faudra répondre correctement pour soumettre le formulaire. On va faire une petite question d’addition… même les plus pourris en math avec 0 de moyenne devraient y arriver (si si… juré).

Go ! Allez on fonce…
Bon première chose à faire c’est de créer un fichier « ContactController.php » sous « /modules/override/controllers ». Dans ce fichier j’ai mis quelques commentaires où j’ai ajouté mon code… (y’a juste à 2 endroits). Je vous colle quand même l’intégralité du fichier. En fait je me contente de générer  2 nombres aléatoires et j’ajoute un contrôle supplémentaire sur l’addition des deux chiffres en comparant avec la réponse donnée par le client, juste avant d’envoyer l’email. Contenu du fichier ContactController.php (c’est gros… je sais navré pour le pavé)

<?php

class ContactController extends ContactControllerCore
{

	public function preProcess()
	{
		//parent::preProcess();

                /* Webbax - 12.12.12 - captcha */
                $nb1 = rand(1,10);
                $nb2 = rand(1,10);
                self::$smarty--->assign(array('nb1'=>$nb1,'nb2'=>$nb2));

		if (self::$cookie->isLogged())
		{
			self::$smarty->assign('isLogged', 1);
			$customer = new Customer((int)(self::$cookie->id_customer));
			if (!Validate::isLoadedObject($customer))
				die(Tools::displayError('Customer not found'));
			$products = array();
			$orders = array();
			$getOrders = Db::getInstance()->ExecuteS('
			SELECT id_order
			FROM '._DB_PREFIX_.'orders
			WHERE id_customer = '.(int)$customer->id.' ORDER BY date_add');
			foreach ($getOrders as $row)
			{
				$order = new Order((int)$row['id_order']);
				$date = explode(' ', $order->date_add);
				$orders[(int)$row['id_order']] = Tools::displayDate($date[0], self::$cookie->id_lang);
				$tmp = $order->getProducts();
				foreach ($tmp as $key => $val)
					$products[$val['product_id']] = $val['product_name'];
			}

			$orderList = '';
			foreach ($orders as $key => $val)
				$orderList .= '<option value="'.$key.'">'.$key.' -- '.$val.'</option>';
			$orderedProductList = '';

			foreach ($products as $key => $val)
				$orderedProductList .= '<option value="'.$key.'">'.$val.'</option>';
			self::$smarty->assign('orderList', $orderList);
			self::$smarty->assign('orderedProductList', $orderedProductList);
		}

		if (Tools::isSubmit('submitMessage'))
		{
			$fileAttachment = null;
			if (Configuration::get('PS_CUSTOMER_SERVICE_FILE_UPLOAD') && isset($_FILES['fileUpload']['name']) &&
				!empty($_FILES['fileUpload']['name']) && !empty($_FILES['fileUpload']['tmp_name']))
			{
				$extension = array('.txt', '.rtf', '.doc', '.docx', '.pdf', '.zip', '.png', '.jpeg', '.gif', '.jpg');
				$filename = uniqid().substr($_FILES['fileUpload']['name'], -5);
				$fileAttachment['content'] = file_get_contents($_FILES['fileUpload']['tmp_name']);
				$fileAttachment['name'] = $_FILES['fileUpload']['name'];
				$fileAttachment['mime'] = $_FILES['fileUpload']['type'];
			}
			$message = Tools::htmlentitiesUTF8(Tools::getValue('message'));
			if (!($from = trim(Tools::getValue('from'))) OR !Validate::isEmail($from))
				$this->errors[] = Tools::displayError('Invalid e-mail address');
			elseif (!($message = nl2br2($message)))
				$this->errors[] = Tools::displayError('Message cannot be blank');
			elseif (!Validate::isCleanHtml($message))
				$this->errors[] = Tools::displayError('Invalid message');
			elseif (!($id_contact = (int)(Tools::getValue('id_contact'))) OR !(Validate::isLoadedObject($contact = new Contact((int)($id_contact), (int)(self::$cookie->id_lang)))))
				$this->errors[] = Tools::displayError('Please select a subject on the list.');
			elseif (!empty($_FILES['fileUpload']['name']) AND $_FILES['fileUpload']['error'] != 0)
				$this->errors[] = Tools::displayError('An error occurred during the file upload');
			elseif (!empty($_FILES['fileUpload']['name']) AND !in_array(substr($_FILES['fileUpload']['name'], -4), $extension) AND !in_array(substr($_FILES['fileUpload']['name'], -5), $extension))
				$this->errors[] = Tools::displayError('Bad file extension');
                        /* Webbax 12.12.12 - contrôle captcha */
                        elseif((Tools::getValue('nb1')+Tools::getValue('nb2'))!=Tools::getValue('sum'))
                                $this->errors[] = Tools::displayError('Le code Anti-robots est incorrect');
			else
			{
				if ((int)self::$cookie->id_customer)
					$customer = new Customer((int)(self::$cookie->id_customer));
				else
				{
					$customer = new Customer();
					$customer->getByEmail($from);
				}

				$contact = new Contact((int)$id_contact, (int)self::$cookie->id_lang);

				if (!((
						$id_customer_thread = (int)Tools::getValue('id_customer_thread')
						AND (int)Db::getInstance()->getValue('
						SELECT cm.id_customer_thread FROM '._DB_PREFIX_.'customer_thread cm
						WHERE cm.id_customer_thread = '.(int)$id_customer_thread.' AND token = \''.pSQL(Tools::getValue('token')).'\'')
					) OR (
						$id_customer_thread = (int)Db::getInstance()->getValue('
						SELECT cm.id_customer_thread FROM '._DB_PREFIX_.'customer_thread cm
						WHERE cm.email = \''.pSQL($from).'\' AND cm.id_order = '.(int)Tools::getValue('id_order'))
					)))
				{
					$fields = Db::getInstance()->ExecuteS('
					SELECT cm.id_customer_thread, cm.id_contact, cm.id_customer, cm.id_order, cm.id_product, cm.email
					FROM '._DB_PREFIX_.'customer_thread cm
					WHERE email = \''.pSQL($from).'\' AND ('.
						($customer->id ? 'id_customer = '.(int)$customer->id.' OR ' : '').'
						id_order = '.(int)Tools::getValue('id_order').')');
					$score = 0;
					foreach ($fields as $key => $row)
					{
						$tmp = 0;
						if ((int)$row['id_customer'] && $row['id_customer'] != $customer->id && $row['email'] != $from)
							continue;
						if ($row['id_order'] != 0 && Tools::getValue('id_order') != $row['id_order'])
							continue;
						if ($row['email'] == $from)
							$tmp += 4;
						if ($row['id_contact'] == $id_contact)
							$tmp++;
						if (Tools::getValue('id_product') != 0 && $row['id_product'] == Tools::getValue('id_product'))
							$tmp += 2;
						if ($tmp >= 5 && $tmp >= $score)
						{
							$score = $tmp;
							$id_customer_thread = (int)$row['id_customer_thread'];
						}
					}
				}
				$old_message = Db::getInstance()->getValue('
				SELECT cm.message FROM '._DB_PREFIX_.'customer_message cm
				WHERE cm.id_customer_thread = '.(int)$id_customer_thread.'
				ORDER BY date_add DESC');
				if ($old_message == htmlentities($message, ENT_COMPAT, 'UTF-8'))
				{
					self::$smarty->assign('alreadySent', 1);
					$contact->email = '';
					$contact->customer_service = 0;
				}
				if (!empty($contact->email))
				{
					$email_variables = array('{email}' => $from, '{message}' => stripslashes($message), '{id_product}' => '', '{id_order}' => '');
					if (Tools::getValue('id_order'))
					{
						$order = new Order((int)Tools::getValue('id_order'));
						if (Validate::isLoadedObject($order) && $order->id_customer == $customer->id)
						{
							$email_variables['{id_order}'] = Mail::l('Related Order ID:').' <strong>'.(int)Tools::getValue('id_order').'</strong>

';
							if (Tools::getValue('id_product') && isset($products[(int)Tools::getValue('id_product')]))
								$email_variables['{id_product}'] = Mail::l('Related Product:').' <strong>'.$products[(int)Tools::getValue('id_product')].'</strong>

';
						}
					}

					if (Mail::Send((int)self::$cookie->id_lang, 'contact', Mail::l('Message from contact form', (int)self::$cookie->id_lang), $email_variables, $contact->email, $contact->name, $from, ((int)(self::$cookie->id_customer) ? $customer->firstname.' '.$customer->lastname : ''), $fileAttachment)
						&& Mail::Send((int)self::$cookie->id_lang, 'contact_form', Mail::l('Your message has been correctly sent', (int)self::$cookie->id_lang), $email_variables, $from))
						self::$smarty->assign('confirmation', 1);
					else
						$this->errors[] = Tools::displayError('An error occurred while sending message.');
				}

				if ($contact->customer_service)
				{
					if ((int)$id_customer_thread)
					{
						$ct = new CustomerThread($id_customer_thread);
						$ct->status = 'open';
						$ct->id_lang = (int)self::$cookie->id_lang;
						$ct->id_contact = (int)($id_contact);
						if ($id_order = (int)Tools::getValue('id_order'))
							$ct->id_order = $id_order;
						if ($id_product = (int)Tools::getValue('id_product'))
							$ct->id_product = $id_product;
						$ct->update();
					}
					else
					{
						$ct = new CustomerThread();
						if (isset($customer->id))
							$ct->id_customer = (int)($customer->id);
						if ($id_order = (int)Tools::getValue('id_order'))
							$ct->id_order = $id_order;
						if ($id_product = (int)Tools::getValue('id_product'))
							$ct->id_product = $id_product;
						$ct->id_contact = (int)($id_contact);
						$ct->id_lang = (int)self::$cookie->id_lang;
						$ct->email = $from;
						$ct->status = 'open';
						$ct->token = Tools::passwdGen(12);
						$ct->add();
					}

					if ($ct->id)
					{
						$cm = new CustomerMessage();
						$cm->id_customer_thread = $ct->id;
						$cm->message = htmlentities($message, ENT_COMPAT, 'UTF-8');
						if (isset($filename) AND rename($_FILES['fileUpload']['tmp_name'], _PS_MODULE_DIR_.'../upload/'.$filename))
							$cm->file_name = $filename;
						$cm->ip_address = ip2long($_SERVER['REMOTE_ADDR']);
						$cm->user_agent = $_SERVER['HTTP_USER_AGENT'];
						if ($cm->add())
						{
							if (empty($contact->email))
								Mail::Send((int)self::$cookie->id_lang, 'contact_form', Mail::l('Your message has been correctly sent', (int)self::$cookie->id_lang), array('{message}' => stripslashes($message)), $from);
							self::$smarty->assign('confirmation', 1);
						}
						else
							$this->errors[] = Tools::displayError('An error occurred while sending message.');
					}
					else
						$this->errors[] = Tools::displayError('An error occurred while sending message.');
				}
				if (count($this->errors) > 1)
					array_unique($this->errors);
			}
		}
	}

}

Ensuite on va modifier le formulaire de contact dans le thème, qui se trouve sous « themes/votretheme/contact-form.tpl », dans ce fichier on va ajouter un champ dans lequel le client devra répondre, à la question de l’addition.

Contenu du fichier « contact-form.tpl »

{capture name=path}{l s='Contact'}{/capture}
{include file="$tpl_dir./breadcrumb.tpl"}
<h1>{l s='Customer Service'} - {if isset($customerThread) && $customerThread}{l s='Your reply'}{else}{l s='Contact us'}{/if}</h1>
{if isset($confirmation)}
{l s='Your message has been successfully sent to our team.'}

<ul class="footer_links">
	<li><a href="{$base_dir}"><img class="icon" src="{$img_dir}icon/home.gif" alt="" /></a><a href="{$base_dir}">{l s='Home'}</a></li>
</ul>
{elseif isset($alreadySent)}
{l s='Your message has already been sent.'}

<ul class="footer_links">
	<li><a href="{$base_dir}"><img class="icon" src="{$img_dir}icon/home.gif" alt="" /></a><a href="{$base_dir}">{l s='Home'}</a></li>
</ul>
{else}
{l s='For questions about an order or for more information about our products'}.
	{include file="$tpl_dir./errors.tpl"}
	<form class="std" action="{$request_uri|escape:'htmlall':'UTF-8'}" enctype="multipart/form-data" method="post">
<fieldset>
<h3>{l s='Send a message'}</h3>
				<label for="id_contact">{l s='Subject Heading'}</label>
			{if isset($customerThread.id_contact)}
				{foreach from=$contacts item=contact}
					{if $contact.id_contact == $customerThread.id_contact}
						<input id="contact_name" name="contact_name" readonly="readonly" type="text" value="{$contact.name|escape:'htmlall':'UTF-8'}" />
						<input name="id_contact" type="hidden" value="{$contact.id_contact}" />
					{/if}
				{/foreach}
			{else}
				<select id="id_contact" name="id_contact">
					<option value="0">{l s='-- Choose --'}</option>
				{foreach from=$contacts item=contact}
					<option value="{$contact.id_contact|intval}">{$contact.name|escape:'htmlall':'UTF-8'}</option>
				{/foreach}
				</select>

				{foreach from=$contacts item=contact}
						<label> </label>{$contact.description|escape:'htmlall':'UTF-8'}
				{/foreach}
			{/if}
				<label for="email">{l s='E-mail address'}</label>
				{if isset($customerThread.email)}
					<input id="email" name="from" readonly="readonly" type="text" value="{$customerThread.email}" />
				{else}
					<input id="email" name="from" type="text" value="{$email}" />
				{/if}
		{if !$PS_CATALOG_MODE}
			{if (!isset($customerThread.id_order) || $customerThread.id_order > 0)}
				<label for="id_order">{l s='Order ID'}</label>
				{if !isset($customerThread.id_order) && isset($isLogged) && $isLogged == 1}
					<select name="id_order"><option value="0">{l s='-- Choose --'}</option>{$orderList}</select>
				{elseif !isset($customerThread.id_order) && !isset($isLogged)}
					<input id="id_order" name="id_order" type="text" value="{if isset($customerThread.id_order) && $customerThread.id_order > 0}{$customerThread.id_order|intval}{else}{if isset($smarty.post.id_order)}{$smarty.post.id_order|intval}{/if}{/if}" />
				{elseif $customerThread.id_order > 0}
					<input id="id_order" name="id_order" readonly="readonly" type="text" value="{$customerThread.id_order|intval}" />
				{/if}
			{/if}
			{if isset($isLogged) && $isLogged}
			<label for="id_product">{l s='Product'}</label>
				{if !isset($customerThread.id_product)}
					<select style="width: 300px;" name="id_product"><option value="0">{l s='-- Choose --'}</option>{$orderedProductList}</select>
				{elseif $customerThread.id_product > 0}
					<input id="id_product" name="id_product" readonly="readonly" type="text" value="{$customerThread.id_product|intval}" />
				{/if}
			{/if}
		{/if}
		{if $fileupload == 1}
			<label for="fileUpload">{l s='Attach File'}</label>
				<input name="MAX_FILE_SIZE" type="hidden" value="2000000" />
				<input id="fileUpload" name="fileUpload" type="file" />
		{/if}
			<label for="message">{l s='Message'}</label>
			 <textarea id="message" style="width: 340px; height: 220px;" cols="20" rows="15" name="message">{if isset($message)}{$message|escape:'htmlall':'UTF-8'|stripslashes}{/if}</textarea>
                {* 12.12.12 - captcha Webbax *}
			<label> * {l s='Anti-robots'}</label>
                        <input style="width: 20px;" maxlength="2" name="nb1" type="text" value="{$nb1}" /> +
                        <input style="width: 20px;" maxlength="2" name="nb2" type="text" value="{$nb2}" /> =
                        <input style="width: 20px;" maxlength="2" name="sum" type="text" />

			<input id="submitMessage" class="button_large" onclick="$(this).hide();" name="submitMessage" type="submit" value="{l s='Send'}" />
</fieldset>
</form>
{/if}

Pour les plus fainéants… vous pouvez télécharger directement les fichiers sources ici.

Regardons le résultat
Bien sûr je vous copie le code en vrac… mais en fait vous avez juste à remplacer ces deux fichiers… c’est méga simple et rapide. Maintenant si on affiche la page on a 2 champs et si le client valide sans donner la réponse… le mail ne sera pas envoyé. Bon bien sûr que y’a des robots capables de résoudre automatiquement ce type d’équations… mais actuellement c’est suffisant pour être tranquille, car la manière de faire n’est pas forcément reconnue par le robot. Si vous ne voyez pas le champ après avoir effectué la manipulation ci-dessus, pensez à désactiver le cache + forcer la compilation (dans de back-office sous l’onglet performances).

Bilan
Comme Prestashop devient de plus en plus populaire, il y’a un intérêt pour les « spamers », car ils peuvent étendre leur offre grâce à tous ces nouveaux marchands. C’est un peu comme WordPress… plus le script est connu… plus il est spamé… car la structure de base est connue… du coup on peut créer un robot qui répond directement aux exigences du formulaire selon la plateforme. Bon, cette astuce est pas la plus sophistiquée, mais elle a le mérite d’être toute simple et lisible, les clients aiment !

Notez mon billet, Google va adorer :
1 étoiles - J'aime pas !2 étoiles - Bof !3 étoiles - Bien !4 étoiles - Très bien !5 étoiles - Génial ! (5 votes, moyenne : 4,60 sur 5)
Loading...

25 commentaires sur “Halte aux spams sur ta page de contact Prestashop”

  1. Reste le problème des SPAM sur les commentaires produits, et là je n’ai pas encore trouvé de module captcha.
    la solution consiste à obliger que le client soit enregistré, mais c’est un peu dommage.

  2. Pas bête surtout que des boites démarchent les clients par ces moyens là.
    Tu fais un scrap des Prestashop, tu dev. un petit template sick submiter et tu peux envoyer tranquillement un mail à tout le monde.
    Comme tu ne passes pas directement pas le mail pour faire la proposition commerciale, ça passe sans accroches.

  3. Bonjour,

    J’ai bien suivi toutes les étapes, j’ai bien les cases qui s’affichent, mais pas de chiffres et donc on peut envoyer un message sans devoir y répondre….

    Est ce que j’ai raté un truc ?

    Merci de votre aide 🙂

    Joel

  4. Hello,

    Oui j’ai testé aussi sur une version 1.5.3 en vidant le cache, idem pas de chiffre dans les cases 🙁
    Merci si Webbax passe par là 🙂
    alex

  5. Bonjour,

    tout d’abords merci pour l’explication.

    Juste pour être sur et comprendre d’ou viens mon erreur, le dossier /modules/override n’existe pas a la base ? je dois le créer et créer controller dedans également ?

    et pour les lignes de code du deuxième fichier c’est de la ligne 73 à 79 ?

    Merci

    1. Bonjour,

      Il faut utiliser le répertoire « /override/controllers » et non « /modules/override » car celui-ci n’existe pas.

      Oui pour les lignes de codes…

      Sinon à noter que ces changements avaient été proposés pour la version 1.4 de Prestashop

  6. Bonjour,

    j’ai installé ton code sur mon formulaire de contact et il fonctionne parfaitement, merci beaucoup.

    J’ai aussi du spam depuis les formulaires SendToMyFriend et Commentaires, est-il possible d’adapter ton code existant ou est-ce que ça passe par un développement spécifique ?

    1. Hello,

      Merci pour ton commentaire !

      Pour cela obligé d’adapter le code au traitement… il faut le faire de manière spécifique dans le module concerné (en reprenant la même logique).

  7. Bonjour,

    Ca marche nickel sur le contact form (desktop) mais malheureusement, ne s’installe pas sur le module Prestashop mobile thème (1.4)
    On peut donc toujours être spammé par ce moyen

  8. salut j’ai active la captcha de Google , mais aujourd’hui j’ai reçu 630 messages .
    je cherche une solution efficace SVP
    Pour cette solution que vous avez propose , je pense que c’est la même chose que captcha de Google .
    Merci de me répondre sur mon email :
    hardhicham@gmail.com
    c urgent

  9. Salut moi j’ai ce soucis Il cache son adresse IP, par contre son adresse mail est visible (Russie) Je reçois 10 messages par jour avec de différentes adresses mail. Comment le bloqué ? Pour info j’ai configurer les pays a bloqué rien affaire ?Merci

    1. Hello,

      Il faut utiliser un module reCaptcha pour Prestashop pour bloquer l’attaque voir dans les commentaires plus haut (sinon le robots arrive à passer les règles de sécurités de la géolocalisation).

      A bientôt !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *