Nous aimons les robots !

Stopper les spammeurs (partie 1) : Créer un formulaire d'enregistrement custom

vendredi 28 juin 2013 à 17h20 • catégories : Comment faire ?

La version 1.0.x de ionize vous permet de définir des formulaires facilement.
Malheureusement, ceux-ci sont souvent spammés par des bots nous envoient pléthore de cochonneries.

Nous allons voir comment construire un formulaire d'enregistrement un tantinet protégé.

Comment limiter le spam sur un formulaire

Nous allons essayer de limiter la plupart des spams de formualire en utilisant la technique du "pot de miel".

Cette technique ne garantit pas un résultat à 100%, mais elle reste efficace pour la plupart des bots.

Un champ pot de miel fonctionne de la façon suivante :

  • Le formulaire possède un champ censé resté vide
  • Ce champ est invisible aux visiteurs humains du site
  • Le robot va bêtement remplir ce champ (nous comptons là dessus)
  • La librairie de traitement des données du formulaire va vérifier que ce champ est bien vide

1. Introduction : Comment fonctionne le formulaire d'enregistrement

Si vous êtes impatient, passez directement à la partie 2, ce chapitre s'attachant au fonctionnement des formulaires dans ionize.

Le fichier /application/config/forms.php défini les formulaires utilisés en frontend.
Par exemple, l'entrée suivante défini le formalaire "register" :

$config['forms']['register']

Ce tableau va définir les champs du formulaire, les emails envoyés lorsque le traitement du formulaire abouti, mais aussi la librairie en charge du traitement de ces données.

La librairie traitant le formulaire "register" est :

'register' => array
(
    'process' => 'TagManager_User::process_data',
    ...
)

Nous pouvons regarder cette librairie TagManager_User() en éditant le fichier /application/libraries/Tagmanager/User.php.

La méthode "process_data()" se charge du traitement des données envoyées.

Elle récupère le nom du formulaire via le champ caché "form" :

$form_name = self::$ci->input->post('form');

Puis, en fonction du nom du formulaire, traite les données.
Par exemple, le traitement du formulaire "register" a lieu à partir de la ligne 324 :

case 'register':
    // Do some stuff here …

2. Créer un formulaire d'enregistrement customisé.

L'objectif est de créer un formulaire d'enregistrement customisé, sans toucher au coeur de ionize ni modifier la librairie TagManager_User().

2.1 Éditez ou créez le fichier /themes/<votre_theme>/config/forms.php :

Ce fichier de définition de formulaire permettre de remplacer la définition du formulaire "register" par notre propre définition.

$config['forms'] = array
(
    // formaulaire d'enregistrement
    'register' => array
    (
        // Nous allons créer cette librairie à l'étape suivante !
        'process' => 'TagManager_Register::process_data',
        'redirect' => 'referer',
        'messages' => array(
            'success' => 'form_alert_error_message',
        '    error' => 'form_alert_success_message',
        ),
        // Champs du formulaire
        'fields' => array
        (
            'firstname' => array(
                'rules' => 'trim|required|xss_clean',
                'label' => 'form_label_firstname',
            ),
            'email' => array(
                'rules' => 'trim|required|min_length[5]|valid_email|xss_clean',
                'label' => 'form_label_email',
            ),
            'password' => array(
                'rules' => 'trim|required|min_length[4]|matches[password2]|xss_clean',
                'label' => 'form_label_password',
            ),
            'password2' => array(
                'rules' => 'trim|required|min_length[4]|xss_clean',
                'label' => 'form_label_password_confirmation',
                // If set to FALSE, ths field will not be saved to DB
                'save' => FALSE,
            ),
            // Voici notre pot de miel
            // En fait, nous ne demandons pas à l'utilisateur de saisir sa ville :
            // Ce champ sera vide
                'city' => array(
                'label' => 'form_label_city'
            ),
        ),
    ),
);

2.2 Création de la vue du formulaire

Ce code est à insérer dans une vue page ou article :

