Mar 23, 2007

メールについて

昔書いたメール環境と bogofilter の日本語パッチあてに関する 説明。そのまま貼ってみる。

◎スパムフィルタ

これまで、 Cyrus imapdMewを使って、IMAP でメールを読んで来た。 mew では自動リファイル(M-o)などは使わず、spam だろうがなんだろうが かならず %inbox で一通ずつ読み、o[ret] でリファイルするか、 d で削除していた。 (フォルダ名は自動推測でほとんど変える必要は無かった。 自動推測のための規則を書いた .mew.el は、 これ である。) 最近は、一日 500 通以上のメールが来て、そのうちのほとんどが、 管理者をしているホストから自動で送られてくる報告メール、 もしくは、自分宛てか自分が入っている ML 宛ての spam になっている。 こうなってくると、メールを読むのに一日5〜6時間かかるように なった。(自分が読みたいと思うメールが長く技術的な英語のメールも 含んでいる、ということも多く関係しているが。) さすがにこれだとメール読みに時間が潰されすぎ、やりたいことが 全然できなくなる。

そこで、bayesian フィルタを使いたいと思うようになった。bayesian フィルタは、 メールに出現した単語それぞれに点数を付け、その出現頻度から あるメールが spam であるかを自動判断するための技術である。 これまでに受け取った spam に良く出現する単語がメールに含まれていれば、 そのメールは高い確率で spam であるし、その逆の spam でないメールに ついても、同じ事が言える。spam かそうでないかの判断は人によって 違うが、それは単語の点数を記録したデータベースをどのように作るかによって 反映できる。また、spam がこのフィルタによって「正しいメール」と 誤って認識されれば、後にこのメールを spam としてデータベースに登録しなおす ことができるので、データベースの成長にともなってフィルタも賢くなって いく。「かなり正しい判断ができる」「判断の誤り、特に false positive (spam でないメールを spam と判断してしまう誤り、重要なメールを読み落す ことになるので、とても危険だ)が少ない」と様々なウェブページで 紹介されていて、とても魅力的だ。bayesian フィルタは、以下のリンクで 勉強できる。特に、後者は必読だ。

しかし、自分が受け取る spam には、自分宛てに送られてくるものと、 ML 宛てに送られてくるものの二つがある。そして、自分が所属する ML の多くは distribute というソフトウェアを使って配布されており、 サブジェクトには「(ML名 30)」のように番号が振られている。 この機能は、ML 上で「ML名 30 のメールに報告をまとめました。」のように、 別のメールを参照する上でとても便利だが、spam を自動で捨ててしまうと 保存したフォルダに残る番号が飛び飛びになってしまう。これは、 A 型の自分には耐えがたいことだ。これに対して、自分宛ての spam は いくら捨てようが構わない。

この理由から、ML で distribute される前に bayesian フィルタで判定し、 spam であれば distribute のシーケンス番号を付けずに配送する (もしくは、配送しない、モデレータの役割を持つような別のメンバに 配送する)、ということを実現したい。

bayesian フィルタの実装には、bogofilter を使うことにした。 また、日本語の spam をちゃんと登録するために、kakasi を 使わなくてはならない。bogofilter を通した結果を利用して 配送先を変えるには、procmail を使う。そして、これまでの 自分の環境どおり、実際の ML メンバへの配送は distribute を使う。

良く見かけるのは以下のような設定だが、自分の環境では何故か 動かなかった。

0HBfw
* ? nkf -me | kakasi -w | bogofilter -l
| dmail + bogofilter

condition 行(* で始まっている行)の実行で、以下のようなエラーが 出てしまう。

procmail: Program failure (1) of " /usr/pkg/bin/nkf -m -e | ..."

原因は、シェルの問題だったかもしれない。また、 ここで、 見つけた以下のような設定も、試してみるといいかも知れない。

BOGOSITY = `nkf -me | kakasi -w | bogofilter -vul`

:0 fhw | formail -I "$BOGOSITY"

:0 * X-Bogosity: Yes spam/

結局どうしたかというと、 ここ にある bogofilter へのパッチで、似たようなことを実現した。 procmail の recipe は、以下のようになる。

:0HBfw
| bogofilter -l -e -p -c /usr/lib/mail-list/sing-ml/bogofilter.conf

:0 * ^X-Bogosity: Yes, tests=bogofilter, | distribute -l sing -h sfc.wide.ad.jp -r sing -a "SING spam" -e -L /usr/lib/mail-list/sing-ml/moderators

:0 | distribute -l sing -h sfc.wide.ad.jp -I /usr/lib/mail-list/sing-ml/sequence -r sing -a SING -e -L /usr/lib/mail-list/sing-ml/members

まず、bogofilter に通して、X-Bogosity: ヘッダをメールに追加する。 ここ にあるパッチを当ててあるので、Subject: の MIME エンコーディングや、 charset が iso-2022-jp (JIS) であるようなマルチパートは、 日本語として認識され、わかち書きに変更されるように kakasi が 呼ばれる。これは、bogofilter 内のバッファで行われるため、 元のメールには変更が無い (-p オプションで明示的に X-Bogosity: ヘッダを 追加するようにしている以外は)。

