스팸 필터 작동 방식 이해를 위한 작은 도구
Ethan Miller
Product Engineer · Leapcell

이메일을 보낼 때 가장 중요한 것은 무엇일까요? 발신자와 수신자, 하지만 그 외에는 무엇이 있을까요?
그 외에도 가장 중요한 요소는 의심할 여지 없이 스팸 필터링입니다. 모든 이메일 클라이언트에는 이 기능이 통합되어 있으며, 이 기능의 판단은 이메일의 운명, 즉 여러분이 실제로 이메일을 볼 수 있는지 여부를 결정할 수 있습니다.
이 기사에서는 스팸 필터링이 작동하는 원리를 살펴보고 스팸을 감지하는 작은 도구를 만드는 방법을 안내합니다.
또는 이 도구를 사용하여 자신의 이메일을 미리 확인하고 수정하여 스팸으로 표시되지 않도록 할 수도 있습니다 ;-).
스팸 필터 작동 방식
스팸 필터링은 종종 Apache SpamAssassin이라는 프로그램을 기반으로 합니다.
Apache SpamAssassin은 Apache Software Foundation에서 유지 관리하는 오픈 소스 스팸 탐지 플랫폼으로, 많은 이메일 클라이언트 및 이메일 필터링 도구에서 메시지를 스팸으로 분류하는 데 널리 사용되는 도구입니다.
it은 다수의 규칙, 베이지안 필터링 및 네트워크 테스트를 사용하여 주어진 이메일에 스팸 "점수"를 할당합니다. 일반적으로 점수가 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 has no Received headers" }, { "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에서 "서비스 생성"을 클릭하고 이 GitHub 프로젝트를 선택합니다.
-
"빌드 명령" 필드에 다음 명령어를 입력하여 SpamAssassin을 설치합니다.
apt-get update && apt-get install -y spamassassin
sa-update
pip install -r requirements.txt
- "제출"을 클릭합니다.
배포되면 스팸 유효성 검사를 위한 API가 생깁니다! API가 호출될 때마다 SpamAssassin을 실행하고 이메일 점수를 매겨 반환합니다.