ベイズ理論によるSPAM 対策メモ −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # SPAM メール対策概要 #--------------------------------------------------------------------------------------------- 近年、海外・国内問わず、SPAM メールが大量にインターネット上を行き来しています。これを回避するには通常、SPAM メールをフィルターしますが、過去のメールからの統計情報により、着信したメールをSPAM かNON-SPAM かを判断するベイズ理論によるフォルタリングが注目されています。 今回は、SPAM メールフィルターにbogofilter+kakasi を、実際SPAM メールボックスへの配送に.qmail+maildrop を用いた例を説明します。 # メールフィルター概要 #--------------------------------------------------------------------------------------------- bogofilter はベイズ理論によるフィルタープログラムです。 ● 日本語メールのSPAM 判断に関する問題 bogofilter は英語圏で開発されたこともあり、日本語メールはうまくフィルターしてくれません。これは、日本語の分かち書き(文節分割) が正しく行われないことに起因しているようです。 そこで、日本語文章を辞書を基に正しい文節に分割するkakasi が登場する訳です。 kakasi は、辞書を基にして日本語を解析し、単語や文節に分けたり、ひらがなやカタカナなどに変換したりするプログラムです。日本語メールをkakasi によって正しく文節分けし、その後bogofilter を通すことで日本語メールも正しくフィルタリングすることができます。 ● bogofilter の学習機能に関して bogofilter は使う側でなにも設定しないことには本来の良さが発揮されません。というのも、bogofilter はSPAM かどうか判断する材料として、内部的に文節データベースを持っています。そして、bogofilter を初めて使う場合は、このデータベースにあらかじめSPAM である文節、SPAM でない文節を複数登録する必要があるのです。安定した運用にはいると、bogofilter は自動的にメールがSPAM であるかでないかを判断するようになります。つまり、使う側がbogofilter に学習させる必要があるのです。 # SPAM メール振り分け概要 #--------------------------------------------------------------------------------------------- SPAM メールであるか、NON-SPAM メールであるかの判断はbogofilter が行いますが、実際メールボックスに配送するのはメール配送エージェント(MDA) です。qmail 標準のMDA であるqmail-pop3d は条件によって配送先を変更する機能はついていないので、maildrop が必要となります。 maildrop はメール配送指示言語を持ったローカルメール配送エージェント(MDA) です。既存のローカルメールエージェントに置き換えることができる他、.qmail などのプログラム転送機能経由で実行することもできます。 qmail は自身の配送エージェントを容易に置き換えることができないので、.qmail からmaildrop を利用することとします。 SPAM メールチェックをbogofilter に行わせるためには、maildrop の設定により、メールをbogofilter に受け渡す処理を記載する必要があります。そこで、maildrop のユーザーごとの設定ファイル $HOME/.maildrop に必要な設定を記述します。 ※ 注意 ) maildrop をqmail と連動して利用する場合、ユーザーのホームディレクトリは、通常、qmail のassign ファイルに記載されたUID ユーザーのホームディレクトリをさします。つまり「$HOME = assign ファイルに記載されたUID ユーザーのホームディレクトリ」となります。 # インストール方法 #--------------------------------------------------------------------------------------------- bogofilter、kakasi、maildrop のソースをインターネットよりダウンロード後、ソースを解凍します。 # su root # cd /usr/local/src/ # wget kakasi-2.3.4.tar.gz # wget bogofilter-0.93.1.tar.gz # wget maildrop-1.7.0.20041104.tar.bz2 ソースを解凍後、ディレクトリに入りインストール作業を行います。 ↓ kakasi をインストール # tar zxvf kakasi-2.3.4.tar.gz # cd kakasi-2.3.4 # ./configure --prefix=/usr/local/kakasi # make # make install ↓ bogofilter をインストール # tar xzvf bogofilter-0.93.1.tar.gz # cd bogofilter-0.93.1 # ./configure --prefix=/usr/local/bogofilter # make # make install # cd /usr/local/bogofilter/etc # cp bogofilter.cf.example bogofilter.cf ← bogofilter の設定ファイルを作成する ↓ maildrop をインストール # tar jxvf maildrop-1.8.0.tar.bz2 # cd maildrop-1.8.0 # ./configure --prefix=/usr/local/maildrop --with-etcdir=/usr/local/maildrop/etc \ --enable-restrict-trusted=0 --enable-maildirquota # make # make install # make install-configure ※ 注意 ) maildrop が配送モードで起動された場合、セキュリティーの関係上maildrop を利用できるユーザーを限定しますが、今回は.qmail ファイルから呼び出されるので「--enable-restrict-trusted=0」としています。 # bogofilter に学習させよう #--------------------------------------------------------------------------------------------- ● bogofilter のグローバル設定 bogofilter の動作に関する設定は/usr/local/bogofilter/etc/bogofilter.cf に記載します。おおむね次のオプションを設定すれば動作します。 bogofilter_dir | bogofilter の文節データベースを格納するディレクトリパス user_config_file | ユーザーが設定可能なbogofiltger 設定ファイルパス ● bogofilter 用ユーティリティー メール全体のうち、ヘッダ、本文(添付ファイルを除く) について、どのような内容がSPAM メールであり、どのような内容がSPAM メールではないのかbogofilter に学習させる必要があります。 ただ、その前に、単純にヘッダ、本文をbogofilter に渡すだけではbogofilter は正常処理できません。なぜなら、困ったことにメールはテキストメールだけではく、次のようなメールがあるからです。 ・テキストメール ・マルチパートメール(添付付きファイル、HTML付きファイルなど) ・本文がiso-2022-jp以外の文字コードであるメール ・本文がエンコードされているメール 上記のうち、特に本文について、bogofilter が解析できるテキストのみを抽出し適切な変換を施す必要があります。この変換(文字コード変換、日本語分かち書きも含む) を行うプログラムを自作で作成します。 なお、下記プログラムは/usr/local/bogofilter/bin/bogofilter.pl として保存してください。 ↓ bogofilter.pl の中身 ------------------------------------ #!/usr/bin/perl use Email::MIME; use NKF; use Text::Kakasi; sub mime_body { my $o = shift; my @p = $o->subparts; my $m = ""; if( @p > 0 ) { foreach my $so ( @p ) { $m = $m . mime_body( $so ); } } elsif( ! defined($o->content_type) || $o->content_type =~ /text/i ) { $m = $m . nkf( "-e"、$o->body ); } else { $m = $m . $o->content_type . "\n"; } return $m; } local $/; my $kakasiOBJ; my $mimeOBJ; my $inputMessage; my $outputMessage; my $resultMessage; # 標準入力からメール全体を受け取る $inputMessage = <>; # メール全体のうち、メールヘッダ+本文を抜き出し、EUC-JP コードに変換する $mimeOBJ = Email::MIME->new( $inputMessage ); $outputMessage = nkf( "-e"、$mimeOBJ->header_obj->as_string."\n" ); $outputMessage .= mime_body( $mimeOBJ ); # kakasi によってメールヘッダ+本文を日本語単語分割する $kakasiOBJ = Text::Kakasi->new; $kakasiOBJ->set( "-w" ); $resultMessage = $kakasiOBJ->get( $outputMessage ); print $resultMessage; ------------------------------------------------------------ ● 学習機能 上記プログラムによって、メール解析、文字コード変換、日本語分かち書きまで実現できたので、後はbogofilter にSPAM メール、でないメールを学習させるだけです。 ↓ SPAM メールを一度に学習させる方法(spamdir にSPAM メールがあるとする) # cd spamdir # for file in ./*; do > cat $file | /usr/local/bogofilter/bin/bogofilter.pl | bogofilter -sv > done ↓ SPAM メールを単体で学習させる方法 # bogofilter -sv < spammail ↓ SPAM ではないメールを一度に学習させる方法(spamdir にSPAM メールがあるとする) # cd nonspamdir # for file in ./*; do > cat $file | /usr/local/bogofilter/bin/bogofilter.pl | bogofilter -nv > done ↓ SPAM でないメールを単体で学習させる方法 # bogofilter -nv < nonspammail また、コマンドライン上でbogofilter にメールを読ませ、SPAM メールチェックを行わせることもできます。 # bogofilter -vI mail X-Bogosity: Ham の場合はSPAM メールでないとして判断されています。 X-Bogosity: Spam の場合はSPAM メールとして判断されています。 上記コマンドによって学習されたbogofilter のデータベースはbogofilter の設定を特に変更しない限りは/usr/local/bogofilter/wordlist/wordlist.db に記憶されます。 ● SPAM フィルターで用いるそれぞれのプログラムの引数について > nkf プログラムの引数 -Z X0208中の英数字と若干の記号をASCIIに変換する。 -m MIME を解読する。 -e EUCコードを出力する。 > kakasi プログラムの引数 -w 分かち書きをします。 ※ kakasi は-w オプションをつけ、実行すると標準入力された内容の分かち書きを実行するようです > bogofilter プログラムの引数 -s 入力されたものをSPAM辞書に登録 -n 入力されたものを普通メール辞書に登録 -S 入力されたものをSPAM辞書から削除 -N 入力されたものを普通メール辞書から削除 -v 詳細情報の表示 -u 判定したメールを自動的に反映(辞書に登録) -l syslog に記憶 -p メールヘッダにX-Bogosity: を付加する -e bogofilter の終了コードを0 とする ※ 注意 ) nkf、kakasi による文字コード変換、日本語分かち書きはbogofilter.pl プログラムによって実施されます。 # メール配送時のフィルター設定 #--------------------------------------------------------------------------------------------- SPAM メールであるか、NON-SPAM メールであるかの判断は、実際にメールがローカルユーザーに配送される際に行います。ローカルユーザーに配送する処理はqmail-pop3d が行うので当然、フィルタールールは.qmail ファイルに記載することになります。 maildrop を.qmail 内で利用するので、maildrop はマニュアルモードで動作させます。.qmail にはmaildrop によるフィルターを行う旨を記載し、.mailfilter にはアカウントごとのフィルター条件を記載します。 ここでは、例として次のドメイン、アカウントによる設定を行います。 ドメイン : example01.jp アカウント : test メールホーム : /var/vpopmail/example01.jp/test ↓ .qmail の中身 ------------------------------------------- |/usr/local/qmail/bin/maildrop /var/vpopmail/example01.jp/test/.mailfilter ------------------------------------------------------------ ※ 注意 ) maildrop の引数に.mailfilter のみを指定してはいけません。この場合はqmail の配送するアカウント(/usr/local/qmail/users/assign に記載されたアカウント) のホームディレクトリの.mailfilter を読み込んでしまいます。 ↓ .mailfilter の中身 -------------------------------------- MAILDROPHOMEDIR=/usr/local/maildrop BOGOHOMEDIR=/usr/local/bogofilter # フィルターログを記憶する logfile "./.filter_log" # bogofilter 実行部を作成する BOGOEXEC=`$BOGOHOMEDIR/bin/bogofilter.pl | $BOGOHOMEDIR/bin/bogofilter -elp -c /home/maild/webmaster-spicy/bogofilter.cf | $MAILDROPHOMEDIR/bin/reformail -X 'X-Bogosity:'` # SPAM メール判断を行う xfilter "$MAILDROPHOMEDIR/bin/reformail -i '$BOGOEXEC'" # 韓国語/中国語のみのメールは.SPAM-BOX に配送する if( /content\-type:.*charset=.*(ks_c_5601-1987|euc-kr|gb2312|big5).*/:w ) { to "./Maildir/.SPAM-BOX/" } # HTML メールは.HTML-BOX に配送する if( /content\-type:.*text\/html.*/:w ) { to "./Maildir/.HTML-BOX/" } # SPAM メールは.SPAM-BOX ボックスに配送する if(/^X-Bogosity: Spam、tests=bogofilter/:h ) { to "./Maildir/.SPAM-BOX/" } to "./Maildir/" ------------------------------------------------------------ ※ 注意 ) refomail : メールを標準入力として受け取り、引数で指示された処理を行い結果を標準出力に返す xfilter : メールを標準入力として受け取り、引数で指示されたフィルタ処理をメール全体に適応する # ユーティリティの使い方 #--------------------------------------------------------------------------------------------- bogofilter にはユーティリティーbogoutil が付属している。これを用いることで現在の学習状況が把握できます。 > データベースのSPAM/HAM の登録数状況 bogoutil -w [wordlist.db ファイルへのパス] .MSG_COUNT > データベース内部のエンコード bogoutil -w [wordlist.db ファイルへのパス] .ENCODING > データベースのキーワード登録状況 bogoutil -d [wordlist.db ファイルへのパス] | iconv -c -f utf-8 -t iso-8859-1 | nkf -e