zspam是一款基于内容的邮件打分系统,用于识别垃圾邮件/钓鱼邮件. 最新版本 5.19
能处理各种类型的附件, 包括: 30多种压缩包, 40多种文档, 20多种图片(OCR, 二维码)
支持中文简体,繁体,日文,韩文,越南文,泰文,希伯来文,波斯文,拉丁等文字. 在线体验反垃圾效果
docker pull mailutils/antispam
@@@ | 免费版 | 收费版 |
基于贝叶斯的统计 | 支持 | 支持 |
基于向量机的统计 | 支持 | 支持 |
垃圾特征库 | 支持 | 支持 |
成人特征库,敏感类特征库 | ||
自定义多个统计目标 | 支持 | |
样本学习 | 支持 | |
关键字匹配(支持多个自定义目标) | 支持 | |
系统给出的判定 | 支持 | |
邮件解析结果 | 支持 | |
附件(文档/压缩包)解析结果 | 支持 | |
二维码识别 | 支持 | 支持 |
OCR文字识别 | 支持 | 支持 |
本系统应该只是邮件反垃圾系统的一个模块,不可能也不应该过滤全部邮件
如下类别的邮件不应该使用本系统(的垃圾判定)
应优先使用其他手段过滤垃圾邮件
如果不使用 "文档转文本服务", 可以删除 document_text.cf
如果不使用 "垃圾邮件识别服务", 可以删除 spamd.cf
/home/spam/etc/service/spamd.cf
spam_license = # 每个进程最多分配内存限制 max-memory = 4G server-command = /home/spam/bin/spamd # 服务端口 server-service = 0:25063 # server-service = 127.0.0.1:25063 # server-service = /home/spam/run/spamd.socket # 进程退出前, 等待多少秒; 0 立即退出 server-stop-on-softstop-after = 10 # 处理多少邮件后, 进程退出; 0 不限制 server-service-max-use = 1000000 # 一个邮件最多处理多少秒, 超时后, 进程退出 server-request-max-run-time = 10 # 日志, syslog 模式 server-log = syslog,mail # 日志, 输出到标准出错输出 # server-log = # 并发进程数 server-proc-count = 3 # 每进程并发线程数 spam_concurrency_limit = 10 # 特征库文件, bayes方法可以可以是多个, svm方法只能有一个 #spam_db = ./db/const.db,./db/const2.db,./db/const9.db spam_db = /home/spam/db/spam.db # touch ./run/reloaddb_event_file, 会导致spamd重新加载特征库 # spam_reloaddb_event_file = ./run/reloaddb_event_file # 其他目标的统计识别(和 spam_db 类似), 支持自定义多个目标 # spam_db_for_testone = ./db/testone111.db # spam_db_for_xxx = ./db/xxx123.db # 关键字匹配模块, 支持自定义多个目标 # spam_keyword_for_something = ./db/chengren_keyword,./db/gggg_kd # spam_keyword_for_antoher = ./db/zhengzhi_keyword # 是否获取邮件及其附件的解析结果 spam_mail_parser_info = no # 是否获取系统判定的其他结论 spam_mail_conclusion_info = no # # 下面是附件文档处理配置 # document_text_antispam_mode = 1 # 启用 fork 运行不稳定的模块 document_text_robust_enable = yes # 资源大小限制, 超过这个限制则不处理 document_text_resource_size_limit = 100M # 除了 txt, 这些格式的文件作为文本处理, 逗号分割 # document_text_extra_text_formats = js,css,cpp # 处理资源个数限制 document_text_resource_count_limit = 1024 # 深度限制 document_text_depth_limit = 3 # 处理文档个数限制 document_text_office_convert_count_limit = 12 # 缓存目录, 空表示禁用缓存, 目前只缓存图片处理结果 document_text_cache_pathname = /home/spam/cache_dir_image/ # 一个 pdf, 处理时间限制 document_text_pdf_time_limit = 3 # 一个图片, 处理时间限制 document_text_image_time_limit = 3 # 一个图片, 大小限制 document_text_image_size_limit = 10M # 一个图片, 限制 (width + height) document_text_image_width_height_sum_limit = 20000 # 是否启用图片文字识别(OCR), 请注意: 图片文字识别速度很慢 document_text_ocr_enable = no # 一个图片, OCR处理时间限制(秒) document_text_ocr_time_limit = 3 # OCR文件个数限制 document_text_ocr_file_count_limit = 3 # OCR文字数量限制 document_text_ocr_char_count_limit = 500 # OCR太慢, 图片宽度限制 document_text_ocr_width_limit = 3000 # OCR太慢, 按高分片处理, 最多分多少片 document_text_ocr_height_slice_limit = 3 # OCR太慢, 分片后, 每片处理的高度为 document_text_ocr_height_per_slice = 100 # document_text_tesseract_lang = chi_sim # document_text_tesseract_psm = 6 # document_text_tesseract_config_TESSERACT_CONFIG_NAME = TESSERACT_CONFIG_VALUE # 是否启用图片二维码识别(QRCODE) document_text_qrcode_enable = yes # 二维码识别有的时候会有点慢, 限制 (width + height) document_text_qrcode_width_height_sum_limit = 10000 # QRCODE文件个数限制 document_text_qrcode_file_count_limit = 12
1, 客户端 连接(2种方法): 1) socket 文件 ./var/socket/spamd 2) 端口 0:25063 ### 默认配置 2, 客户端 输入(2种方法): 1) score[一个空格]邮件文件路径\r\n ### 请注意, 服务在docker内运行 2) score[一个空格]{邮件数据长度}\r\n邮件数据\r\n 3, 服务端 返回(3个情况): 1) OK[一个空格]得分\r\n 2) OK[一个空格]得分 {额外数据(JSON)的长度}\r\nJSON数据\r\n 3) ERR[一个空格]error_msg\r\n 4, 客户端 继续(2种): 1)关闭连接 2)回到 2
所有的日志输出默认都是 SYSLOG, MAIL
本镜像系统没有提供日志服务, 见docker的参数 -v /dev/log:/dev/log
docker run --name container_name -d -p 25062:25062 -p 25063:25063 -p 25065:25065 -v /dev/log:/dev/log image_name
docker run --name container_name image_name /bin/sleep 36500d
docker exec -it container_name /home/spam/bin/master.sh reload
本镜像基于操作系统: debian 11.6
docker exec -it container_name /bin/zy_spamc --help
本文提到的zy_spamc命令都是在容器内执行, 需要注意路径的问题
大部分情况需要修改参数, 风格为:
zy_spamc [ -config config_file [ -config config_file ] ] [ -some_key some_value [ -some_key1 some_value1 ] ] \ do_something do_something_arg1 ... \ [ -config config_file [ -config config_file ] ] [ -some_key some_value [ -some_key1 some_value1 ] ]
# 作为一个命令, 配置文件spamd.cf中的配置项都可以作为参数使用
# 如果参数中出现(可以多组) -config somepath.cf, 那么会立即加载配置文件somepath.cf
# 遵循规则: 后加载的配置项覆盖先加载的配置项
# 遵循规则: 命令行上的配置项覆盖配置文件中的配置项
命令 /home/spam/bin/test_spamd.sh 用于在线测试 spamd 服务
可以在容器外执行
# docker cp container_name:/home/spam/bin/test_spamd.sh /somepath/ ./test_spamd.sh [ 127.0.0.1:25063 ] eml_file [ eml_file1 [ ... ] ] [ eml_dir [ ...] ]
关键字匹配模块, 支持自定义多个目标
支持配置多个"关键字文件"
在 spamd.cf 中配置
# spam_keyword_for_something = var/spamdb/chengren_keyword,var/spamdb/gggg_kd # spam_keyword_for_antoher = var/spamdb/zhengzhi_keyword # spam_keyword_for_testone = var/spamdb/testone
结论在服务返回的 JSON 中, 形如:
{ "keyword": [ {"for":"something","part":"TEXT","word":"匹配的关键字"}, {"for":"antoher","part":"SUBJECT","word":"匹配的关键字"}, {"for":"testone","part":"SUBJECT","word":"匹配的关键字"} ] }
如果没有配置, 或所有的关键都不存在, 则 JSON 中可能没有 "keyword" 属性
### 是文本 关键字第一个 关键字第二个忽略行首尾空格 第三个 关键 字 ### 行首为三个连续的 # 符号, 则忽略 ### 忽略空行 ### 不支持任何转义
如果关键字文件有10万行以上,建议先编译再使用
zy_spamc keyword_compile 第一个关键字文件 [ 第二个 [ 第三个 ... ] ] 目标文件
zy_spamc keyword 关键字文件1,关键字文件2[, ...] 邮件或邮件目录
在spamd.cf 中配置
是否获取系统判定的其他结论
spam_mail_conclusion_info = yes
结论在服务返回的 JSON 中, 形如:
{ "conclusion": [ "TO_MAIL_IN_TO_NAME", "MIME_ONLY_HTML", "HTML_TABLE_WORD_TD_COUNT:120", "SUBJECT_ONE_TOKEN", "HEADER_NO_MAILER_USER_AGENT" ] }
ATT_COUNT:数量 | 附件个数(不包含内嵌图片附件) 例子, 12个附件, ATT_COUNT:12 |
MAILER:FOXMAIL | X-Mailer 是 foxmail |
MAILER:OUTLOOK | X-Mailer 是 outlook |
MIME_ONLY_HTML | 仅有 HTML |
MIME_ONLY_PLAIN | 仅有 PLAIN |
OUTLOOK_ONLY_HTM | OUTLOOK, 仅有 HTML, 基本可以判垃圾 |
OUTLOOK_ONLY_PLAIN | OUTLOOK, 仅有 PLAIN, 基本可以判垃圾 |
HEADER_REFERENCES_COUNT:数量 | References 里条目数 |
FROM_MAIL:发件人 | 发件人地址 |
FROM_DOMAIN:发件人域名 | 发件人地址的域名 |
FROM_DOMAIN_NONE | 发件人没域名 |
FROM_MAIL_EMPTY | 发件人地址为空, 或不存在 From: |
FROM_DOMAIN_IN_MESSAGE_ID | From的域名出现在 Message-id 中 |
FROM_DOMAIN_NOT_IN_MESSAGE_ID | From的域名没出现在 Message-id 中 |
FROM_DOMAIN_NOT_IN_TO_DOMAIN | From的域名出现在 to 的域名 中 |
FROM_DOMAIN_NOT_IN_TO_DOMAIN | FROM_DOMAIN 没出现在 TO_DOMAIN |
FROM_DOMAIN_NE_SENDER_DOMAIN | From 的域名 和 X-Sender的域名不同 |
FROM_DOMAIN_EQ_SENDER_DOMAIN | From 的域名 和 X-Sender的域名相同 |
FROM_IN_TO | 发件人在收件人中 |
FROM_IN_TO_NAME | 发件人的地址在收件人的名字中 |
FROM_MAIL_IN_BODY | 发件人地址在正文中 |
FROM_MAIL_NOT_IN_BODY | 发件人地址不在正文中 |
FROM_NAME_EMPTY | 发件人没名字, 或不存在 From: |
FROM_NAME_NO_EQ | (原始)发件人名没有 =? |
FROM_NAME_DOMAIN_NE_FROM_DOMAIN 发件人名字的域名 和 发件人的域名不同 | |
FROM_NAME_DOMAIN_EQ_FROM_DOMAIN/FROM_NAME_NE_FROM 发件人名字的域名 和 发件人的域名相同/同时/发件人的名字和发件人不同 | |
FROM_NE_RECEIPT | From 和 Disposition-Notification-To 不同 |
FROM_EQ_RECEIPT | From 和 Disposition-Notification-To 相同 |
FROM_NE_REPLY_TO | From和 Replay-To 不同 |
FROM_EQ_REPLY_TO | From和 Replay-To 相同 |
RECEIPT_NE_REPLY_TO | Disposition-Notification-To 和 Reply-To 不同 |
RECEIPT_EQ_REPLY_TO | Disposition-Notification-To 和 Reply-To 相同 |
SENDER_NE_RECEIPT | X-Sender 和 Disposition-Notification-To 不同 |
SENDER_EQ_RECEIPT | X-Sender 和 Disposition-Notification-To 相同 |
SENDER_NE_REPLY_TO | X-Sender 和 Reply-To 不同 |
SENDER_EQ_REPLY_TO | X-Sender 和 Reply-To 相同 |
TO_COUNT:数量 | To 个数 |
TO_ONE/TO_MAIL_EMPTY | 一个 TO, 且没有邮件地址 |
TO_ONE/FROM_EQ_TO | 一个 TO, 且其地址和 From 的地址相同 |
TO_UID_IN_TO_NAME | To 邮件地址@前面的部分出现在名称中 |
TO_MAIL_IN_TO_NAME | To 邮件地址出现在名称中 |
TO_MAIL:收件人地址 | 收件人地址 |
TO_DOMAIN:收件人域名 | 收件人地址的域名 |
TO_MAIL_NOT_IN_TO_NAME/TO_DOMAIN_IN_TO_NAME To 邮件地址没出现在名称中, 且域名出现在名称中 | |
TO_DOMAIN_COUNT | To 中不同域名的个数 |
TO(FROM_DOMAIN)_COUNT | 和 From 的域名同域的 To 的个数 |
TO_DOMAIN_ONE/FROM_DOMAIN_EQ_TO_DOMAIN | 收件人中只有一个域, 且和发件人域相同 |
FROM_DOMAIN_NOT_IN_TO_DOMAIN | From 域没出现在 To 中 |
SUBJECT_FWD_RE | 主题开始字符串是 RW/FWD/RE/转发/回复/答复/轉寄/回覆 |
SUBJECT_LEN:长度 | 主题长度 |
SUBJECT_IN_BODY | 主题出现在正文 |
SUBJECT_ONE_TOKEN | 主题只有一个TOKEN |
SUBJECT_NO_EQ | 原始主题没有 =? |
SUBJECT_A1B2C_TOKEN | 主题中存在类似字段 ABC123D56EFG 或 12BDAF6756SGFA8013 |
SUBJECT_A1B2_TOKEN | 主题中存在类似字段 ABC123DSFSDF56 或 12BDAF6756SGFA |
BODY_LEN:长度 | 正文长度(去掉 html 标签) |
BODY_NCR_COUNT:数量 | 正文中 ncr 个数 |
INLINE_IMAGE_COUNT:数量 | 内嵌图片附件个数 |
INLINE_IMAGE_UNKNOWN_TYPE | size最大的内嵌图片的真实类型未知 |
INLINE_IMAGE_HEIGHT:数量 | size最大的内嵌图片附件的高度 |
INLINE_IMAGE_WIDTH:数量 | size最大的内嵌图片附件的宽度 |
ATT_COUNT:数量 | 附件个数(不包含内嵌图片附件) |
ATT_SUFFIX:附件名后缀 | 附件名的后缀(文件类型) |
ATT_NAME_A1B2C_TOKEN | 附件名中存在类似字段 ABC123D56EFG 或 12BDAF6756SGFA8013 |
ATT_NAME_A1B2_TOKEN | 附件名中存在类似字段 ABC123DSFSDF56 或 12BDAF6756SGFA |
ATT_NAME_IN_BODY | 附件名出现在正文 |
ATT_NAME_IN_SUBJECT | 附件名出现在主题 |
ATT_NAME_LEN_EQ0 | 附件名长度为 0 |
ATT_NAME_LEN_GT0_LT3 | 附件名长度为 大于 0, 小于 3 |
ATT_NAME_LEN_GT256 | 附件名长度为 大于 256 |
ATT_NAME_ONE_TOKEN | 附件名只有一个字段 |
ATT_NAME_CONSECUTIVE_BLANK_GT10 | 附件名连续的空格(或 .)超过 10 个 |
FIRST_TO_IN_HTML_ATT | 第一个收件人出现在HTML类型的附件中 |
URL_COUNT:数量 | URL个数 |
HTML_URL_AT | 网址中有字符 |
FIRST_TO_IN_URL | 网址中有第一个收件人地址 |
HTML_URL_NO_MAILTO | 网址中没有 mailto: |
HTML_URL_CONTENT_MISMATCHED | A(AREA)标签的显示名称是url地址,且和href不一样 |
HTML_DATA_IMAGE | html有内嵌图片(data:image) |
HTML_ATTRIBUTE_NOT_ABC | 存在非字母的 HTML 属性 |
HTML_FONT_SPECIAL | 存在特殊的干扰阅读的html属性 |
HTML_FONT_COLOR_COUNT:数量 | 有 color 属性的 font 标签的个数 |
HTML_FONT_SIZE0_COUNT:数量 | size 为 0 的 font 标签个数 |
HTML_FONT_SIZE1_COUNT:数量 | size 为 1 的 font 标签个数 |
HTML_FONT_SIZE2_COUNT:数量 | size 为 2 的 font 标签个数 |
HTML_FONT_SIZE5_COUNT:数量 | size 为 5 的 font 标签个数 |
HTML_FONT_SIZE6_COUNT:数量 | size 为 6 的 font 标签个数 |
HTML_FONT_SIZE7_COUNT:数量 | size 为 7 的 font 标签个数 |
HTML_STYLE_COLOR_COUNT:数量 | style 中有 color 属性的标签的个数 |
HTML_STYLE_DISPLAY | style 中有 display 属性 |
HTML_STYLE_POSITION_ABSOLUTE | style 中有 position:absolute |
HTML_STYLE_SIZE0_COUNT:数量 | style 中有 size 为 0 属性的标签的个数 |
HTML_STYLE_SIZE_COUNT:数量 | style 中有 size 属性的标签的个数 |
HTML_TABLE_WORD_TD_COUNT:数量 | table 字, 每行 td 个数(至少30) |
HTML_TAG_FORM | 有 form 标签 |
HTML_TAG_FRAME | 有 frame 标签 |
HTML_TAG_SCRIPT | 有 script 标签 |
HTML_IMAGE_WIDTH_1 | img 的 width 属性值为 1 |
HTML_IMAGE_HEIGHT_1 | img 的 height 属性值为 1 |
HTML_IMAGE_WIDTH_HEIGHT_1 | img 的 width/height 属性值都为 1 |
A_weak_malware | 低风险附件文档, 如 html, cab, jar |
A_general_malware | 一般风险附件文档, 如 ace, arj, iso, r00 |
A_high_malware | 高风险附件文档, 如 bat, com, exe, pif, scr, vbs |
A_forged_malware | 伪造的高风险附件文档, 如 abcdef.pdf.exe |
A_malicious | 附件文档里有可能含有(恶意)脚本 |
A_detected | 附件文档类型是探测得到的, 如 abcdef.zip 实际是 abcdef.rar |
A_html_refresh | 附件文档是html, 且含有 refresh 指令 |
A_html_tag_frame | 附件文档是html, 且含有 iframe 标签 |
A_html_tag_script | 附件文档是html, 且含有 script 标签 |
A_html_tag_form | 附件文档是html, 且含有 form标签 |
A_html_to_in_url | 附件文档是html, 且第一个收件人出现在url中 |
A_html_at_in_url | 附件文档是html, 且 “@” 出现在url中 |
A_html_url_file | 附件文档是html, 且 url 协议是 file:// |
A_html_data_image | 附件文档是html, 且 url 中含有图片 |
A_html_css_data_image | 附件文档是html, 且 css 中含有图片 |
A_html_url_count:数量 | 附件文档是html, url的数量 |
A_html_image_url_count | 附件文档是html, 图片url的数量 |
A_only_one_high_malware_in_tar | 附件是压缩包, 且只含有一个文件, 且这个文件是高风险文件 |
A_only_one_file_in_tar | 附件是压缩包, 且只含有一个文件 |
A_first_to_in_att | 第一个收件人在附件中 |
A_att_name_blank_gt10 | 附件名含有超过10个连续空格或 "." |
A_encrypted | 有加密类型的附件 |
ATT_IMAGE_QRCODE | 附件图片中含有二维码 |
INLINE_IMAGE_QRCODE | 内嵌图片中含有二维码 |
是否获取邮件及其附件的解析结果
spam_mail_parser_info = yes
结论在服务返回的 JSON 中, 形如:
{ "parser": { "subject_utf8":"行政部收", "from":{"address":xxx@xxx.com","name_utf8":"xxx"}, "to":[ {"address":xxx@xxx.com","name_utf8":"xxx"}, {"address":xxx2@xxx.com","name_utf8":"xxx2"} ], "text_mime":[ { "charset":"utf-8", "content":"文本<\/html>", "encoding":"quoted-printable", "readable_content":"去除html标签后的可读可显示的文本", "size":3147, "type":"text\/html", "urls":["html中的网址","网址2"] } ], "attachment_mime":[ { "charset":"", "doc_data":{这里的结构相当复杂, 见 http://linuxmail.cn/document_text/help.html }, "encoding":"base64", "size":7756, "type":"application\/octet-stream", "name_utf8":"可读的附件名" } ] } }
zy_spamc 工具, bayes 方法的使用
获得邮件a.eml, b.eml, 和 eml_path1下所有文件(假设都是邮件) 的得分
zy_spamc bayes score ./a.db,./b.db,./c.db a.eml b.eml emlpath1 zy_spamc bayes score ./a.db a.eml b.eml emlpath1
获得邮件a.eml, b.eml, 和 eml_path1下所有邮件 的得分
zy_spamc bayes score 0:25063 a.eml b.eml emlpath1
训练a.eml, b.eml, c.eml, eml_path1, eml_path2 下所有的邮件为正常邮件
zy_spamc bayes ham ./train.db a.eml b.eml eml_path1 zy_spamc bayes ham ./train.db c.eml eml_path2
提示, train.db 可以不存在, 程序会自动创建
提示, train.db是bdb格式, 不能用const格式
训练x.eml, y.eml, z.eml, eml_path8, eml_path9 下所有的邮件为垃圾邮件
zy_spamc bayes spam ./train.db x.eml z.eml eml_path9 zy_spamc bayes spam ./train.db y.eml zy_spamc bayes spam ./train.db eml_path8
合并a.db, b.db, c.db 到 train.db
zy_spamc bayes merge_db a.db train.db zy_spamc bayes merge_db b.db train.db zy_spamc bayes merge_db c.db train.db
把train.db 转换为const.db
const.db是系统自带的只读数据库格式(速度快)
zy_spamc bayes convert_db train.db const.db
同 convert_db, 且清理意义不大的 token
zy_spamc bayes clear_db train.db const.db
获得邮件a.eml, b.eml, 和 eml_path1下所有邮件 的得分
./bin/zy_spamc svm score ./svm.db a.eml b.eml emlpath1
获得邮件a.eml, b.eml, 和 eml_path1下所有邮件的得分
./bin/zy_spamc svm score 0:25063 a.eml b.eml emlpath1
训练a.eml, b.eml, c.eml, eml_path1, eml_path2 下所有的邮件为正常邮件
./bin/zy_spamc svm ham ./flat.txt a.eml b.eml eml_path1./bin/zy_spamc svm ham ./flat.txt c.eml eml_path2
提示, flat.txt 可以不存在, 程序会自动创建
训练x.eml, y.eml, z.eml, eml_path8, eml_path9 下所有的邮件为垃圾邮件
./bin/zy_spamc svm spam ./flat.txt x.eml z.eml eml_path9 ./bin/zy_spamc svm spam ./flat.txt y.eml ./bin/zy_spamc svm spam ./flat.txt eml_path8
把 flat.txt 编译为SVM模型, 存为 svm.db, 且清理意义不大的 token
./bin/zy_spamc svm model flat.txt svm.db
形如: /opt/mail/storage/some1.eml
系统假设: 客户端提供的任何文件都是邮件且可读
形如: /opt/mail/storage/
系统假设: 此目录下, 所有的(递归)文件都是邮件且可读
形如 0.700268
介于0.000000 ~ 1.000000 之间
越接近 1.0, 是垃圾邮件的可能性越大
根据作者实际经验,大于0.7的可以判定垃圾邮件,小于0.4为可以判定正常邮件
SVM 模式下, 得分只有两个值 0.000000 和 1.000000