Forward Email
Spam Scannerは、オープンソースでプライバシー重視のメールサービスであるForward Emailが、自社のメールサーバーのために開発しました。Forward Emailはメッセージ内容のログを残さないため、外部のフィルタリングサービスは使えませんでした。フィルターは自社のサーバーで動作し、人がメールを読まなくても各判定を説明できる必要がありました。
このページでは、Forward Emailのようなメールサーバーでの使い方と、Spam Scanner 5または6向けに書かれたコードから何が変わったかを説明します。
受信メールサーバーで#
Forward Emailはsmtp-serverでメールを受信します。smtp-serverで構築したサーバーであれば、次のパターンを使えます。
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()はSMTPストリームを直接受け付けます。mailauthの結果がすでに手元にある場合は、authenticationを省き、IPアドレスだけを渡してください。
421または451で応答すると、送信側のサーバーはメッセージをキューに入れ、後で再試行します。新しい拒否ルールは一時的なコードで始め、結果を確認してから550に切り替えれば、その間にメールを失うことはありません。
バージョン5または6からのアップグレード#
バージョン7は全面的な書き直しです。バージョン5と6のコードが使うコンストラクター、scan()、結果のフィールドはそのまま動きます。分類器、モデル、オプションのTensorFlowによる検査は変わりました。
変わらないもの#
new SpamScanner(options)とawait scanner.scan(source)。require('spamscanner')はクラスを返し、import SpamScanner from 'spamscanner'も使えます。result.isSpam、result.message、そしてresult.results.classification、.phishing、.executables、.arbitrary、.viruses、.macros、.idnHomographAttack。results.phishing、.executables、.arbitrary、.virusesの各項目は、以前と同じ種類のメッセージ文字列に変換されます(String(item)、テンプレートリテラル、message.includes('adult-related content'))。各項目は現在、type、message、詳細を持つオブジェクトです。getTokensAndMailFromSource()、getClassification()、getTokens()。- 次のオプションは新しい名前に対応付けられます。
clamscanはclamavに、enableMacroDetection: falseはmacros: falseに、enableArbitraryDetection: falseはarbitrary: falseに、enableAuthenticationとauthOptionsはauthenticationとsessionに、enableReputationとreputationOptions.apiUrlはreputationに、strictIDNDetectionはphishing.homograph.strictModeに対応し、allowlistとdenylistはそのままです。loggerとmemoizeは受け付けますが無視します。
変わったもの#
| 以前 | 現在 |
|---|---|
scan('path/to/file.eml')はファイルを読んでいた | 文字列はメッセージのテキストとして扱う。scanFile(path)を使うか、Bufferを渡す |
単語の単純ベイズモデル(classifier.json)。現在は読み込めない | 新しい分類器とモデル形式。spamscanner trainで再学習する(学習) |
| 有害性とNSFWの検査は、初回使用時にネットワークからTensorFlowのモデルを読み込んでいた | 自分でモデルを用意する。toxicity: {model}とnsfw: {model}は、classify()メソッドを持つ任意のオブジェクトを受け取る(例:@tensorflow-models/toxicityとnsfwjs) |
results.arbitraryは一致したすべてのパターンを列挙していた | 単独でスパムと判定できるほど強いルールを列挙する。すべてのルールはresult.testsにある |
| はい・いいえの回答 | result.score、result.action(accept、tag、reject)、result.tests。各テストに点数と理由が付く |
isSpamは分類器または単一の検査で決まっていた | isSpamはスコア5以上を意味する。しきい値と点数は変更できる |
| Forward Emailのエンドポイントに対するレピュテーション検査 | 汎用のレピュテーションサービス。reputation.apiUrlを設定しない限り無効 |
新機能#
- 際どい判定のための、ローカルまたはホスト型の言語モデル。
- SPFSender Policy Framework。あるドメインのメールの送信を許可されたサーバーを列挙するDNSレコード。、DKIMDomainKeys Identified Mail。メッセージに付ける署名で、送信者のDNSで公開された公開鍵を使って検証します。、DMARCあるドメインを名乗るメールがSPFとDKIMのアライメントに失敗したとき、受信側がどう扱うべきかを示すDNSのポリシー。、ARCAuthenticated Received Chain。転送サーバーを経由しても認証結果を引き継ぐための署名。、DNSブロックリスト、Cloudflareのフィルタリング用リゾルバー。
- 内容に基づく添付ファイルの検査:偽装した実行ファイル、アーカイブ、マクロ、アクティブなPDF。
- milter、HTTP API、TCPサーバー、spamdサーバーとコマンドライン。
- コマンドラインまたはAPIからの学習、評価、報告からの学習。