邮件头注入是攻击者通过在PHP mail()函数的$to或$headers参数中插入\r\n换行符,伪造邮件头(如CC、BCC、From)甚至拼接恶意邮件体。根本原因是mail()不校验输入,必须对所有用户输入严格清洗:先FILTER_VALIDATE_EMAIL验证,再显式剔除\r\n,禁用拼接用户数据到$headers,推荐改用PHPMailer等安全库。

什么是邮件头注入,mail() 函数怎么被利用
PHP 的 mail() 函数本身不校验参数内容,只要传入的 $headers 或 $to 字符串里混入换行符(\r 或 \n),攻击者就能强行插入额外邮件头,比如伪造 CC:、BCC:、From:,甚至拼接整个恶意邮件体。典型现象是:你只发了一封给用户的邮件,结果收件人收到多封,或发现邮件来自未知地址,后台日志里 mail() 调用的 $headers 字段明显被篡改。
过滤用户输入的邮箱地址和头字段
所有进入 mail() 的字符串,只要可能含用户输入(如表单里的 email、name),必须做严格清洗。重点不是“过滤特殊字符”,而是“只保留合法字符”:
-
filter_var($email, FILTER_SANITIZE_EMAIL)仅去除非法字符,但不阻止\r\n—— 它会原样保留换行符,不能直接用于$to或$headers - 真正安全的做法是:用
filter_var($email, FILTER_VALIDATE_EMAIL)验证格式,再用正则preg_replace('/[\r\n]+/', '', $email)显式剔除所有换行符 - 对自定义头字段(如
Subject)同样处理:str_replace(["\r", "\n"], '', $subject)比htmlspecialchars()更直接有效,因为后者不处理换行符
避免拼接用户数据到 $headers 字符串
很多人习惯把用户输入的姓名、邮箱直接拼进 $headers,例如:$headers = "From: {$name} "; —— 这是高危写法。哪怕 $name 是 “张三\r\nCC:attacker@evil.com”,mail() 就真会发 CC。
- 永远不要把任何用户输入拼进
$headers字符串 - 如果必须动态设置发件人,用
-f参数(注意是命令行参数,不是 header):mail($to, $subject, $body, $headers, '-f' . $cleaned_email),其中$cleaned_email已经过filter_var(... FILTER_VALIDATE_EMAIL)和换行符清除 - PHP 8.0+ 可用
mail()的第五个参数传-f,但旧版本需确认 sendmail 路径是否支持;若用 SMTP(如 PHPMailer),就完全绕过这个风险
更稳妥的替代方案:用 PHPMailer 或 mail() + 严格白名单
原生 mail() 天然缺乏输入隔离机制,修复成本高于替换。生产环境强烈建议切换:
- PHPMailer 支持自动转义头字段、内置邮箱验证、可禁用远程代码执行(关掉
isHTML(true)时的脚本解析) - 若坚持用
mail(),至少把所有头字段固化为常量:$headers = "From: noreply@yourdomain.com\r\nContent-Type: text/plain; charset=UTF-8";,用户数据只允许出现在邮件正文(且仍需过滤 XSS) - Linux 下检查
sendmail_path配置,确保没被篡改为恶意脚本;Windows 主机尤其要注意,部分 IIS 配置下mail()会调用外部程序,扩大攻击面
最常被忽略的一点:前端 JavaScript 校验毫无意义,攻击者直接发 POST 请求绕过。所有过滤必须在服务端做,且每次调用 mail() 前都要重新清洗——缓存过的“干净邮箱”也可能被二次污染。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











