Forward Email
Spam Scanner a été développé par Forward Email, le service de messagerie open source axé sur la confidentialité, pour ses propres serveurs de messagerie. Forward Email ne conserve aucun journal du contenu des messages : aucun service de filtrage externe ne pouvait donc convenir. Le filtre devait s’exécuter sur ses propres serveurs et expliquer chaque décision sans que personne ne lise le courrier.
Cette page montre comment un serveur de messagerie comme celui de Forward Email l’utilise, et ce qui a changé pour le code écrit pour Spam Scanner 5 ou 6.
Sur un serveur de messagerie entrant#
Forward Email reçoit le courrier avec smtp-server. Le schéma, pour tout serveur construit sur cette bibliothèque :
const SpamScanner = require('spamscanner');
const scanner = new SpamScanner({
clamav: true, // clamd on its default socket
authentication: true, // SPF, DKIM, DMARC, ARC
dnsbl: {ip: ['zen.spamhaus.org'], domain: ['dbl.spamhaus.org']},
dns: {servers: ['127.0.0.1']}, // a local caching resolver
});
async function onData(stream, session, callback) {
const scan = await scanner.scan(stream, {
session: {
remoteAddress: session.remoteAddress,
resolvedClientHostname: session.clientHostname,
helo: session.hostNameAppearsAs,
envelope: session.envelope,
},
});
if (scan.results.viruses.length > 0) {
return callback(Object.assign(new Error(`Message contains a virus: ${scan.results.viruses[0]}`), {responseCode: 554}));
}
if (scan.action === 'reject') {
// A temporary error: the sender retries, and a wrong decision can be undone.
return callback(Object.assign(new Error(`Message rejected as spam: ${scan.message}`), {responseCode: 421}));
}
// scan.isSpam: deliver to the Junk folder, or add scan's headers (spamHeaders) and forward.
callback();
}scanner.scan() accepte directement le flux SMTP. Si des résultats de mailauth sont déjà disponibles, laissez de côté authentication et passez seulement l’adresse IP.
Une réponse 421 ou 451 amène le serveur expéditeur à mettre le message en file d’attente et à réessayer plus tard. De nouvelles règles de rejet peuvent commencer avec un code temporaire et passer à 550 une fois leurs résultats vérifiés, sans perdre de courrier entre-temps.
Mise à niveau depuis la version 5 ou 6#
La version 7 est une réécriture. Le constructeur, scan() et les champs de résultat que lit le code des versions 5 et 6 fonctionnent toujours ; le classifieur, le modèle et les vérifications TensorFlow facultatives ont changé.
Ce qui ne change pas#
new SpamScanner(options)etawait scanner.scan(source).require('spamscanner')renvoie la classe, etimport SpamScanner from 'spamscanner'fonctionne.result.isSpam,result.message, ainsi queresult.results.classification,.phishing,.executables,.arbitrary,.viruses,.macroset.idnHomographAttack.- Chaque élément de
results.phishing,.executables,.arbitraryet.virusesse convertit en le même type de chaîne de message qu’avant (String(item), littéraux de gabarit,message.includes('adult-related content')). Ce sont désormais des objets avectype,messageet des détails. getTokensAndMailFromSource(),getClassification()etgetTokens().- Ces options correspondent à leurs nouveaux noms :
clamscandevientclamav,enableMacroDetection: falsedevientmacros: false,enableArbitraryDetection: falsedevientarbitrary: false,enableAuthenticationavecauthOptionsdevientauthenticationetsession,enableReputationavecreputationOptions.apiUrldevientreputation,strictIDNDetectiondevientphishing.homograph.strictMode, etallowlistetdenylistrestent.loggeretmemoizesont acceptés et ignorés.
Ce qui a changé#
| Avant | Maintenant |
|---|---|
scan('path/to/file.eml') lisait le fichier | Une chaîne est le texte d’un message. Utilisez scanFile(path) ou passez un Buffer |
Un modèle bayésien naïf de mots (classifier.json), qui ne peut plus être chargé | Un nouveau classifieur et un nouveau format de modèle ; réentraînez avec spamscanner train (entraînement) |
| Les vérifications de toxicité et NSFW chargeaient des modèles TensorFlow depuis le réseau à la première utilisation | Apportez votre propre modèle : toxicity: {model} et nsfw: {model} acceptent tout objet doté d’une méthode classify(), par exemple issu de @tensorflow-models/toxicity et de nsfwjs |
results.arbitrary listait chaque motif reconnu | Il liste les règles assez fortes pour marquer un spam à elles seules ; toutes les règles figurent dans result.tests |
| Une réponse par oui ou non | result.score, result.action (accept, tag ou reject) et result.tests, chacun avec des points et une raison |
isSpam décidé par le classifieur ou par une seule vérification | isSpam correspond à un score de 5 ou plus ; les seuils et les points sont modifiables |
| Vérifications de réputation auprès d’un point d’accès de Forward Email | Un service de réputation générique, désactivé tant que reputation.apiUrl n’est pas défini |
Nouveautés#
- Des modèles de langage pour les cas limites, locaux ou hébergés.
- SPFSender Policy Framework : un enregistrement DNS qui liste les serveurs autorisés à envoyer du courrier pour un domaine., DKIMDomainKeys Identified Mail : une signature portant sur un message, vérifiée avec une clé publique publiée dans le DNS de l’expéditeur., DMARCUne politique DNS qui indique aux destinataires quoi faire d’un message qui se réclame d’un domaine mais échoue à l’alignement SPF et DKIM. et ARCAuthenticated Received Chain : des signatures qui transmettent les résultats d’authentification d’un serveur de réexpédition à l’autre. ; listes de blocage DNS ; résolveurs filtrants de Cloudflare.
- Vérification des pièces jointes d’après leur contenu : exécutables déguisés, archives, macros, PDF actifs.
- Un milter, une API HTTP, un serveur TCP et un serveur spamd, et une ligne de commande.
- Entraînement, évaluation et apprentissage à partir des signalements, en ligne de commande ou via l’API.