迷惑メールを自動判定しよう
近頃、迷惑メールが多くなってきました。添付ファイルを開いたり、リンクされているWEBページへ行ったりすると
大変なことになります。
以前は、spamassasinで判定していたのですが、違う対策をしてみようと思いました。
Sysopによっては複数のソフトを使ってより精度の高い判定をしているようです。
今回利用するソフトはBogofilterというソフトです。
VineLinuxでは分かち書きソフトにkakashiではなくmecabを使っているようです。
各ソフトは下記のとおりです。(内容はヘルプを引用しています)
【bogofilter】
Filename: bogofilter-1.2.3-1vl6.x86_64.rpm
Summary: ベイジアン解析による高速アンチスパムフィルタリング
Description:(ロボット翻訳)
Bogofilterはベイジアンスパムフィルタである。 それのその正常なモードにおいて
操作、それは、標準のインプットにおいてEメールメッセージまたは他のテキストを取る、
する 「よい」および「悪い」言葉のリストに対する統計のチェック および
リターン スパムのメッセージのisステータスコード標示 。
Bogofilterは速いアルゴリズムによってデザインされる(バークレーDBシステムを含む)、
スピードでCにおいて直接コード化されて、調整される。従って、それは生産のために使われうる
たくさんのメールを処理するサイトによって。
【gsl】
Filename: gsl-1.14-2vl6.x86_64.rpm
Summary: 数値解析用の GNU 科学技術計算ライブラリ
Description:
gsl パッケージは、科学技術計算用のライブラリ、GNU Scientific Library (GSL)
を収めたものです。GSL は C で記述された数値解析ルーチンのコレクションです。
【ecab】
Filename: mecab-0.99-1vl6.x86_64.rpm
Summary: もう一つの形態素解析器
Description:
MeCab は 京都大学情報学研究科-日本電信電話株式会社コミュニケーション
科学基礎研究所共同研究ユニットプロジェクトの一環として開発された
オープンソース形態素解析エンジンです. 言語, 辞書,コーパスに依存しない
汎用的な設計を基本方針としています. パラメータの推定に Conditional
Rondom Fields (CRF) を用いており, ChaSenが採用している隠れマルコフ
モデルに比べ性能が向上しています。また、平均的に ChaSen, Juman, KAKASI
より高速に動作します. ちなみに和布蕪(めかぶ)は, 作者の好物です.
<<<インストール>>>
インストールは apt を使ってインストールしました。
# apt-get install bogofilter
パッケージリストを読みこんでいます... 完了
依存情報ツリーを作成しています... 完了
以下の追加パッケージがインストールされます:
gsl mecab
以下のパッケージが新たにインストールされます:
bogofilter gsl mecab
アップグレード: 0 個, 新規インストール: 3 個, 削除: 0 個, 保留: 0 個
1885kB のアーカイブを取得する必要があります。
展開後に 5727kB のディスク容量が追加消費されます。
続行しますか? [Y/n]y
取得:1 http://updates.vinelinux.org 6/i386/main gsl 1.14-2vl6 [957kB]
取得:2 http://updates.vinelinux.org 6/i386/plus mecab 0.99-1vl6 [392kB]
取得:3 http://updates.vinelinux.org 6/i386/plus bogofilter 1.2.3-1vl6 [536kB]
1885kB を 0s で取得しました (5676kB/s)
変更を適用しています...
準備中 ############################## [100%]
更新/インストール中
mecab-0.99-1vl6.i686 ############################## [100%]
gsl-1.14-2vl6.i686 ############################## [100%]
bogofilter-1.2.3-1vl6.i686 ############################## [100%]
完了
<<<プログラムの設定や環境ファイルを変更する>>>
bogofilterの環境設定は /etc/bogofilter.cf で変更します。
# vi /etc/bogofilter.cf
変更点1
spam_header_name=X-Bogosity のコメントアウトを外します。
これにより、bogofilterを通ったメールのヘッダーに「X-Bogosity」の行がつきます。
変更前
#spam_header_name=X-Bogosity
変更後
spam_header_name=X-Bogosity
変更点2
spam_subject_tag のコメントアウトを外します。
これにより、迷惑メールと判定したメールの件名に「[SPAM]」がつきます。
メーラの振り分け機能で『件名に[SPAM]があるものは迷惑メールに移動』としておけば便利ですね。
変更前
##spam_subject_tag=***SPAM***
変更後
spam_subject_tag=[SPAM]
ソフトの準備は以上です
次に環境の設定をします。
届いたメールがbogofilterを通るようにします。
利用するソフトは「procmail」です。標準でインストールされていると思いますが、まだの場合は
# apt-get install procmail
として、インストールしてください。最新バージョンは「3.22-4vl6(2016/11/08現在)」です。
利用ユーザの /home に仕掛けをします。
下記内容を登録してください。
# su - hogehoge
$ vi .forward
---- .forward ----
"|exec /usr/bin/procmail -f- || exit 75 #~/Maildir/"
---- EOF ----
これで、届いたメールは順次procmailに送られます。
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
procmailのレシピを書きます。
procmailではいろいろな条件を元にいろいろ振り分けることができるソフトです。
今回は、『bogofilterに送って、迷惑メール判定をしてもらい、迷惑メールなら「迷惑メールフォルダ」にコピーして
なおかつ、自分のフォルダにも保存する』という事にします。
迷惑メールと判定すれば「/dev/null」で削除してもよいのですが、判定間違いもありますので件名に[SPAM]がついた状態で
ユーザに確認してもらうという方法を取っています。
このファイルは同じくユーザの /home に仕掛けます。
$ vi .procmailrc
---- .procmailrc ----
#
## .PROCMAILRC 振り分けルール
##
#
## 受信メールに対して bogofilterをかける
##
MAILDIR=$HOME/Maildir/
DEFAULT=$MAILDIR
# Copy mail to access licens 066
UMASK=023
:0Efw
| /usr/bin/bogofilter -p -e -l -u
# -f)isher, -p)assthrough -u)pdate, -l)og, -e)xitcode 0 for spam and ham
# -v)erbose
#### bigin error catcher ####
#
:0e
{
EXITCORD=75
HOST
}
#### end error catcher ####
### spam move directory
:0c:
* ^X-Bogosity: Spam
/usr/local/share/bogofilter/spam/.
:0
$DEFAULT
---- EOF ----
これで、来たメールは forward から procmail を経由して bogofilter を通ります。
通ったメールは利用者に届くようになりました。
<<<運用準備>>>
bogofiletrは学習しないと何もできません。
そこで、今まで集まった迷惑メールを一つのフォルダに集めることにします。
正しいメールも必要なので用意しましょう。メーリングリストなどで流れてくるメールなどが良いと思います。
届いたメールの中で、迷惑メールと間違って迷惑メールと判定されたものを用意し、それぞれフォルダに格納して
学習をさせていきたいと思います。
メールのファイル化はそれぞれのメールソフトを確認してください。「エクスポート」機能があれば簡単にファイル化できます。
また、ドラックアンドドロップでファイル化するものもあります。
学習用のメール保存するフォルダを用意します。
今回は下記フォルダに保存することにしました。
間違って迷惑メールと判定されたもの(hamメール)
/usr/local/share/bogofilter/ham/
迷惑メール(SPAMメール)
/usr/local/share/bogofilter/spam/
/*/*//*/*/*/*//*/*/*/*//*/*/*/*//*/*/*/*//*/*/*/*//*/*/*/*//*/*/*/*//*/*/*/*//*/*
登録サイトを作ります。
メールファイルをサーバに保存する方法として、WEBを使うことにします。
今回はファイルをドラッグアドドロップで取り込めるようにしてみました。
HTML5を使うと実現しました。しかし、完全なコピペのため詳細は不明です。 m(__)m
ファイル構成は2つです。
表示のファイルとバックエンドで作業するファイルです。表示のファイルはHTML形式て書いていますが、
JQueryを使っています。バックエンドはPHP5です。多少はいじっていますので、詳細はコピペ元をご覧ください。
参照元 IT-viewさん(多謝!)
html5部分 Jqueryを使ってHTML5ドラッグ&ドロップファイルアップロード
php5部分 phpファイルアップロードサーバー保存
完成サイト
---- hupload.htm ----
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<!-- php http://www.it-view.net/php-file-upload-server-save-296.html -->
<!-- html5 http://www.it-view.net/drag-and-drop-file-upload-jquery-178.html -->
<!- jQueryライブラリをHTMLに引用 -->
<script
src="http://code.jquery.com/jquery-3.0.0.js"
integrity="sha256-jrPLZ+8vDxt2FnE1zvZXCkCcebI/C8Dt5xyaQBjxQIo="
crossorigin="anonymous">
</script>
<style>
#dragandrophandler
{
border:2px dotted #0B85A1;
width:500px;
color:#92AAB0;
text-align:left;vertical-align:middle;
padding:10px 10px 10 10px;
margin-bottom:10px;
font-size:200%;
}
.progressBar {
width: 200px;
height: 22px;
border: 1px solid #ddd;
border-radius: 5px;
overflow: hidden;
display:inline-block;
margin:0px 10px 5px 5px;
vertical-align:top;
}
.progressBar div {
height: 100%;
color: #fff;
text-align: right;
line-height: 22px; /* same as #progressBar height if we want text middle aligned */
width: 0;
background-color: #0ba1b5; border-radius: 3px;
}
.statusbar
{
border-top:1px solid #A9CCD1;
min-height:25px;
width:700px;
padding:10px 10px 0px 10px;
vertical-align:top;
}
.statusbar:nth-child(odd){
background:#EBEFF0;
}
.filename
{
display:inline-block;
vertical-align:top;
width:250px;
}
.filesize
{
display:inline-block;
vertical-align:top;
color:#30693D;
width:100px;
margin-left:10px;
margin-right:5px;
}
.abort{
background-color:#A8352F;
-moz-border-radius:4px;
-webkit-border-radius:4px;
border-radius:4px;display:inline-block;
color:#fff;
font-family:arial;font-size:13px;font-weight:normal;
padding:4px 15px;
cursor:pointer;
vertical-align:top
}
</style>
<title>Ham mail sample upload</title>
</head>
<body>
<!-- divタグをアップロード処理したいの所に追加 -->
<h2><span style="background:aqua;">正しい(Ham)メールをアップロードします。</span><br>拡張子はemlもしくはtxtでお願いします。</h2> <a href='index.htm'>TOPに戻る</a><br><br>
<div id="dragandrophandler">ここにドロップしてください。</div>
<br><br>
<div id="status1"></div>
<!-- jQuery AJAX APIを使って、FormData()をサーバーに送信 -->
<script>
function sendFileToServer(formData,status)
{
var uploadURL ="http://アップロードサイト/hamupload.php"; //Upload URL
var extraData ={}; //Extra Data.
var jqXHR=$.ajax({
xhr: function() {
var xhrobj = $.ajaxSettings.xhr();
if (xhrobj.upload) {
xhrobj.upload.addEventListener('progress', function(event) {
var percent = 0;
var position = event.loaded || event.position;
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
//Set progress
status.setProgress(percent);
}, false);
}
return xhrobj;
},
url: uploadURL,
type: "POST",
contentType:false,
processData: false,
cache: false,
data: formData,
success: function(data){
status.setProgress(100);
$("#status1").append("ファイルをアップロードしました。<br>");
}
});
status.setAbort(jqXHR);
}
var rowCount=0;
function createStatusbar(obj)
{
rowCount++;
var row="odd";
if(rowCount %2 ==0) row ="even";
this.statusbar = $("<div class='statusbar "+row+"'></div>");
this.filename = $("<div class='filename'></div>").appendTo(this.statusbar);
this.size = $("<div class='filesize'></div>").appendTo(this.statusbar);
this.progressBar = $("<div class='progressBar'><div></div></div>").appendTo(this.statusbar);
this.abort = $("<div class='abort'>Abort</div>").appendTo(this.statusbar);
obj.after(this.statusbar);
this.setFileNameSize = function(name,size)
{
var sizeStr="";
var sizeKB = size/1024;
if(parseInt(sizeKB) > 1024)
{
var sizeMB = sizeKB/1024;
sizeStr = sizeMB.toFixed(2)+" MB";
}
else
{
sizeStr = sizeKB.toFixed(2)+" KB";
}
this.filename.html(name);
this.size.html(sizeStr);
}
this.setProgress = function(progress)
{
var progressBarWidth =progress*this.progressBar.width()/ 100;
this.progressBar.find('div').animate({ width: progressBarWidth }, 10).html(progress + "% ");
if(parseInt(progress) >= 100)
{
this.abort.hide();
}
}
this.setAbort = function(jqxhr)
{
var sb = this.statusbar;
this.abort.click(function()
{
jqxhr.abort();
sb.hide();
});
}
}
function handleFileUpload(files,obj)
{
for (var i = 0; i < files.length; i++)
{
var fd = new FormData();
fd.append('file', files[i]);
var status = new createStatusbar(obj); //Using this we can set progress.
status.setFileNameSize(files[i].name,files[i].size);
sendFileToServer(fd,status);
}
}
$(document).ready(function()
{
var obj = $("#dragandrophandler");
obj.on('dragenter', function (e)
{
e.stopPropagation();
e.preventDefault();
$(this).css('border', '2px solid #0B85A1');
});
obj.on('dragover', function (e)
{
e.stopPropagation();
e.preventDefault();
});
obj.on('drop', function (e)
{
$(this).css('border', '2px dotted #0B85A1');
e.preventDefault();
var files = e.originalEvent.dataTransfer.files;
//We need to send dropped files to Server
handleFileUpload(files,obj);
});
$(document).on('dragenter', function (e)
{
e.stopPropagation();
e.preventDefault();
});
$(document).on('dragover', function (e)
{
e.stopPropagation();
e.preventDefault();
obj.css('border', '2px dotted #0B85A1');
});
$(document).on('drop', function (e)
{
e.stopPropagation();
e.preventDefault();
});
});
</script>
</body>
</html>
---- EOF ----
これと同じもので迷惑メールの分も作ります。
ファイル名の変更とアップロードの時に呼び出すphpファイル名を変更してください。
迷惑メール登録サイト完成図
/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*
phpファイルを作ります。
保存場所である「$dd」変数と「$dr」変数の確認を忘れずにしてください。
間違って迷惑メールと判定されたものに関しては、迷惑メールフォルダにずっと保存しておくことは
再度学習するときに誤りを覚えてしまうので、正しいメールの方にコピーするようにしました。
その際、転送されたものだとわかるようにファイル名の頭に「S」を付けています。
また、誤り訂正の学習の為転送されたものを学習できるように移動した日時にファイルの更新日時を
現在の時間に変更するようにしました。
---- hamupload.php ----
<?php
ini_set('display_errors',1);
$dd = "/usr/local/share/bogofilter/";
$dr = $dd . "ham/";
if (is_uploaded_file($_FILES["file"]["tmp_name"])) {
$kyou=date('Ymd');
$kazu=count(glob($dr . $kyou . "*"));
$FineN=$kyou . sprintf("%02d",$kazu++);
if (move_uploaded_file($_FILES["file"]["tmp_name"], $dr . $FineN)) {
echo $_FILES["file"]["name"] . "をアップロードしました。";
chmod("files/" . $_FILES["file"]["name"], 0644);
# spam folder check ham mail.
//送信したファイルからMessage-IDを取得する
$HamWord=trim(shell_exec('grep ^Message- ' . $dr . $FineN));
//spamフォルダにhamメールのmessage-IDがないか調べる
$chHam='grep -l ^\'' . $HamWord . '\' ' . $dd . 'spam/*';
$HitHam=trim(shell_exec($chHam));
if (strlen($HitHam)>0) {
//spamフォルダにhamメールがあったと判定した場合
//spamフォルダからhamフォルダに移動し、更新日時を変更する
exec('mv ' . $HitHam . ' ' . $dr . 'S' . substr($HitHam, -10));
exec('touch ' . $dr . 'S' . substr($HitHam, -10));
}
} else {
echo "ファイルをアップロードできません。";
}
} else {
echo "ファイルが選択されていません。";
}
?>
---- EOF ----
迷惑メールはファイルを保存するだけなので、コピペ元とよく似ています。
---- spamupload.php ----
<?php
$dr="/usr/local/share/bogofilter/spam/";
if (is_uploaded_file($_FILES["file"]["tmp_name"])) {
$kyou=date('Ymd');
$kazu=count(glob($dr . $kyou . "*"));
$FineN=$kyou . sprintf("%02d",$kazu++);
if (move_uploaded_file($_FILES["file"]["tmp_name"], $dr . $FineN)) {
echo $_FILES["file"]["name"] . "をアップロードしました。";
chmod("files/" . $_FILES["file"]["name"], 0644);
} else {
echo "ファイルをアップロードできません。";
}
} else {
echo "ファイルが選択されていません。";
}
?$gt;
---- EOF ----
これで学習ファイルをサイトから保存できるようになりました。
複数のファイルをドラックアンドドロップで保存できます。
<<<定期的に学習させる>>>
迷惑メールと間違って迷惑メールになったものの保存ができるようになりました。
procmailにより迷惑メールは自動的に迷惑メールフォルダに保存されるようになっています。
これは、メールユーザにより学習する内容に誤差があります。
迷惑メールのたくさん来る人とこない人では当然、来ない人の方が判定は常に甘くなります。
そこで、せっかく来た迷惑メールも資産になるようにみんなで学習し合うという事にしました。
定期的に保存フォルダを巡回し、知らない迷惑メールを学習するという方法です。
もちろん間違った迷惑メールもありますが、ユーザからの間違ったメールという申告があれば
自動的に迷惑メールフォルダから間違って迷惑メールになったフォルダに移動するため、正しい
学習も可能となります。
ファイル名を「bogo.sh」としました。
---- bogo.sh ----
#/bin/sh
# bogofilter system stating command
# 1: $cat /usr/local/share/bogofilter/spam/* | bogofilter -s -N -v -M
# 2: $cat /usr/local/share/bogofilter/ham/* | bogofilter -n -S -v -M
# 3: $crontab -e
# (イニシャルのアルファベットの番号) */3 * * * /bin/sh ./bogo.sh > /dev/null
MYDIR=$HOME
# .bogofilter folder check
if [ ! -d ./.bogofilter ] ; then
cat /usr/local/share/bogofilter/spam/* | bogofilter -s -N -v -M
cat /usr/local/share/bogofilter/ham/* | bogofilter -n -S -v -M
fi
## spam ##
/bin/find /usr/local/share/bogofilter/spam -type f -mmin -180 -exec sh -c 'cat {} | /usr/bin/bogofilter -s -N -M ' ';'
## ham ##
/bin/find /usr/local/share/bogofilter/ham -type f -mmin -180 -exec sh -c 'cat {} | /usr/bin/bogofilter -n -S -M ' ';'
# log
/bin/date >> bogo.log
/usr/bin/bogoutil -w $MYDIR/.bogofilter/wordlist.db .MSG_COUNT >> bogo.log
---- EOF ----
logに関しては、監視用として使っていただき、安定していると思えばコメントアウトした方が良いと思います。
学習方法はcronで定期的にshファイルを実行するという方法です。
ただ、これには1つ問題があり、全員が同時間で学習を始めるとサーバへの負担がかなりのものとなり、
サーバがダウンする可能性があります。
そこで、3時間おきという設定をし、この180分の中にバラけるようにcronを設定することが重要となります。
いろいろな手法があると思いますが、今回はユーザの一文字目を基本的に使うという手法を取りました。
確認表
分 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
29 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
アルファベット |
A |
B |
C |
D |
E |
F |
G |
H |
I |
J |
K |
L |
M |
N |
O |
P |
Q |
R |
S |
T |
U |
V |
W |
X |
Y |
Z |
3時間おきのパターン
パターン名 |
1回 |
2回 |
3回 |
4回 |
5回 |
6回 |
7回 |
8回 |
Aパターン |
0 |
3 |
6 |
9 |
12 |
15 |
18 |
21 |
Bパターン |
1 |
4 |
7 |
10 |
13 |
16 |
19 |
22 |
Cパターン |
2 |
5 |
8 |
11 |
14 |
17 |
20 |
23 |
(例) 最初が「a」で始まるユーザが複数いる場合のパターンです
アルファベットが「a」なので、分は「1」となります。
後は、上記の表に照らし合わせてcronを作成します。
cron表
メールアドレス |
検討パターン |
分計算 |
cron |
a_bcd@hogehoge.co.jp |
Aパターン |
a[1]=1 |
1 */3 * * * |
a_efg@hogehoge.co.j |
Bパターン |
a[1]=1 |
1 1,4,7,10,13,16,19,22 * * * |
a_hig@hogehoge.co.j |
Cパターン |
a[1]=1 |
1 2,5,8,11,14,17,20,23 * * * |
a_ijk@hogehoge.co.j |
分を30分ずらしてのAパターン |
a[1]+30=31 |
31 */3 * * * |
a_lmn@hogehoge.co.j |
分を30分ずらしてのBパターン |
a[1]+30=31 |
31 1,4,7,10,13,16,19,22 * * * |
a_opq@hogehoge.co.j |
分を30分ずらしてのCパターン |
a[1]+30=31 |
31 2,5,8,11,14,17,20,23 * * * |
a_stu@hogehoge.co.j |
[a]と[s]を足して60を引いた分とAパターン |
60-(a[1]+s[29])=30 |
30 */3 * * * |
a_vwx@hogehoge.co.j |
[a]と[v]を足して60を引いた分とBパターン |
60-(a[1]+v[22])=37 |
37 1,4,7,10,13,16,19,22 * * * |
a_yz@hogehoge.co.jp |
[a]と[y]を足して60を引いた分とCパターン |
60-(a[1]+y[25])=34 |
34 2,5,8,11,14,17,20,23 * * * |
cronは以下のようになります。(a_bcd@hogehoge.co.jpの場合)
1 */3 * * * /bin/sh ./bogo.sh > /dev/null
以上で、設定は終わりです。最後に動作をまとめてみます。
1:メールが届くとfechmailがprcmailを経由してbogofilterに送られ、迷惑メールを判定する。
2:迷惑メールの場合は、迷惑メールフォルダにコピーを置く。
3:メールの判定が間違っていたらサイトにて訂正登録をする。
4:3時間おきに迷惑メールフォルダとまちがっって迷惑メールになったフォルダを読み込み学習する。
5:3時間おきの学習記録はログが残る。
2016/11/18 記述
Let's PC の Topに戻る
ホームページのTopに戻る