smtp.sendmail的msg参数必须包含完整邮件头与正文,且头部与正文间严格用\r\n\r\n分隔;from等字段需过滤控制字符、校验邮箱、编码中文,并确保与smtp认证账号一致,否则易被拒收或入垃圾箱。

邮件头字段值不能直接拼接,必须过滤控制字符和换行符
Go 的 smtp.SendMail 不做任何输入校验,如果 From、Subject 或 To 字段里混入 \r、\n 或 \0,就会触发邮件头注入(header injection)——攻击者可伪造 Cc、Bcc 甚至插入额外 MIME 部分。这不是理论风险,QQ 邮箱和 Gmail 服务端在解析时会原样信任你传入的字符串,只要出现 \r\nTo: 就可能被当作新头部处理。
实操建议:
- 所有用户可控字段(如用户名、标题、邮箱地址)必须用
strings.TrimSpace去首尾空白,并用strings.Map过滤掉\r、\n、\0、\f、\v - 邮箱地址必须经
mail.ParseAddress解析再取.Address字段,不能直接信任前端传来的字符串(例如"admin@example.com\r\nCc:attacker@evil.com") -
Subject中文需先 UTF-8 编码再 base64 转义,格式为Subject: =?UTF-8?B?...?=\r\n;直接写中文且不编码,Outlook 和 iOS Mail 会显示乱码或截断
From 地址必须与 SMTP 认证账号一致,且需完整包含邮箱
很多开发者以为 From: 小王 <xiaowang></xiaowang> 就够了,但实际发件人标识由两个地方共同决定:SMTP 登录凭证(smtp.PlainAuth 的 username)和邮件头里的 From 字段。二者不一致时,Gmail 会静默降权,163 邮箱直接拒收,QQ 邮箱标记“高危邮件”。
实操建议:
-
username参数必须填完整邮箱,如"notify@myapp.com",不是"notify"或昵称 -
From头部值必须与username完全相同,或严格限定在同域名下(如 username 是notify@myapp.com,则From只能是notify@myapp.com或"MyApp通知" <notify></notify>) - 禁止动态构造
From为用户输入邮箱(如注册邮箱),否则 SPF 检查必失败,99% 进垃圾箱
Content-Type 和 charset 必须显式声明,且不能依赖默认值
省略 Content-Type 或只写 text/plain 不带 charset,会导致中文正文在 Outlook 或安卓原生邮件客户端中显示为方块。更严重的是,某些反垃圾网关(如 SpamAssassin)会因缺失 charset 直接给邮件 +3 分(满分 5 分即进垃圾箱)。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
实操建议:
- 纯文本邮件必须写
Content-Type: text/plain; charset=utf-8\r\n - HTML 邮件必须写
Content-Type: text/html; charset=utf-8\r\n,且正文中所有、<code>"、&需 HTML 实体转义(用html.EscapeString) - 避免手写
MIME-Version: 1.0—— 它不是必需字段,但若写了就必须紧跟在Content-Type后且换行正确,错一个\r\n就可能让整个 MIME 结构失效
头部与正文之间必须用 \r\n\r\n 分隔,且不能有多余空格或注释
这是最常被忽略的硬性协议要求。smtp.SendMail 把整个 msg 当作原始字节流发出去,它不会帮你补空行、去空格、或修正换行符。Windows 下用 \r\n、Linux 下用 \n 拼接头部,都会导致部分客户端无法识别 From 或 Subject。
实操建议:
- 头部字段之间用
\r\n,头部与正文之间**严格使用\r\n\r\n**(两个 CRLF,不能是\n\n或\r\n \r\n) - 正文开头不能有空格、tab 或 BOM;如果正文来自模板渲染,确保
template.Execute输出前没有前置空白 - 不要在头部里加注释行(如
# this is from)或空行——SMTP 协议不认这些,会被当作文本正文的一部分
真正容易被忽略的是:即使所有字段都合法、编码都正确、分隔符也精准,只要 DNS 的 SPF 记录没配好(比如用 notify@myapp.com 发信,但 myapp.com 的 TXT 记录里没包含 v=spf1 include:_spf.google.com ~all),收件方服务器在 SMTP 会话的 RCPT TO 阶段就可能直接拒绝连接。这不是 Go 代码能解决的问题,但开发者常把它误判为“邮件头构造失败”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










