メールを送信する際に最も重要なことは何でしょうか?送信者と受信者ですが、それ以外には?
それ以上に、最も重要な要素は間違いなくスパムフィルタリングです。すべてのメールクライアントにはこの機能が統合されており、その判断がメールの運命を左右します。つまり、あなたがそれを見るかどうかです。
この記事では、スパムフィルタリングの仕組みの原則を説明し、スパムを検出するための小さなツールを構築する方法をガイドします。
あるいは、このツールを使用して、自分のメールがスパムとしてフラグ付けされないように、事前にチェックおよび修正することもできます ;-)。
スパムフィルタリングの仕組み
スパムフィルタリングは、しばしばApache SpamAssassinと呼ばれるプログラムに依存します。
Apache SpamAssassinはApache Software Foundationによって維持されているオープンソースのスパム検出プラットフォームであり、多くのメールクライアントやメールフィルタリングツールがメッセージをスパムとして分類するために使用する広く普及したツールです。
数多くのルール、ベイジアンフィルタリング、ネットワークテストを使用して、指定されたメールにスパム「スコア」を割り当てます。一般的に、スコアが5以上のメールはスパムとしてフラグ付けされるリスクが高いです。
スパム検出のためにSpamAssassinをローカルにインストールする
SpamAssassinはLinuxでのみ実行できることに注意してください。LinuxオペレーティングシステムまたはDockerコンテナが必要です。
Debian/Ubuntuシステムでは、SpamAssassinをインストールするために次のコマンドを使用します。
apt-get update && apt-get install -y spamassassin
sa-update
sa-updateコマンドは、SpamAssassinの検出ルールを最新バージョンに更新するために使用されます。
インストールしたら、それを使用してスパムを検出できます。使い方は次のとおりです。
spamassassin -t < input_email.txt > results.txt
このコマンドはinput_email.txtをSpamAssassinに渡し、検出結果をresults.txtに書き込みます。
results.txtの内容は、SpamAssassinのスコアと理由が末尾にリストされている、次のようなものになります。
X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on 254.254.254.254
X-Spam-Level:
X-Spam-Status: No, score=0.2 required=5.0 tests=HTML_MESSAGE,
MIME_HTML_ONLY,MISSING_MID,NO_RECEIVED,
NO_RELAYS autolearn=no autolearn_force=no version=4.0.0
// ...
Content analysis details: (0.2 points, 5.0 required)
pts rule name description
---- ---------------------- --------------------------------------------------
0.1 MISSING_MID Missing Message-Id: header
-0.0 NO_RECEIVED Informational: message has no Received headers
-0.0 NO_RELAYS Informational: message was not relayed via SMTP
0.0 HTML_MESSAGE BODY: HTML included in message
0.1 MIME_HTML_ONLY BODY: Message only has text/html MIME parts
SpamAssassinをAPIでラップする
Linux以外のデバイスがSpamAssassinを使用できるようにしたり、他のワークフローに統合したりするために、APIでラップできます。
たとえば、このAPIの典型的なユースケースは次のようになります。メールの「送信」ボタンをクリックする前に、コンテンツが最初にSpamAssassin APIに送信されます。メールは、スパムではない基準を満たしている場合にのみ送信が許可されます。
次に、Pythonを使用して、subject、html_body、text_bodyという以下のメールフィールドを受け入れる簡単なAPIを作成します。これらのフィールドをSpamAssassinに渡し、検証結果を返します。
from fastapi import FastAPI
from datetime import datetime, timezone
from email.utils import format_datetime
from pydantic import BaseModel
import subprocess
def extract_analysis_details(text):
lines = text.splitlines()
start_index = None
for i, line in enumerate(lines):
if line.strip().startswith("pts rule"):
start_index = i
break
if start_index is None:
print("No content analysis details found.")
return []
data_lines = lines[start_index+2:]
parsed_lines = []
for line in data_lines:
if line.strip() == "":
break
parsed_lines.append(line)
results = []
current_entry = None
split_line = lines[start_index+1]
pts_split, rule_split, *rest = split_line.strip().split(" ")
pts_start = 0
pts_end = pts_start + len(pts_split)
rule_start = pts_end + 1
rule_end = rule_start + len(rule_split)
desc_start = rule_end + 1
for line in parsed_lines:
pts_str = line[pts_start:pts_end].strip()
rule_name_str = line[rule_start:rule_end].strip()
description_str = line[desc_start:].strip()
if pts_str == "" and rule_name_str == "" and description_str:
if current_entry:
current_entry["description"] += " " + description_str
else:
current_entry = {
"pts": pts_str,
"rule_name": rule_name_str,
"description": description_str
}
results.append(current_entry)
return results
app = FastAPI()
class Email(BaseModel):
subject: str
html_body: str
text_body: str
@app.post("/spam_check")
def spam_check(email: Email):
# assemble the full email
message = f"""From: example@example.com
To: recipient@example.com
Subject: {email.subject}
Date: {format_datetime(datetime.now(timezone.utc))}
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="__SPAM_ASSASSIN_BOUNDARY__"
--__SPAM_ASSASSIN_BOUNDARY__
Content-Type: text/plain; charset="utf-8"
{email.text_body}
--__SPAM_ASSASSIN_BOUNDARY__
Content-Type: text/html; charset="utf-8"
{email.html_body}
--__SPAM_ASSASSIN_BOUNDARY__--"""
# Run SpamAssassin and capture the output directly
output = subprocess.run(["spamassassin", "-t"],
input=message.encode('utf-8'),
capture_output=True)
output_str = output.stdout.decode('utf-8', errors='replace')
details = extract_analysis_details(output_str)
return {"result": details}
上記のコードでは、完全な出力からスコアの理由を抽出するためのヘルパー関数extract_analysis_detailsを定義しています。たとえば、結果から特定のルールをフィルタリングするなど、この関数をさらに変更できます。
このAPIをテストしてみましょう。次のパラメータを渡します。
subject
Claim Your Prize
html_body
<h2>Claim Your Prize</h2>
<p>Dear Winner:</p>
<p>Click the link below to claim your prize.</p>
text_body
Claim Your Prize
Dear Winner:
Click the link below to claim your prize.
返される結果は次のとおりです。
[
{
"pts": "0.1",
"rule_name": "MISSING_MID",
"description": "Missing Message-Id: header"
},
{
"pts": "-0.0",
"rule_name": "NO_RECEIVED",
"description": "Informational: message has no Received headers"
},
{
"pts": "3.1",
"rule_name": "DEAR_WINNER",
"description": "BODY: Spam with generic salutation of \"dear winner\""
},
{
"pts": "-0.0",
"rule_name": "NO_RELAYS",
"description": "Informational: message was not relayed via SMTP"
},
{
"pts": "0.0",
"rule_name": "HTML_MESSAGE",
"description": "BODY: HTML included in message"
}
]
ご覧のとおり、「Dear winner」というフレーズは、さまざまなスパムメールで頻繁に使用されるため、非常に疑わしいです。
このAPIツールをオンラインでデプロイする
この小さなツールをオンラインでデプロイすることで、いつでもスパムを検出できます。
このツールはPythonで記述されており、事前にApache SpamAssassinをインストールする必要があるため、AWS EC2やDigitalOceanのようなサービスのみを使用してデプロイできると思われるかもしれません。しかし、これらは高価になる可能性があり、デプロイプロセスは複雑です。
より適切なツールはありますか?
はい、Leapcellを使用してデプロイできます。
Leapcellは、Python、Go、Rustを含むさまざまな言語のデプロイをサポートしています。Dockerアーキテクチャを使用しており、さまざまな基盤ライブラリのインストールを可能にします。最も重要なのは、Leapcellは実際のAPI呼び出し回数に基づいてのみ課金するため、アイドル状態のプロジェクトを保持するのは完全に無料です。これにより、AWSやDigitalOceanのようなプラットフォームよりも大幅に安価になります。
デプロイ手順は簡単です。
-
プロジェクトをGitHubにプッシュします。
-
Leapcellで「Create Service」をクリックし、このGitHubプロジェクトを選択します。
-
「Build Command」フィールドに次のコマンドを入力してSpamAssassinをインストールします。
apt-get update && apt-get install -y spamassassin
sa-update
pip install -r requirements.txt
- 「Submit」をクリックします。
デプロイすると、スパム検証のためのAPIが利用可能になります!APIが呼び出されるたびに、SpamAssassinが実行され、メールがスコアリングされ、スコアが返されます。
Xでフォローする:@LeapcellJP
関連記事:


