Combattre efficacement le spam avec DSPAM

Magazine
Marque
GNU/Linux Magazine
Numéro
132
|
Mois de parution
novembre 2010
|


Résumé
Vous avez déjà goûté du spam ? Je veux dire, du vrai spam. Celui qui est servi en boîtes rectangulaires et a vaguement la couleur du jambon. On m’en a servi, une fois, lors d’un réveillon, et je peux vous assurer que ce n’est vraiment pas bon ! Mais le spam dont on va parler ici est différent, plus électronique, mais tout aussi dégoûtant.Combattre le spam est probablement la tâche la plus complexe du postmaster. Les techniques sont nombreuses et il est indispensable de les cumuler pour obtenir un résultat pertinent.

Body

Le choix de base en matière d'antispam open source se porte généralement sur SpamAssassin, qui, au fil des ans, a montré son efficacité. Mais depuis presque 2 ans, et la libération de DSPAM en open source, l'intérêt pour ce moteur d'analyse de contenu a grandi... presque au point d'avoir une version à jour dans Debian.

Cet article est un tour d'horizon de DSPAM, des technologies employées et de sa mise en place avec Postfix. Une fois n'est pas coutume, on se basera sur les sources pour l'installation, DSPAM n'ayant pas été intégré dans Squeeze à l'écriture de cet article.

1. Vue d'ensemble

DSPAM a été initialement écrit par Jonathan Zdziarski, un développeur américain, en 2003, à la suite de ses recherches sur la classification du spam. DSPAM a ensuite été vendu en 2006 à la société Sensory Networks, qui n'en a rien fait de pertinent si ce n'est fâcher la communauté par manque de communication, pour finalement, en 2009, rendre les droits au groupe dspam-community et donc publier DSPAM sous GPL.

DSPAM est écrit en C et nécessite un back-end pour stocker les données. Si MySQL, PostgreSQL et Sqlite sont disponibles, il est également possible de se baser sur un hash driver sur disque, sans moteur relationnel. Ce « hash driver » est l'option par défaut que nous allons utiliser, car c'est également le driver le plus rapide accessible dans DSPAM.

L'analyse du spam est réalisée par et pour chaque utilisateur. Ainsi, chaque adresse e-mail du domaine (sous la forme <utilisateur>@<domaine>) disposera de son répertoire personnel contenant les données de DSPAM. Il est toutefois possible de partager des informations de filtrage entre plusieurs utilisateurs sous la forme de groupes. Il y a plusieurs types de groupes, que nous détaillerons plus tard.

1.1 Technologie

Retour en 2002, Paul Graham, un autre développeur américain, publie « A Plan for Spam », un article qui changera la vision d'alors de l'antispam. Au début des antispams, les règles étaient essentiellement basées sur des critères spécifiques au spam, tel que « titre en majuscule » ou « contient 48 points d'exclamation à la suite ». Paul Graham a travaillé sur ce type de règles, qui fonctionnaient plutôt bien, mais le problème était que le faible pourcentage de faux positifs résultant, de l'ordre de 1 a 2 %, était extrêmement difficile à filtrer.

Son idée (que d'autres avaient eu avant lui, mais sans la même résonance) était donc de découper le contenu d'un e-mail en tokens, un token étant typiquement un mot, un composant des en-têtes, une balise HTML, etc., et de calculer des statistiques via l'algorithme de Bayes (dont le détail dépasse la portée de cet article).

Le résultat étant nettement satisfaisant, cette technique est devenue la norme et est au cœur de DSPAM.

Graham a également montré que, pour être réellement efficace, un antispam devait être centré sur un utilisateur en particulier. Utiliser une base globale pour tous les utilisateurs est moins efficace, certains mots couramment utilisés par certains pouvant être considérés comme du spam par d'autres (ceux d'entre vous qui travaillent comme commerciaux pour des sociétés pharmaceutiques en comprendront certainement le principe).

2. Installation

Partons des sources, disponibles sur http://dspam.sourceforge.net/, et de la dernière version disponible à l'écriture de cet article : dspam-3.9.1-RC1.

L'archive contient la documentation, qui manque cruellement sur le wiki. En fait, les quelques 2153 lignes/14433 mots du fichier README forment l'essentiel de ce qu'il faut savoir à propos de DSPAM.

Avant de lancer la compilation, définissons ce que nous souhaitons faire :

1 - DSPAM doit s'interfacer avec Postfix (en tant que content-filter), et va donc recevoir et réinjecter les e-mails via des sockets TCP sur localhost. DSPAM doit donc fonctionner en mode Daemon.

2 - Chaque utilisateur sera identifié via son adresse e-mail complète.

3 - Chaque utilisateur va disposer d'un répertoire propre dans /var/spool/dspam, ce répertoire contiendra son dictionnaire (contenant les tokens et valeurs statistiques associées), les logs d'activité, l'historique qui sera utilisé pour l'apprentissage, etc. On va donc utiliser le hash driver comme back-end de manipulation des données.

DSPAM n'a pas vraiment de dépendances externes, une Debian Squeeze toute fraîche avec quelques outils de compilation suffit à l'installer (littéralement : gcc et make). Avec ces critères, on peut créer un utilisateur dspam et compiler DSPAM de la façon suivante :

$ su

# useradd -r -s /bin/false -U -d /var/spool/dspam dspam

# exit

$ ./configure --enable-daemon --enable-split-configuration --enable-syslog --enable-clamav --enable-preferences-extension --enable-domain-scale --with-dspam-home=/var/spool/dspam --with-dspam-home-owner=dspam --with-dspam-home-group=dspam --with-dspam-owner=dspam --with-dspam-group=dspam --with-storage-driver=hash_drv --prefix=/usr/local/dspam --sysconfdir=/etc/dspam --mandir=/usr/share/man --bindir=/usr/bin --sbindir=/usr/sbin --libdir=/usr/lib --includedir=/usr/include

$ make

$ su

# make install

Les options de compilation sont détaillées dans ./configure --help. Activez les options de debug (--enable-debug --enable-bnr-debug --enable-verbose-debug) lors du ./configure si vous aimez la lecture.

Nous voici avec un répertoire /etc/dspam qui contient dspam.conf, et des binaires et bibliothèques installés dans leurs répertoires respectifs.

3. Configuration de DSPAM

Avant d'alimenter DSPAM avec le flux d'e-mails venant de Postfix, nous allons le configurer et le tester.

Le fichier dspam.conf est livré avec un grand nombre de commentaires, tous n'étant pas faciles à interpréter sans une lecture attentive du README (on y revient) ou de cet article (vous y êtes).

Nous avons déjà dit à DSPAM, lors du ./configure, que l'on voulait utiliser le hash driver et conserver les répertoires des utilisateurs dans /var/spool/dspam. On retrouve donc ces directives au début du fichier de configuration (respectivement StorageDriver et Home).

Comme nous l'avons dit précédemment, nous voulons que les communications avec DSPAM se fassent via des sockets TCP. Pour cela, il faut définir un point d'entrée et un point de sortie dans dspam.conf.

Le point d'entrée recevra les messages venant de Postfix. Il écoute sur le port TCP/10033 (choix arbitraire) et attend de Postfix qu'il lui parle le LMTP (une version allégée du SMTP pour les communications intra-infrastructure).

Les directives de configuration sont les suivantes :

ServerPort              10033

ServerQueueSize         32

ServerPID               /var/run/dspam/dspam.pid

ServerMode              auto

ServerParameters        « --deliver=innocent,spam -d %u »

ServerIdent             « localhost.localdomain »

La directive ServerParameters permet d'obliger DSPAM à réinjecter non seulement les e-mails innocents, mais également les spams. Sur un système en test, il est préférable de tout recevoir dans sa boîte mail et de filtrer sur les messages marqués [SPAM], plutôt que de mettre en quarantaine directement les supposés spams (à noter tout de même qu'il est possible d'envoyer des notifications quotidiennes listant les messages en quarantaine).

Sur le point de sortie, DSPAM va se connecter à Postfix pour lui réinjecter l'e-mail après analyse. Plus tard, on va configurer Postfix pour qu'il écoute sur le port TCP/10034. Pour le moment, on place les directives suivantes dans dspam.conf :

DeliveryHost        127.0.0.1

DeliveryPort        10034

DeliveryIdent       localhost

DeliveryProto       SMTP

Ici, on parle le SMTP et non plus le LMTP.

3.1 Mode d'apprentissage