その後、X-Bogosity: ヘッダの内容によって、distribute の呼び方を変える ようにしている。spam だと判定された場合は、distribute のシーケンス 番号を利用しないように設定しているため、spam によってシーケンス番号が どんどん先に進んでしまう、というような不快な状況が回避できた。

spam は、ML の全てのメンバに送る必要はないだろう。 ここでは、ML のメンバから「spam を送られても構わない人」 を moderators という名のファイルに羅列し、その人たちに spam だと 判定されたメールを送っている。もし、重要なメールが spam だと判定された としても、moderators のうちの一人でもそれに気付けば、そのメールを 送り直したり bogofilter のデータベースをアップデートしたりして、 適切な対応ができる。重要なメールが勝手に捨てられてしまうことは、 この設定により発生しないと期待できる。

spam は、見た途端にわかるように、またメーラでリファイルしやすいように、 Subject: を変更するようにした。ML に投稿された普通のメールは、 "(SING 30)" などの文字列が先頭に付くが、spam の場合は、 "(SING spam)" という文字列を付けるようにした。これを Mew で見ると、 以下のようになる。上が spam で、下が普通のメールだ。

 M04/07 "Brendan Sprin (SING spam) Fwd: Any Pills U W|                       
  04/08 To:sing@sfc.wi (SING 13334) Re: Whiten Your T|何通かフィルタ抜けて届

まとめると、このようになる。MTA (sendmail や postfix など)がメールを 受け取り、ML に配送しようとする。MTA の aliases の設定から procmail が 呼ばれ、procmail から bogofilter と distribute が呼ばれる。ここで、 spam であるかどうかによって、メールサブジェクトをどのように変更するか、 どのメンバに配送するかを変更している。

◎設定

  • /etc/aliases:
    ############################################
    # sing@sfc.wide.ad.jp
    sing:"| /usr/local/bin/procmail -m /usr/lib/mail-list/sing-ml/procmail.conf"
    sing-request:   owner-sing
    owner-sing:     yasu
    

  • /usr/lib/mail-list/sing-ml/procmail.conf:
    HOME=/tmp
    MAILDIR=$HOME
    LOGFILE=/usr/lib/mail-list/sing-ml/procmail.log
    PATH="$PATH:/usr/local/sbin:/usr/local/bin"
    

    :0HBfw | bogofilter -l -e -p -c /usr/lib/mail-list/sing-ml/bogofilter.conf

    :0 * ^X-Bogosity: Yes, tests=bogofilter, | distribute -l sing -h sfc.wide.ad.jp -r sing -a "SING spam" -e -L /usr/lib/mail-list/sing-ml/moderators

    :0 | distribute -l sing -h sfc.wide.ad.jp -I /usr/lib/mail-list/sing-ml/sequence -r sing -a SING -e -L /usr/lib/mail-list/sing-ml/members

  • /usr/lib/mail-list/sing-ml/bogofilter.conf
    bogofilter_dir=/usr/lib/mail-list/sing-ml
    

この他に、/usr/lib/mail-list/sing-ml/ には、moderators、members、sequence、 wordlist.qdbm というファイルがある。moderators と members には、spam と 普通のメールを配送するメンバがリストされている。sequence は distribute に よって利用されるシーケンス番号を保存するためのファイルで、owner が nobody になっている必要がある(ファイルの内容は空で構わない)。wordlist.qdbm は bogofilter が利用するデータベースである(コンパイル時に、データベースエンジンを qdbm にしてみた)。

drwxrwxr-x  2 root    rg         512 Apr  4 02:30 RCS/
-r--r--r--  1 root    rg          42 Mar 29 06:58 bogofilter.conf
-r--r--r--  1 root    rg        1717 Mar 29 06:54 members
-r--r--r--  1 root    rg        1679 Apr  4 02:30 moderators
-r--r--r--  1 root    rg         655 Apr  4 00:01 procmail.conf
-rw-rw-rw-  1 root    rg       25352 Apr  8 17:01 procmail.log
-rw-r--r--  1 nobody  wheel        6 Apr  8 15:56 sequence
-rw-r--r--  1 root    rg     4039925 Apr  8 15:48 wordlist.qdbm

◎メーラ(MUA: Mail User Agent)と Mew について

Mew で、一つのキーストロークで refile フォルダ先を固定し、次に 進みたい。"k" で %spam へ、"n" で 自動推測のフォルダへ、 "p" で %private へ、など。片手でできることが重要(飯食いながら メール読みたい)。シフトを押したりしなくてはならないと、 飯食い終ってから真剣にメールを読まなくてはいけないので不便。 個人的には、左手だけでできると便利。最近 M-o (自動リファイル)を 使い始めたので、"n" でマークされたメールを飛ばすのはあまり嬉しくない。

Posted at 05:51 in howto | WriteBacks (0) | Edit
WriteBacks
TrackBack ping me at
http://www.sfc.wide.ad.jp/~yasu/nblog/howto/mail-with-spam-filter.trackback
Post a comment

writeback message: Ready to post a comment.