<form method="post" action="">

    <input type="hidden" name="form" value="register" />

    <label for="firstname"><ion:lang key="form_label_firstname" /></label>
    <input id="firstname" name="firstname" type="text" value="<ion:form:register:field:firstname />" />
    <ion:form:register:error:firstname tag="p" class="input-error" />

    <label for="email"><ion:lang key="form_label_email" /></label>
    <input type="text" id="email-reg" name="email" value="<ion:form:register:field:email />"/>
    <ion:form:register:error:email tag="p" class="input-error" />

    <label for="password"><ion:lang key="form_label_password" /></label>
    <input type="password" id="password" name="password" value="<ion:form:register:field:password />"/>
    <ion:form:register:error:password tag="p" class="input-error" />

    <label for="password2"><ion:lang key="form_label_password_confirmation" /></label>
    <input type="password" id="password2" name="password2" value="<ion:form:register:field:password2 />"/>
    <ion:form:register:error:password2 tag="p" class="input-error" />

    <div class="city">
        <label for="city">Website</label>
        <input type="text" id="city" name="city" value="<ion:form:register:field:website />"/>
    </div>

    <input type="submit" class="button success" value="<ion:lang key='form_button_register' />" />

</form>

Pour masquer le champ "city" au visiteur, nous ajoutons à notre fichier CSS :

div.city {
    position: absolute;text-indent: -9999em;margin:0 0 0 -9999em;
}

2.3 Préparation de la classe custom TagManager_Register()

Crééz le fichier /themes/<your_theme>/librairies/Tagmanager/Register.php :

<?php

class TagManager_Register extends TagManager
{
    public static function process_data(FTL_Binding $tag)
    {
        if (TagManager_Form::validate('register'))
        {
            $honeypot = self::$ci->input->post('city');

            // SPAM !!!
            if ( ! empty($honeypot))
            {
                $message = TagManager_Form::get_form_message('success');
                TagManager_Form::set_additional_success('register', $message);

                $redirect = TagManager_Form::get_form_redirect();
                if ($redirect !== FALSE) redirect($redirect);
            }
            else
            {
                // Get user's allowed fields
                $fields = TagManager_Form::get_form_fields('register');
                if ( is_null($fields))
                    show_error('No definition for the form "register"');

                $fields = array_fill_keys($fields, FALSE);
                $user = array_merge($fields, self::$ci->input->post());

                // Compliant with User, based on username
                $user['username'] = $user['email'];
                $user['join_date'] = date('Y-m-d H:i:s');

                if ( ! User()->register($user))
                {
                    $message = User()->error();
                    if ( empty($message))
                        $message = TagManager_Form::get_form_message('error');

                    TagManager_Form::set_additional_error('register', $message);
                }
                else
                {
                    // Get the user saved in DB
                    $user = self::$ci->user_model->find_user($user['username']);

                    if (is_array($user))
                    {
                        // Must be set before set the clear password
                        $user['activation_key'] = User()->calc_activation_key($user);

                        $user['password'] = User()->decrypt($user['password'], $user);

                        // Merge POST data for email template
                        $user = array_merge($user, self::$ci->input->post());

                        // Create data array and Send Emails
                        TagManager_Email::send_form_emails($tag, '', $user);

                        $message = TagManager_Form::get_form_message('success');
                        TagManager_Form::set_additional_success('register', $message);

                        // Potentially redirect to the page setup in /application/config/forms.php
                        $redirect = TagManager_Form::get_form_redirect();
                        if ($redirect !== FALSE) redirect($redirect);
                    }
                    else
                    {
                        $message = TagManager_Form::get_form_message('error');
                        TagManager_Form::set_additional_error('register', $message);
                    }
                }
            }
        }
    }
}

Et voilà !
Le formulaire "register" est à présent déclaré via un paramétrage custom et traité par la classe que nous venons de développer.

Bien entendu, ce système n'est pas sûr à 100% et il peut être intéressant de se pencher sur des services comme http://wangguard.com.

Basé sur la même approche de formulaire custom, il serait intéressant d'utiliser un tel service.
Si vous souhaitez l'implémenter sur votre site, n'hésitez pas à partager votre code sur le forum :
http://ionizecms.com/forum/ !