DSPAM démarre son fonctionnement avec des dictionnaires vides, ce qui signifie que durant les premiers jours (semaines selon la volumétrie), DSPAM ne filtrera rien et apprendra sur tout.

Cela signifie également qu'il sera de la responsabilité des utilisateurs de marquer les e-mails selon qu'ils sont du spam ou non (dans le second cas, sur erreur de DSPAM uniquement). Charge au postmaster de fournir un moyen simple de marquer les e-mails.

Plusieurs modes d'apprentissage existent. Il sont décrits dans man dspam. Celui qui nous intéresse ici se nomme « teft » et forcera DSPAM à apprendre sur chaque e-mail qu'il traitera, innocent comme spam.

Ce mode est particulièrement intensif car il va, pour chaque e-mail, créer ou mettre à jour tous les tokens du message dans le dictionnaire de l'utilisateur. C'est parfait pour un nouvel utilisateur qui a besoin de rapidement se constituer un dictionnaire, mais peut être consommateur de CPU sur un environnement chargé.

Pour appliquer le mode teft, on positionne la directive suivante :

TrainingMode teft

Pour pallier ce problème de performance, d'autres modes d'apprentissage existent. Le mode tum, par exemple, n'apprendra que pendant une période de démarrage et ne modifiera le dictionnaire ensuite que sur réapprentissage.

Ces paramètres pouvant être fixés pour chaque utilisateur séparément, comme nous allons le voir dans les préférences, il est toujours possible d'avoir un mode teft par défaut, comme défini ci-dessus.

3.2 Mode de détection

Nous voici maintenant dans le core de DSPAM : le mode de détection. DSPAM n’étant ni plus ni moins qu’un moteur statistique d’analyse de contenu, son paramétrage passe essentiellement par trois directives :

- le mode de découpage du contenu ;

- l'algorithme statistique ;

- le calcul de probabilité.

Le contenu de ce chapitre est pour beaucoup tiré des explications de Stevan Bajic. Je le remercie au passage d'avoir pris le temps de répondre à mes (nombreuses) questions.

3.2.1 Découpage du contenu

DSPAM appelle cela un tokenizer. C’est le module qui va découper un contenu, en faire un token et stocker son empreinte dans le dictionnaire de l'utilisateur. Ces tokens peuvent être de plusieurs formes selon le mode choisi, le plus basique étant de prendre les mots un par un, chaque mot étant un token diffèrent.

Mais il existe également des modules plus évolués, capables de prendre en considération différentes parties de la phrase. Pour ceux qui aiment la prose germanique, voici comment une phrase sera découpée par les différents modules :

« Heute Abend war ich mit meiner Freundin im Kino und habe viel gelacht ».

Le caractère + désigne une combinaison de mots, le caractère # désigne un mot non pris en compte.

Module WORD : chaque mot représente un token, on a donc 13 tokens.

• TOKEN: ‘Heute’ CRC: 6716984897371635712

• TOKEN: ‘Abend’ CRC: 6670531613365895168

• TOKEN: ‘war’ CRC: 4772677679197454336

• TOKEN: ‘ich’ CRC: 6329956816985784320

[...]

Module CHAIN : le mot est lié au mot qui le suit, on a donc un token de moins, soit 12 tokens.

• TOKEN: ‘Heute+Abend’ CRC: 9299536586222406967

• TOKEN: ‘Abend+war’ CRC: 5205867775940263209

• TOKEN: ‘war+ich’ CRC: 6329956649787979024

• TOKEN: ‘ich+mit’ CRC: 5158416839735805488

[...]

Module OSB (Orthogonal Sparse biGram) : pour chaque mot, on crée une fenêtre glissante de 5 mots autour du mot. On va donc associer le mot en cours avec un voisin dans un rayon de -4/+4 positions autour du mot. Cela crée un total de 55 tokens, la formule étant ((nb_mots - taille_fenetre)*taille_fenetre) + ((taille_fenetre - 1 * taille_fenetre)/2).

• TOKEN: ‘Heute+#+#+#+mit’ CRC: 2006452661602586241

• TOKEN: ‘Abend+#+#+mit’ CRC: 5482652074219693289

• TOKEN: ‘war+#+mit’ CRC: 15707817493435847227

• TOKEN: ‘ich+mit’ CRC: 5158416839735805488

• TOKEN: ‘Abend+#+#+#+meiner’ CRC: 8544044731047037263

• TOKEN: ‘war+#+#+meiner’ CRC: 14722667808637756004

[...]

Module SBPH (Sparse Binary Polynomial Hashing) : similaire à OSB, mais plus complet, car on va ici utiliser une fenêtre glissante de 5 mots, mais également considérer les mots intermédiaires dans la fenêtre, et pas seulement les ignorer (représenté par un ‘#’ dans OSB). On a donc 159 tokens, la formule étant (nb_mots - ( taille_fenetre -1))*(2^( taille_fenetre -1))+(((taille_fenetre-1)^2)-1).

• TOKEN: ‘mit’ CRC: 5158417007107899392

• TOKEN: ‘ich+mit’ CRC: 5158416839735805488

• TOKEN: ‘war+#+mit’ CRC: 15707817493435847227

• TOKEN: ‘war+ich+mit’ CRC: 6905336139605378569

• TOKEN: ‘Abend+#+#+mit’ CRC: 5482652074219693289

• TOKEN: ‘Abend+#+ich+mit’ CRC: 2006454003823721484

Évidemment, avec 159 tokens produits par SBPH, contre seulement 12 pour CHAIN ou WORD, le volume du dictionnaire n’est pas le même.

Mais l'énorme avantage des tokenizers comme OSB et SBPH est qu'ils peuvent identifier des phrases qu'ils n'ont jamais vu auparavant, et ce, en utilisant la combinaison (caractère +) et le saut (caractère #).

Par exemple, imaginons le token Buy+#+Viagra. Cet unique token est capable d'identifier des phrases du type :

- Buy cheep Viagra ;

- Buy good Viagra ;

- Buy herbal Viagra ;

- Buy exclusive Viagra ;

- Buy boosting Viagra ;

- Buy fresh Viagra ;

- Buy qualitative Viagra.

Alors que dans la même situation, le tokenizer WORD serait capable d'identifier les mots individuellement, mais pas leur combinaison. Et le tokenizer CHAIN ne verrait quasiment rien...à moins d'avoir toutes les combinaisons dans le dictionnaire.

SBPH possède également un mécanisme de poids associé aux tokens. Ainsi, un token possédant 5 mots aura un poids beaucoup plus important qu'un token ne possédant qu'un seul mot, selon la formule : poids = 2^(2*n), avec « n » représentant le nombre de mots voisins pris en compte.

En jonglant toujours avec notre prose germanique, la table de poids pour la phrase « Heute Abend war ich mit » est la suivante :

Token

Poids

Heute

1

Heute+Abend

4

Heute+#+war

4

Heute+Abend+war

16

Heute+#+#+ich

4

Heute+Abend+#+ich

16

Heute+#+war+ich

16

Heute+Abend+war+ich

64

Heute+#+#+#+mit

4

Heute+Abend+#+#+mit

16

Heute+#+war+#+mit

16

Heute+Abend+war+#+mit

64

Heute+#+#+ich+mit

16

Heute+Abend+#+ich+mit

64

Heute+#+war+ich+mit

64

Heute+Abend+war+ich+mit

256

Le poids correspondant est ensuite utilisé pour multiplier l'impact du token lors du calcul de probabilité.

Pour la configuration présentée dans cet article, nous nous contenterons du tokenizer OSB. Mais que cela ne vous empêche pas d'expérimenter avec SBPH.

La directive à placer dans dspam.conf est donc la suivante :

Tokenizer osb

3.2.2 Algorithme statistique

Avec tous ces tokens, la difficulté est de déterminer lesquels influencent la décision et dans quelles proportions. Comme nous l’avons dit, DSPAM n’est pas livré avec un dictionnaire pré-rempli. Il ne sait pas dire immédiatement si le token Abend+#+#+mit est pertinent pour déterminer si le message est un spam ou non. Mais il apprend, et au fur et à mesure de son apprentissage, il va moduler les probabilités associées à ces tokens et appliquer cela aux nouveaux messages.

Au-delà du pur calcul de probabilité, l'algorithme statistique permet de définir des critères à prendre en compte lors du calcul. DSPAM nous donne le choix entre plusieurs algorithmes statistiques que sont :

- naive : Naive-Bayesian (All Tokens) ;

- graham : Graham-Bayesian (« A Plan for Spam ») ;

- burton : Burton-Bayesian (SpamProbe) ;

- chi-square : Fisher-Robinson's Chi-Square Algorithm.

Il est également possible de cumuler ces algorithmes. Une combinaison graham+burton montre généralement un bon ratio faux positifs/faux négatifs. C’est donc ce que nous allons utiliser, via la directive suivante :

Algorithm graham burton

Mais faisons un petit retour en arrière pour bien comprendre la mécanique derrière cette directive. L'approche naïve (de l'algorithme du même nom) considère l'ensemble des tokens composant un message. Chaque token est initialisé avec une valeur statistique neutre, soit 0.5 qui signifie donc « ni spam ni ham » (proche de 1 égal spam). Mais le problème avec ce fonctionnement naïf, c'est qu'un spammeur peut inclure un long texte contenant des mots tout à fait communs (« et », « bonjour », ...), et une ou deux phrases contenant le message de spam, et l'algorithme va traiter l'ensemble au même niveau, permettant au texte « support » de réduire la probabilité finale du message d'être un spam.

Cette approche a été discutée par Paul Graham, toujours lui, pour démontrer qu'une approche plus optimale était possible. Ainsi, l'algorithme de Graham utilise les critères suivants :

1 - Analyser le message et sélectionner les 15 tokens les plus pertinents. Les tokens choisis sont ceux qui ont la plus forte déviation de la probabilité neutre 0.5.

2 - Ignorer les tokens qui ont été vus moins de 5 fois par le passé.

3 - Ne prendre les tokens qu'une seule fois. Si un token est présent deux fois dans le message, la seconde occurrence ne sera pas prise en compte dans le calcul.

4 - Lors de l'ajout de nouveaux tokens, définir une probabilité initiale de 0.4 au lieu de 0.5. Cela permet de préjuger un token comme innocent.

Burton, de Brian Burton, utilise une version modifiée de Graham. Le nombre de tokens considérés passe de 15 à 27, et si un token pertinent est présent plusieurs fois, alors il sera pris en compte plusieurs fois. Son algorithme a été intégré dans l'antispam SpamProbe.

Toujours dans la mouvance du début des années 2000, Gary Robinson publia en 2003 dans Linux Journal sa version améliorée de l'algorithme de Graham. Sa propre version est au cœur du projet SpamBayes, un autre moteur de classification, et présente une approche plus performante pour traiter les tokens qui apparaissent rarement. Son approche est basée sur le test statistique Chi-Square, d'où le nom de la directive dans DSPAM.

Difficile de dire lequel de ces algorithmes est le plus pertinent. Tous obtiennent d'excellents résultats, libre à vous d'expérimenter avec celui de votre choix.

3.2.3 Calcul de probabilités

Nous avons donc des tokens dont la valeur initiale est 0.4 en utilisant l'algorithme de Graham, et des critères de calcul permettant d'analyser les messages.

La dernière étape consiste à calculer la probabilité qu'un message soit un spam ou non. C'est ce que DSPAM appelle la Pvalue et il fournit trois algorithmes pour réaliser ce calcul.

Ces algorithmes statistiques sont markov (du mathématicien russe Andrey Markov), robinson (de Gary Robinson) et bcr (Bayesian Chain Rule, de Paul Graham).

L'algorithme standard, celui souvent pris en exemple, est bcr, Bayesian Chain Rule, qui est également l'algorithme décrit par Paul Graham dans son article « A Plan for Spam ». C'est donc celui-ci que nous allons utiliser.

Pvalue bcr

3.2.3.1 Le filtrage Bayesien

Dès que l'on parle de technologie antispam, les travaux de Thomas Bayes sont systématiquement cités. Le théorème de Bayes permet de calculer la probabilité de survenance d'un événement sur sa survenance constatée par le passé, en d'autres termes : sur l'expérience.

En ce qui nous concerne, la formule qui permet de calculer la probabilité qu'un message soit un spam ou non est la suivante : P = S / (S + H)

Avec :

- P : la (P)robabilité que le message soit un spam

- S : produit des probabilité associées aux tokens composants le message

   -- soit : P(token-1) * P(token-2) * … * P(token-n)

- H : inverse des probabilités des tokens

   -- soit : (1 - P(token-1)) * (1-P(token-2)) * … * (1-P(token-n))

Comme on l'a vu, lorsqu'un token est ajouté dans le dictionnaire, il prend une valeur par défaut (0.4 avec Graham). Puis, chaque fois que DSPAM apprend un message contenant le même token, il modifie cette valeur. Ainsi, si le mot « Viagra » est présent dans 10 messages, dont 9 spams, la probabilité associée à ce token sera : P(Viagra) = 9 / (9+1) = 0.9.

Prenons un exemple. Considérons le message « Hi! Buy Viagra ». On va appliquer à ce message un tokenizer de type WORD (plus simple à manipuler pour l'exercice).

La première chose que fait le tokenizer est de supprimer les caractères non pris en compte, comme le point d'exclamation. Le message est donc « Hi Buy Viagra ».

Chaque mot est un token à part entière (c'est le fonctionnement de WORD), on peut donc imaginer que le dictionnaire de l'utilisateur est dans l'état suivant :

Token

Nb de Spam (s)

Nb de Ham (h)

Probabilité

p=s/(s+h)

Hi

25

62

0,29

Buy

157

87

0,64

Viagra

231

11

0,95

On peut finalement calculer la Pvalue du message avec la formule de Bayes :

S = 0.29*0.64*0.95=0.176

H = (1-0.29)*(1-0.64)*(1-0.95) = 0.71 * 0.36 * 0.05 = 0.0127

Pvalue = S / (S+H) = 0.176 / (0.176 + 0.0127) = 0.93

Donc, la probabilité finale que ce message soit un spam est de l'ordre de 93 %.

3.2.3.2 Markov

Toutefois, dans le cas particulier où le tokenizer est SBPH, il est possible d'utiliser la notion de poids des tokens dans le calcul statistique. C'est ce que fait la méthode markov, qui ne fonctionne donc que si SBPH est activé (et aucun autre, pas même OSB).

Donc, en pratique, markov est une amélioration de bcr, qui permet de dire que si un token est très précis (par exemple, 5 mots sur 5), alors son impact dans le calcul est très important (256 fois plus important qu'un token n'ayant qu'un mot). La valeur de poids d'un token est ainsi utilisée pour multiplier l'importance de la probabilité associée au token dans le calcul global.

3.2.3.3 Confiance

DSPAM exporte une valeur de confiance par rapport au résultat produit. La confiance est calculé en fonction de la probabilité que le message soit un spam ou non.

Quand le message est innocent, plus la probabilité associée est basse (proche de zéro), plus DSPAM est confiant dans son résultat : la confiance est élevée. Ainsi, si le message est innocent, confiance égal (1 – probabilité) (exemple : probabilité = 0.0184 ; confiance = 1 – 0.0184 = 0. 9816).

Quand le message est un spam, plus la probabilité associée est élevée (proche de un), plus DSPAM est confiant dans son résultat. Ainsi, si le message est un spam, confiance égal probabilité.

Ce sera tout pour les mathématiques. Si le sujet vous passionne, n'hésitez pas à poser vos questions sur la mailing list de DSPAM, les gentils développeurs ne manqueront pas de vous envoyer tous les papiers de recherche correspondants.

3.3 Volume des dictionnaires

Les Tokens que nous allons générer prennent de la place, beaucoup de place. DSPAM permet de configurer la taille du fichier de Hash que chaque utilisateur remplira au fur et à mesure (son dictionnaire), et avec un tokenizer comme OSB, il faut prévoir assez large.

Par exemple, un compte plutôt actif, recevant entre 200 et 300 messages par jour, arrivera au alentour de 2.5 millions de tokens en l'espace de deux semaines. Évidemment, cette valeur va énormément varier selon que les messages contiennent des tokens similaires ou non.

En positionnant la valeur de HashRecMax à plus de 6 millions d’entrées, on donne un peu de marge de manœuvre à DSPAM, mais on va toutefois lui laisser la possibilité d’augmenter cette valeur jusqu’à 16 millions (par palier de 50000), juste au cas où.

HashRecMax              6291469

HashAutoExtend          on

HashMaxExtents          10000000

HashExtentSize          49157

Cela signifie également que le fichier de hash d’un utilisateur sera initialisé avec une taille proche de 100 Mo ! Ca peut être gênant sur un système gérant un grand nombre d’utilisateurs.

3.4 Whitelist

DSPAM a la possibilité d’observer les émetteurs de messages pour un destinataire donné, et de positionner en whitelist les émetteurs qui ont envoyé plus de 20 messages sans qu’aucun n’ait été marqué comme spam. Cette fonction, plutôt pratique, n’a pas besoin d’autre configuration que la directive :

Feature whitelist

3.5 Les préférences

Chaque utilisateur peut déterminer son jeu de préférences via l’interface web que nous installerons plus tard. Il est toutefois possible de positionner des valeurs par défaut que l’utilisateur pourra ou non modifier.

Par exemple, la configuration par défaut ne délivre pas les spams aux utilisateurs, mais les placent en quarantaine. Pour changer ce comportement, il faut modifier les directives suivantes :

Preference « spamAction=tag »     #{ quarantine | tag | deliver } -> default:quarantine

Preference « spamSubject=[SPAM] » # { string } -> default:[SPAM]

Preference « tagSpam=on »         #{ on | off }

Preference « tagNonspam=off »     #{ on | off }

On laisse toutefois la possibilité aux utilisateurs de modifier ces options (dans l’interface web) via les directives :

AllowOverride spamAction

AllowOverride spamSubject

AllowOverride tagSpam

AllowOverride tagNonspam

Il est également possible d’enlever la signature DSPAM du corps des messages via la préférence :

Preference « signatureLocation=message » #{ message | headers } -> default:message

Dans la mesure où cette dernière se fait rapidement oublier et permet de réapprendre les spams sur un simple forward, comme nous allons le voir juste après, il est préférable de conserver la signature dans le corps des messages.

3.6 dspam.conf

En résumé, votre fichier de configuration final devrait ressembler au listing ci-dessous. De nombreuses options sont encore configurables, mais pour un rapide tour d’horizon, cette configuration est fonctionnelle.

Home /var/spool/dspam/

StorageDriver /usr/lib/dspam/libhash_drv.so

TrustedDeliveryAgent « /usr/bin/procmail »

DeliveryHost            127.0.0.1

DeliveryPort            10034

DeliveryIdent           localhost

DeliveryProto           SMTP

OnFail error

Trust root

Trust dspam

TrainingMode teft

TestConditionalTraining on

Feature whitelist

Feature tb=5

Algorithm graham burton

Tokenizer osb

Pvalue bcr

WebStats on

Preference « trainingMode=TEFT »

Preference « spamAction=tag »

Preference « spamSubject=[SPAM] »

Preference « statisticalSedation=5 »

Preference « enableBNR=on »

Preference « enableWhitelist=on »

Preference « signatureLocation=message »

Preference « tagSpam=on »

Preference « tagNonspam=off »

Preference « showFactors=on »

Preference « optIn=off »

Preference « optOut=off »

Preference « whitelistThreshold=20 »

Preference « makeCorpus=off »

Preference « storeFragments=off »

Preference « localStore= »

Preference « processorBias=on »

Preference « fallbackDomain=off »

Preference « trainPristine=off »

Preference « optOutClamAV=off »

Preference « ignoreRBLLookups=off »

Preference « RBLInoculate=off »

Preference « notifications=on »

AllowOverride enableBNR

AllowOverride enableWhitelist

AllowOverride fallbackDomain

AllowOverride ignoreGroups

AllowOverride ignoreRBLLookups

AllowOverride localStore

AllowOverride makeCorpus

AllowOverride optIn

AllowOverride optOut

AllowOverride optOutClamAV

AllowOverride processorBias

AllowOverride RBLInoculate

AllowOverride showFactors

AllowOverride signatureLocation

AllowOverride spamAction

AllowOverride spamSubject

AllowOverride statisticalSedation

AllowOverride storeFragments

AllowOverride tagNonspam

AllowOverride tagSpam

AllowOverride trainPristine

AllowOverride trainingMode

AllowOverride whitelistThreshold

AllowOverride dailyQuarantineSummary

AllowOverride notifications

HashRecMax              6291469

HashAutoExtend          on

HashMaxExtents          10000000

HashExtentSize          49157

HashPctIncrease         10

HashMaxSeek             10

HashConnectionCache     10

Notifications   on

PurgeSignatures 14

PurgeNeutral    90

PurgeUnused     90

PurgeHapaxes    30

PurgeHits1S     15

PurgeHits1I     15

LocalMX 127.0.0.1

SystemLog       on

UserLog         on

Opt out

ServerHost              127.0.0.1

ServerPort              10033

ServerQueueSize 32

ServerPID               /var/run/dspam.pid

ServerMode auto

ServerParameters        « --deliver=innocent,spam -d %u »

ServerIdent             « localhost.localdomain »

ProcessorURLContext on

ProcessorBias on

StripRcptDomain off

4. Un petit test qui ne va pas marcher

Pour lancer le démon sous l’utilisateur dspam, la méthode standard Debian est de passer par start-stop-daemon, de la façon suivante :

# start-stop-daemon --start --chuid dspam --exec /usr/bin/dspam -- --daemon

DSPAM crée son pid automatiquement dans /var/run. Assurez-vous que l’utilisateur dspam peut écrire dans ce répertoire.

On obtient un processus démarré et un port en écoute :

UID        PID PPID CSTIME TTY          TIME CMD

dspam    27473     1 003:26 pts/0    00:00:00 /usr/bin/dspam --daemon

Proto Recv-Q Send-Q Local Address    Foreign Address State User Inode PID/Program name

tcp     0            0          127.0.0.1:10033 0.0.0.0:*           LISTEN 999 18244 27473/dspam

Le démon répond bien aux requêtes sur ce port, voyons donc ce qu’il se passe lorsque l’on tente de lui envoyer un e-mail :

$ nc localhost 10033

220 DSPAM LMTP 3.9.1 Ready

lhlo mail

250-localhost.localdomain

250-PIPELINING

250-ENHANCEDSTATUSCODES

250-8BITMIME

250 SIZE

mail from:<jp.troll@gmail.com>

250 2.1.0 OK

rcpt to:<jean-kevin@debian.lab>

250 2.1.5 OK

data

354 Enter mail, end with « . » on a line by itself

From: Jean-Pierre Troll <jp.troll@gmail.com>

To: Jean-Kevin De La Motte <jean-kevin@debian.lab>

Subject: This is Not a Spam

might be a troll, but a spam... no!

.

421 4.3.0 <jean-kevin@debian.lab> Unable to connect to server quit

221 2.0.0 OK

DSPAM prend bien en compte notre message, mais semble avoir des difficultés à l’envoyer à destination, ce qui est tout à fait normal, car nous n’avons pas configuré Postfix pour qu’il reprenne les messages traités par DSPAM.

Toutefois, jetons un coup d’œil au répertoire Home de DSPAM. Ce dernier a créé une arborescence pour l’utilisateur qui vient d’arriver dans /var/spool/dspam/data/debian.lab/jean-kevin/ :

# tree -s

.

+-- [         23] data

¦   +-- [         23] debian.lab

¦       +-- [        114] jean-kevin

¦           +-- [ 100663544] jean-kevin.css

¦           +-- [          0] jean-kevin.lock

¦           +-- [         85] jean-kevin.log

¦           +-- [         40] jean-kevin.sig

¦           ¦   +-- [        384] 4c873bcd274731106759975.sig

¦           +-- [         12] jean-kevin.stats

+-- [          6] log

+-- [        115] system.log

Regardons de plus près ces fichiers. On a un fichier jean-kevin.css, dont la taille, 100 Mo, nous rappelle fortement celle spécifiée pour le fichier de hash dans dspam.conf : c'est le dictionnaire.

Ensuite, le fichier jean-kevin.log contient la liste des messages traités. On y retrouve la trace de notre message :

# cat jean-kevin.log

1283931466 I Jean-Pierre Troll <jp.troll@gmail.com> 4c873d4a274731062016872 This is Not a Spam Delivered

Chaque ligne a 6 colonnes : un timestamp au format UNIX, un statut d’inspection (Inspected, Whitelisted, ...), l’émetteur, la signature, le sujet du message, et enfin, le statut DSPAM. Dans notre cas, le message est marqué Delivered car, malgré l’erreur de connexion vers Postfix, DSPAM considère ce message comme valide.

Le répertoire jean-kevin.sig contient un fichier dont le nom est la signature du message traité. Cette signature est en fait localisée en trois points : dans le log que nous venons de voir, dans le répertoire de signatures de l'utilisateur, et dans le corps du messages reçu par l'utilisateur.

Lorsque Jean-Kevin veux réapprendre un message, DSPAM va prendre la signature, rechercher un fichier ayant comme nom cette signature dans le répertoire jean-kevin.sig et mettre à jour les tokens contenus dans jean-kevin.css à partir de ce fichier.

Cette configuration de DSPAM est donc fonctionnelle, passons maintenant à la communication avec Postfix.

5. Configurer Postfix pour communiquer avec DSPAM

Postfix a une méthode générique pour communiquer avec les logiciels comme DSPAM. Il les traite en tant que Content-Filter, et peut très facilement faire suivre un message reçu vers un content-filter via son fichier master.cf.

Sur une configuration vierge de Postfix, on peut ajouter le content-filter directement dans le service SMTP principal (celui qui écoute sur le port TCP/25). Pour cela, il faut modifier /etc/postfix/master.cf comme suit :

# Postfix master process configuration file. For details on the format

# of the file, see the master(5) manual page (command: « man 5 master »).

#

# ==========================================================================

# service type private unpriv chroot wakeup maxproc command + args

#               (yes)   (yes) (yes)   (never) (100)

# ==========================================================================

smtp      inet n       -       -       -       -       smtpd

-o content_filter=lmtp:127.0.0.1:10033

Cela suffit pour que Postfix envoie les mails entrants vers DSPAM. Toutefois, pour configurer la route retour, il faut, cette fois, ouvrir un nouveau service dans master.cf, qui écoutera en SMTP sur le port TCP/10034. On mettra cette fois les nouvelles lignes à la fin du fichier master.cf.

127.0.0.1:10034 inet n -       n       -        -      smtpd

-o content_filter=

 -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks

 -o smtpd_helo_restrictions=

 -o smtpd_client_restrictions=

 -o smtpd_sender_restrictions=

 -o smtpd_recipient_restrictions=permit_mynetworks,reject

 -o mynetworks=127.0.0.0/8

 -o smtpd_authorized_xforward_hosts=127.0.0.0/8

Il faut ensuite recharger Postfix avec postfix reload. La réception d’e-mail devrait maintenant fonctionner. Recommencez le test précédent avec netcat sur localhost, et vous devriez recevoir le message. Pour déboguer, pensez aux fichiers suivants (sous Debian) :

- /var/log/mail.info contient tous les logs relatifs aux processing d'e-mails :

- /var/spool/dspam/system.log contient l'activité globale de DSPAM (une ligne par message traité) ;

- si vous avez compilé avec le mode debug, activez Debug * dans dspam.conf et vous aurez des logs détaillés dans /var/spool/dspam/log/ ;

- et, dans le pire des cas, tcpdump -s 16436 -SvnXi lo tcp and port 10033 (ou 10034) pour écouter la communication entre Postfix et DSPAM.

Une fois le mail passé par Postfix, puis DSPAM, puis Postfix, il devrait être reçu par le destinataire sous la forme suivante :

From jp.troll@gmail.com

Wed Sep 804:02:27 2010

Return-Path: <jp.troll@gmail.com>

X-Original-To: jean-kevin@debian.lab

Delivered-To: jean-kevin@debian.lab

From: Jean-Pierre Troll <jp.troll@gmail.com>

To: Jean-Kevin De La Motte <jean-kevin@debian.lab>

Subject: This is Not a Spam

Date: Wed, 8Sep 2010 03:56:49 -0400 (EDT)

X-DSPAM-Result: Innocent

X-DSPAM-Processed: Wed Sep 804:02:27 2010

X-DSPAM-Signature: 4c874313289291828119542

might be a troll, but a spam... no!

!DSPAM:4c874313289291828119542!

code

Le message est innocent, comme indiqué dans X-DSPAM-Result.

X-DSPAM-Probability nous indique la probabilité que le message soit un spam (plus la valeur est proche de 1, plus la probabilité d’être un spam est forte).

Enfin, X-DSPAM-Confidence indique le niveau de confiance du filtre.

Si vous voulez un peu plus de détails sur les tests réalisés et les tokens pris en compte, activez la préférence showFactors=on. C’est verbeux, mais instructif. Chaque token est alors listé avec la valeur statistique associée.

X-DSPAM-Factors: 27,

 To*La+#+#+kevin, 0.01000,

 Subject*This+#+#+a, 0.01000,

To*La+#+<jean, 0.01000,

 To*Kevin+#+La, 0.01000,

 To*Motte+<jean, 0.01000,

[...]

Le corps du mail contient également la signature sous la forme !DSPAM:<signature>!. On l'a déjà dit, il est préférable de conserver la signature dans le corps des message car, de cette façon, elle n’est pas supprimée lors des éventuels forwards pour apprentissage. L'autre option serait de placer la signature dans les headers uniquement, mais ces derniers sont généralement supprimés lors d'un forward, DSPAM ne retrouvera donc pas ses petits.

6. Gérer les faux positifs et les faux négatifs

Évidemment, il ne faut pas attendre de DSPAM qu’il apprenne tout parfaitement, tout de suite. Il faut l’alimenter.

Tout d’abord, il est possible de le faire via la ligne de commandes et la signature du message. Ainsi, en reprenant notre message précédent, on peut le signaler comme spam via la commande :

# dspam --source=error --class=spam --user jean-kevin@debian.lab --signature=’4c874313289291828119542’

Dans les logs de l’utilisateur, on verra alors que le message portant cette signature a été retrained, soit réappris en fonction de la classe spécifiée : spam ou innocent.

# tail -n 1 jean-kevin.log

1283934571 M <None Specified> 4c874313289291828119542 <None Specified> Retrained

Cette solution n’étant pas la meilleure lorsque l’on a 15000 utilisateurs, il est possible de faire mieux : forwarder les e-mails à {spam|notspam}-<user>@<domaine> ou passer par l’interface web. Les deux étant laissés à la main de l’utilisateur.

6.1 Apprentissage en mode forward

L’apprentissage en mode forward fonctionne de la manière suivante : lorsque DSPAM inspecte un message, il positionne une signature dans le corps du message. Un utilisateur peut donc faire suivre (forward) le même message à DSPAM pour lui indiquer qu’il a pris la mauvaise décision : le supposé spam est en fait innocent, ou l’inverse.

Pour cela, DSPAM a besoin de deux choses : la signature du message et l’identité de l’utilisateur.

La signature permet à DSPAM de retrouver le message dans son historique et d’enregistrer le changement d’état. Sans cette signature, DSPAM n’a pas la possibilité d’identifier le message dans son historique. (Note : l’historique est par défaut conservé 14 jours, c’est la directive PurgeSignatures).

L’identité de l’utilisateur peut être automatiquement déduite par DSPAM. On va ajouter un préfixe a l’adresse e-mail de l’utilisateur sous la forme {spam|notspam)-<adresse email>. Notre utilisateur jean-kevin@debian.lab aura donc deux alias spam-jean-kevin@debian.lab et notspam-jean-kevin@debian.lab, qui seront dédiés au réapprentissage.

DSPAM dispose d’une fonctionnalité pour réapprendre automatiquement quand un e-mail est délivré à ces alias. En fait, pour chaque message entrant, il va regarder le champ To: du corps du message, et si ce dernier contient {spam|notspam}, il va découper le contenu et déclencher un retrain. La configuration de cette fonction est tout à fait basique, elle passe par les trois directives suivantes du fichier dspam.conf :

ParseToHeaders on

ChangeModeOnParse on

ChangeUserOnParse full

La directive ParseToHeaders informe DSPAM qu’il doit découper le champ To: du mail reçu pour déterminer si le message contient les mots-clés {spam|notspam}. Ce champ To: fait partie du corps du message, à ne pas confondre avec la commande SMTP rcpt to.

Le parsing activé, DSPAM peut changer de mode d’apprentissage en fonction de la première partie du champ To:. Cela est contrôlé par ChangeModeOnParse, qui va donc activer la classe spam si l’adresse est spam-*, et la classe innocent si l’adresse est notspam-*.

Enfin, ChangeUserOnParse permet de dire que la partie restante de l’adresse e-mail contient l’identifiant DSPAM de l’utilisateur. En le positionnant à Full, on dit a DSPAM de prendre le nom d’utilisateur et le domaine comme identifiant, soit jean-kevin@debian.lab.

Il faut maintenant dire à Postfix que les utilisateurs spam-jean-kevin@debian.lab et notspam-jean-kevin@debian.lab existent. Dans un environnement de production, vous aurez certainement une base SQL ou un LDAP pour gérer les alias, mais dans notre cas, on va se contenter de créer deux entrées dans /etc/aliases. Ce sera suffisant pour le test.

# vim /etc/aliases

[...]

spam-jean-kevin: jean-kevin

notspam-jean-kevin: jean-kevin

# postalias /etc/aliases

On peut maintenant se reconnecter à Postfix via netcat et réinjecter le même e-mail que précédemment, en changeant toutefois le destinataire. Les headers peuvent être oubliés, les sections importantes étant le champ To: et la signature DSPAM à la fin du corps du message.

$ nc localhost 25

220 debian.lab ESMTP Postfix (Debian/GNU)

ehlo mail

250-debian.lab

250-PIPELINING

250-SIZE 10240000

250-VRFY

250-ETRN

250-STARTTLS

250-ENHANCEDSTATUSCODES

250-8BITMIME

250 DSN

mail from:<jean-kevin@debian.lab>

250 2.1.0 Ok

rcpt to:<spam-jean-kevin@debian.lab>

250 2.1.5 Ok

data

354 End data with <CR><LF>.<CR><LF>

From: Jean-Kevin De La Motte <jean-kevin@debian.lab>

To: <spam-jean-kevin@debian.lab>

Subject: This is Not a Spam

might be a troll, but a spam... no!

!DSPAM:4c874313289291828119542!

250 2.0.0 Ok: queued as 42509114E28

quit

221 2.0.0 Bye

console

Regardons maintenant les logs de Jean-Kevin dans DSPAM, on y voit que le message a été « retrained ».

1283936972      M       Jean-Kevin De La Motte <jean-kevin@debian.lab> 4c874313289291828119542      This is Not a Spam      Retrained <20100908090905.42509114E28@debian.lab>

DSPAM va ensuite laisser le message continuer son chemin, il retournera donc vers l'utilisateur (le préfixe est supprimé). Un texte est toutefois ajouté à la fin du message pour informer l'utilisateur que le message a fait l'objet d'un réapprentissage.

Il faut créer ce texte manuellement. Un pour le spam, un pour le ham. Cela peut être fait de la façon suivante :

# echo 'Scanned and tagged as SPAM by DSPAM on Debian.Lab' > /var/spool/dspam/txt/msgtag.spam

# echo 'Scanned and tagged as HAM by DSPAM on Debian.Lab' > /var/spool/dspam/txt/msgtag.nonspam

6.2 Apprentissage par l'interface web

L’utilisation de l’interface web est indispensable lorsque les messages détectés comme spams ne sont pas délivrés aux utilisateurs, mais mis en quarantaine (Préférence spamAction=quarantine). Les utilisateurs doivent alors régulièrement consulter l’interface pour vérifier qu’aucun faux positif ne se trouve en quarantaine. Ils peuvent donc également utiliser l’interface pour marquer les e-mails comme spams.

Les sources de DSPAM fournissent un répertoire nommé « webui ». Il s’agit d’un ensemble de scripts CGI pour contrôler le travail de DSPAM au travers d’une interface web. Pas de surprise, c’est écrit en Perl. Pour l’exécuter, il faudra donc configurer {Apache|lighttpd|nginx|<ton serveur web ici>} pour exécuter des scripts Perl en CGI.

Comme la doc existe déjà pour Apache et lighttpd, j’ai choisi de décrire la configuration pour Nginx.

En fait, c’est un plus compliqué que cela, car le CGI doit être capable de déterminer l’identité de l’utilisateur qui se connecte. Donc, Nginx, dans notre cas, va devoir authentifier l’utilisateur et faire suivre son identité à DSPAM.

Nginx ne sait pas exécuter de scripts externes. La seule chose qu’il sait faire, c’est envoyer des requêtes FastCGI vers un socket. On va donc avoir besoin d’un autre programme, qui va se placer entre Nginx et nos scripts CGI pour exécuter ces derniers, ce programme se nomme fcgiwrap.

On va également avoir besoin de quelques packages Perl nécessaires aux CGI de DSPAM (pour parser du HTML, afficher des graphes avec GD, etc.).

Donc, en une ligne, nous installons les packages suivants :

# aptitude install nginx fcgiwrap libcgi-pm-perl libhtml-parser-perl libgd-graph-perl libgd-graph3d-perl

L’interface de DSPAM a besoin des droits pour accéder aux données dans /var/spool/dspam, en lecture comme en écriture, puisque nous allons modifier les préférences et les états des dictionnaires. Comme c’est fcgiwrap qui va exécuter les scripts, nous allons lancer celui-ci sous l’utilisateur/groupe dspam.

On va également donner les droits en écriture à tout le monde dans le socket de fcgiwrap pour que Nginx puisse y écrire.

Il s’agit ici d’une configuration de test, comme dit le proverbe « Don’t do this at home ».

# vim /etc/init.d/fcgiwrap

[..]

FCGI_USER= »dspam »

FCGI_GROUP= »dspam »

[...]

# /etc/init.d/fcgiwrap restart

# chmod o+w /var/run/fcgiwrap.socket

La configuration de Nginx est ensuite rapide, on fait suivre les requêtes CGI vers fcgiwrap. Il faut également authentifier les utilisateurs afin que DSPAM détermine l’identité du visiteur. Cette identité est stockée dans la variable REMOTE_USER qui est fournie à fcgiwrap.

# vim /etc/nginx/sites-available/default

[...]

 location /dspam/cgi-bin {

   auth_basic      « DSPAM »;

   auth_basic_user_file /var/www/dspam/passwords;

   include /etc/nginx/fastcgi_params;

   index dspam.cgi;

   fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

   fastcgi_param REMOTE_USER $remote_user;

   if ($uri ~ « \.cgi$ »){

     fastcgi_pass unix:/var/run/fcgiwrap.socket;

}

 }

# /etc/init.d/nginx restart

Il faut ensuite créer un fichier /var/www/dspam/passwords via l’outil htpasswd. Ce fichier devra contenir une ligne par utilisateur, l’identifiant étant l’adresse e-mail complète.

# htpasswd -c /var/www/dspam/passwords jean-kevin@debian.lab

New password:

Re-type new password:

Adding password for user jean-kevin@debian.lab

# cat /var/www/dspam/passwords

jean-kevin@debian.lab:H2CigqsDz1U4E

# chown dspam:www-data /var/www/dspam/passwords

# chmod o-rwx /var/www/dspam/passwords

L’infrastructure étant prête, il faut copier les fichiers de l’interface web. Ils sont disponibles dans les sources de DSPAM, dans le répertoire « webui ». On peut copier ces fichiers directement dans le document root de nginx.

# cp -r ~/dspam-3.9.1-RC1/webui/* /var/www/dspam/

# chown dspam:www-data /var/www/dspam -R

À ce stade, il nous reste un peu de configuration à faire. Le script /var/www/dspam/cgi-bin/configure.pl contient la configuration permettant à l’interface web de consulter les répertoires de DSPAM. Il faut donc vérifier les valeurs de $CONFIG{’DSPAM_HOME’}, $CONFIG{’DSPAM_BIN’}, etc., afin que cela corresponde à notre environnement.

$CONFIG{’DSPAM_HOME’}   =« /var/spool/dspam »;

$CONFIG{’DSPAM_BIN’}    =« /usr/bin »;

[...]

$CONFIG{’WEB_ROOT’}     =« /dspam/htdocs/ »;

[...]

$CONFIG{’LOCAL_DOMAIN’} = « debian.lab »;

Voilà, avec tout cela, vous devriez être capable d’ouvrir la page http://monserveur/dspam/cgi-bin/. Il faut alors vous loguer, avec l’utilisateur jean-kevin@debian.lab, et vous arriverez sur l’interface de DSPAM.

Elle permet, entre autres, dans l’onglet History, de réapprendre les messages déjà traités. Vous pouvez également changer les préférences, etc.

dspam_control_center

Figure 1 : interface web de DSPAM

L’interface fournit une section d'administration. Pour y accéder, il faut déclarer un administrateur dans le fichier /var/www/dspam/cgi-bin/admins.

# echo ‘jean-kevin@debian.lab’ >> /var/www/dspam/cgi-bin/admins

On peut ensuite accéder a l’URL http://monserveur/dspam/cgi-bin/admin.cgi et admirer les beaux graphiques d’activité, ou changer les options par défaut.

7. Gestion des groupes et inoculation

Si DSPAM focalise l’analyse sur l’utilisateur, il permet également à des groupes d’utilisateurs de partager des données. En définissant correctement ces groupes, via leur activité, par exemple, on peut s’attendre à des contenus de messages similaires et donc à des statistiques par tokens similaires. Partager ces informations permet d’accélérer l’apprentissage de DSPAM.

En pratique, chaque utilisateur dispose de son dictionnaire de tokens dans le fichier <user>.css. Ce dictionnaire contient les tokens et les statistiques associées, durement produites par l’utilisateur.

DSPAM permet de partager ces tokens et statistiques de différentes manières (ou types de groupes).

- Shared : les dictionnaires des utilisateurs sont fusionnés en un seul et unique dictionnaire pour tous les membres du groupe, chaque membre conserve toutefois son propre répertoire de quarantaine. Problème : si un utilisateur a un comportement diffèrent du reste du groupe, il va perturber l'ensemble, à commencer par lui-même.

- Classification : permet de partager les dictionnaires individuels. Si le dictionnaire d'un utilisateur ne permet pas de déterminer si un message est de type spam ou innocent (confiance<0.65 ou dictionnaire contenant moins de 1000 messages innocents et 250 spams), alors les dictionnaires des autres membres listés dans le groupe sont utilisés. L'analyse s'arrête dès qu'un dictionnaire classe formellement le message. En pratique, ce groupe est une chaîne contenant tous les utilisateurs du groupe et qui est parcourue linéairement jusqu'à ce qu'une décision soit obtenue. Il faut être listé comme membre du groupe pour pouvoir interroger les dictionnaires des autres membres.

- Global : une variante de Classification. Le type Global permet de définir un groupe de classification dans lequel tous les membres du système peuvent interroger les dictionnaires des membres listés. Si le dictionnaire d'un utilisateur est insuffisant pour classer un message, il demandera alors l'avis des membres du Global, en parcourant la chaîne jusqu'à ce qu'une décision formelle soit obtenue. En clair, Global est une sorte de « conseil des sages » que chaque utilisateur peut interroger.

- Merged : permet de cumuler un dictionnaire de référence au dictionnaire des utilisateurs. Merged assemble le dictionnaire de l'utilisateur et le dictionnaire de référence pour n'en faire qu'un, et utilise ce nouveau dictionnaire pour l'analyse.

- Inoculation : Ce dernier type de groupe est un peu particulier. Il correspond au principe de la vaccination et permet à un utilisateur ayant reçu un spam non détecté d'informer tous les autres utilisateurs que ce message est un spam. Ainsi, chaque utilisateur dispose de son propre dictionnaire, qu'il utilise exclusivement pour l'analyse, mais les utilisateurs peuvent s'échanger des tokens entre eux. Le premier utilisateur est contaminé, les autres sont vaccinés. Ce principe de l’inoculation permet également de définir un utilisateur poubelle, un honeypot à spam, qui recevra exclusivement du spam et permettra donc d’accélérer l’apprentissage pour tout le monde. Ce deuxième mode de fonctionnement est appelé external inoculation.

7.1 Configuration d'un groupe

La mise en place d’un groupe est plutôt simple, la partie la plus complexe étant de déterminer le bon type de groupe selon l’environnement, et surtout, de monitorer le comportement au fil des semaines.

Dans notre exemple, nous allons mettre en place un groupe de type classification. Ce type permettant à chacun de conserver son dictionnaire personnel, nous prenons peu de risques.

DSPAM lit la configuration des groupes dans un fichier texte localisé dans son Home Directory, sous /var/spool/dspam/group. Le fichier prend une ligne par groupe sous la forme : <nom du groupe>:<type>:<utilisateur 1>,...,<utilisateur n>.

On va créer un groupe de type classification comprenant les utilisateurs jean-kevin, julien et root, et l’on va nommer ce groupe class-debian-lab.

# echo « class-debian-lab:classification:jean-kevin@debian.lab,julien@debian.lab,root@debian.lab » > /var/spool/dspam/group

# chown dspam:dspam /var/spool/dspam/group

# kill `pidof dspam`

# start-stop-daemon --start --chuid dspam --exec /usr/bin/dspam -- --daemon

En activant les traces de debug dans dspam.conf (directive Debug * quand DSPAM est compilé avec le mode debug), on peut constater la création du groupe dans le fichier /var/spool/dspam/log/dspam.debug.

10150: [09/08/2010 14:43:19] user jean-kevin@debian.lab is member of classification group class-debian-lab

10150: [09/08/2010 14:43:19] adding user julien@debian.lab to classification network group

10150: [09/08/2010 14:43:19] adding user root@debian.lab to classification network group

8. Administration

DSPAM stocke une grande quantité d’informations, que ce soit pour les tokens ou l'historique. Il fournit donc deux outils permettant de faire un peu de nettoyage. Ces outils, dspam_clean et dspam_logrotate, vont respectivement nettoyer les dictionnaires et les fichiers de logs selon les paramètres définis dans dspam.conf.

8.1 dspam_clean

Les directives par défaut de dspam_clean conservent les signatures pendant 14 jours et nettoient les tokens peu utilisés au bout de 15, 30 et 90 jours selon le type. Encore une fois, le fichier de configuration fourni par les sources est plutôt bien commenté.

#

# Purge configuration: Set dspam_clean purge default options, if not otherwise

# specified on the commandline

#

PurgeSignatures 14      #Stale signatures

PurgeNeutral    90      #Tokens with neutralish probabilities

PurgeUnused     90      #Unused tokens

PurgeHapaxes    30      #Tokens with less than 5 hits (hapaxes)

PurgeHits1S     15      #Tokens with only 1 spam hit

PurgeHits1I     15      #Tokens with only 1 innocent hit

Afin de réaliser une purge périodique, il faut ajouter dspam_clean dans cron. Par exemple, avec une commande dans /etc/crontab qui se lance tous les jours à 5h :

0 5    ** *   dspam   /usr/bin/dspam_clean -s -p -u

Cette commande va réaliser la purge des trois types d’informations, à savoir signatures, tokens neutres et tokens non utilisés.

8.2 dspam_logrotate

Ce programme permet de réaliser une rotation des logs système et utilisateur de DSPAM (ceux stockés dans /var/spool/dspam).

La commande peut être lancée pour un utilisateur précis ou pour l’ensemble du répertoire dspam. Dans notre cas, on veut réaliser la rotation pour tout le monde lorsque les logs dépassent 60 jours. On va donc placer la commande suivante dans crontab :

30 5    ** *   dspam   dspam_logrotate -a 60 -d /var/spool/dspam/data/

9. Procédure de test

Pour terminer cette article, nous allons dérouler la procédure de test indiquée dans la documentation de DSPAM. Cette procédure permet non seulement de vérifier que la configuration est opérationnelle, mais également de se familiariser avec les commandes internes de DSPAM.

Étape 1 : Créer un utilisateur vierge :

# useradd -d /home/michel-rene -U -m michel-rene

# passwd michel-rene

Étape 2 : Envoyer un court e-mail à notre nouvel utilisateur :

# nc localhost 25 << EOF

ehlo mail

mail from:<jp.troll@gmail.com>

rcpt to:<michel-rene@debian.lab>

data

From: <jp.troll@gmail.com>

To: <michel-rene@debian.lab>

Subject: Cours message de test

10 mots c'est pas assez long pour un troll.

.

quit

EOF

Étape 3 : Vérifier les statistiques du compte avec la commande dspam_stats :

# dspam_stats michel-rene@debian.lab

michel-rene@debian.lab TP:     0TN:     1FP:     0FN:     0SC:     0NC:     0

On a bien un seul message inspecté par DSPAM dans les statistiques.

Étape 4 : Vérifier la liste des tokens et les probabilités associées via dspam_dump :

# dspam_dump michel-rene@debian.lab

4311867737599848632 S: 00000 I: 00001 P: 0.4000 LH: Wed Sep 821:20:22 2010

9486336444479993084 S: 00000 I: 00001 P: 0.4000 LH: Wed Sep 821:20:22 2010

18360635214432484661 S: 00000 I: 00001 P: 0.4000 LH: Wed Sep 821:20:22 2010

[…]

Les tokens sont associés à un message innocent, c'est pour cela que la valeur S (pour Spam) est à zéro, et la valeur I (pour Innocent) est à un. Au passage, on notera que le tokenizer OSB crée 114 tokens pour ce petit message (quelques headers ont toutefois été ajoutés par Postfix entre deux).

On peut voir la statistique associée à un token particulier en le saisissant sous forme texte dans la ligne de commandes. Évidemment, avec OSB comme tokenizer, la difficulté est de connaître la forme de ce token.

# dspam_dump michel-rene@debian.lab un+troll

1157728372545618534 S: 00000 I: 00001 P: 0.4000

# dspam_dump michel-rene@debian.lab assez+#+#+#+troll

695260355258399736   S: 00000 I: 000001 P: 0.4000

Étape 5 : Marquer le message comme spam, par exemple dans l'interface web.

Étape 6 : Vérifier les statistiques DSPAM de l'utilisateur encore une fois :

# dspam_stats michel-rene@debian.lab

michel-rene@debian.lab TP:     0TN:     0FP:     0FN:     1SC:     0NC:     0

On voit bien, cette fois, que DSPAM marque le message comme False Negative (FN).

Étape 7 : Vérifier l'état des tokens encore une fois :

# dspam_dump michel-rene@debian.lab

4311867737599848632 S: 00001 I: 00000 P: 0.4000 LH: Wed Sep 821:28:31 2010

9486336444479993084 S: 00001 I: 00000 P: 0.4000 LH: Wed Sep 821:28:31 2010

18360635214432484661 S: 00001 I: 00000 P: 0.4000 LH: Wed Sep 821:28:31 2010

[…]

La mise à jour a bien été faite, ces tokens sont désormais associés à un spam (S est à un, I à zéro).

Ces quelques commandes permettent non seulement de vérifier que notre antispam est fonctionnel, mais également de suivre la vie des tokens au fil du temps.

Conclusion

Notre visite guidée de DSPAM est terminée. Je n'ai pas vraiment parlé de taux de réussite et autres critères généralement utilisés pour classer les solutions antispam entre elles. Cela pour deux raisons : la première, c'est que ces chiffres sont généralement menteurs et que les résultats d'un antispam dépendent énormément du comportement d'un utilisateur, difficile donc de sortir des chiffres reproductibles. La seconde raison, c'est qu'il n'y a pas vraiment de sens, aujourd'hui, à baser une infrastructure antispam sur un seul produit. Intégrer un système de greylist avec Postfix est trivial, et il est même possible de coupler SpamAssasin et DSPAM l'un derrière l'autre (il suffit d'appeler un content-filter vers SpamAssassin depuis le service de retour de DSPAM dans Postfix).

Ainsi, ma propre configuration ne reçoit qu'entre 2 et 3 % de spams (postgrey est diablement efficace), et sur ce faible pourcentage, plus de la moitié est marquée comme spam par DSPAM après seulement quelques semaines d'utilisation. Encore un mois ou deux, et ce devrait être 99 % des spams qui seront détectés.

Donc, au final, combattre le spam, c'est multiplier les techniques, mais ce que nous avons vu dans ces pages, c'est que DSPAM est un superbe outil pour réaliser ce travail. Peut-être un peu difficile à prendre en main au départ, mais le résultat et la flexibilité du produit en valent largement l'investissement initial.


Par le même auteur

Mozilla InvestiGator : quand vos serveurs se prennent pour Sherlock Holmes

Magazine
Marque
MISC
HS n°
Numéro
11
|
Mois de parution
juin 2015
|
Domaines
Résumé

Avec suffisamment de serveurs à surveiller, l'investigateur expérimenté se retrouve souvent à chercher l'aiguille dans la botte de foin. Pour peu que l'infrastructure soit hétérogène, la recherche peut prendre plusieurs heures et souvent ne pas couvrir l’intégralité du domaine. Si vous êtes familier de ce problème et que PSSH ne fait plus l'affaire, la suite devrait vous intéresser. Une note toutefois : les personnages et situations qui suivent sont purement fictifs et ne représentent pas forcément la routine des investigations réalisées par l'auteur dans le cadre de son activité.

TLS, état des lieux côté serveur

Magazine
Marque
MISC
Numéro
72
|
Mois de parution
mars 2014
|
Domaines
Résumé
Configurer TLS côté serveur est difficile. 19% des sites en HTTPS proposent toujours SSLv2. 28% vont négocier une session avec DES_CBC_SHA et ses clés de 56 bits si le client le demande. Il y a un peu plus d'un an, la majorité des experts en SSL/TLS recommandaient RC4 à AES. Quelques mois plus tard, c'était l'inverse. Une partie de la communauté cryptographique supporte Perfect Forward Secrecy, alors qu'une autre le refuse pour cause de lenteur. Il existe même des doutes sur le niveau réel de sécurité fourni par AES-256, par rapport à la version 128 bits. Ajoutons à cela une bonne dose de perte de confiance dans les standards existants, en particulier depuis que l'on sait Dual_EC_DRBG backdoored, et nous voici avec une parfaite recette de confusion et d'incohérence. Les désaccords sur la meilleure façon de configurer HTTPS sont nombreux. Et si le débat est utile aux experts, il est presque impossible pour un non-initié de naviguer dans la nébuleuse TLS, et d'en extraire une configuration de référence.

Postscreen, l'exterminateur de zombies

Magazine
Marque
GNU/Linux Magazine
Numéro
147
|
Mois de parution
mars 2012
|
Domaines
Résumé
Il y a plus d'un an de cela, je vous présentais dans ces pages un formidable répulsif à Sus scrofa domesticus, j'ai nommé DSPAM. Au même moment, notre rédacteur en chef préféré m'avouait sa passion déraisonnable et certainement bien personnelle pour ce formidable résidu de cuisine qu'est le pâté de jambon, communément appelé Spam de mon côté de l'Atlantique. Fort de ma machine à tokens dopée aux amphétamines (toujours DSPAM), je m'empressais de pondérer les propos du malheureux finnois d'une statistique bien négative, et éventuellement repoussais lesdits échanges aux confins de mon dossier spam (sans pour autant bannir complètement l'intéressé, dont la conversation est toujours des plus constructive et bienvenue). Heureux de ma vie tranquille, lisant plaisamment mes courriels sans l'once d'une distraction viagriarienne « enlarge your peniche » pendant presque un an, je relâchais donc ma garde. Mais le spammer est malin, et sous prétexte de bricoler des bidules électroniques, le voilà qui libérait toute une armée de zombies spammers prêts à délivrer leurs contenus dégoûtants dans les entrailles de l'Internet.

Développement web en Perl avec Mojolicious

Magazine
Marque
GNU/Linux Magazine
Numéro
138
|
Mois de parution
mai 2011
|
Résumé
Cela fait maintenant quelques années que Perl, en tant que langage de développement web, se fait discret. Beaucoup de développeurs l'utilisent toujours, mais la (relativement récente) vague Python/Ruby s'est ajoutée à PHP pour occuper l'essentiel du Web. En tant que sysadmin, j'ai une tendance naturelle au conservatisme : si ça marche, ne touche à rien. Bon, évidemment, ça ne s'applique pas partout, mais quand il s'agit de programmation, j'aime bien me dire que Perl fera le boulot au moins aussi bien, et peut-être mieux, que d'autres langages.J'étais donc curieux du petit dernier de la communauté Perl : le framework MVC Mojolicious. Petit frère du très reconnu Catalyst, Mojolicious est un projet récent qui essaie de corriger certains mécontentements que les moines de Perl ont pu ressentir en utilisant Catalyst. Dans la mesure où les robes en bure, hormis le fait que ça gratte, ce n'est pas ma spécialité (et n'étant pas développeur de nature), cet article n'est pas un rapport d'expertise sur Perl et les frameworks MVC, mais plutôt une introduction au développement web avec Perl et Mojolicious 1.1.

Combattre efficacement le spam avec DSPAM

Magazine
Marque
GNU/Linux Magazine
Numéro
132
|
Mois de parution
novembre 2010
|
Résumé
Vous avez déjà goûté du spam ? Je veux dire, du vrai spam. Celui qui est servi en boîtes rectangulaires et a vaguement la couleur du jambon. On m’en a servi, une fois, lors d’un réveillon, et je peux vous assurer que ce n’est vraiment pas bon ! Mais le spam dont on va parler ici est différent, plus électronique, mais tout aussi dégoûtant.Combattre le spam est probablement la tâche la plus complexe du postmaster. Les techniques sont nombreuses et il est indispensable de les cumuler pour obtenir un résultat pertinent.