邮件发送功能中如果涉及从用户输入获取邮件内容、收件人信息等数据,并将这些数据直接拼接进数据库查询语句执行,就会存在SQL注入风险。攻击者可以在邮件内容相关输入中插入恶意SQL片段,篡改查询逻辑获取敏感数据。

漏洞产生原因分析
常见的错误写法是将用户输入的内容直接拼接成SQL字符串,例如需要查询发送邮件的用户是否存在时,部分代码会写成如下形式:
<?php // 错误示例:直接拼接用户输入 $user_email = $_POST['email']; // 这里是从邮件发送功能获取的收件人邮箱,属于用户输入 $sql = "SELECT * FROM users WHERE email = '" . $user_email . "'"; $result = mysqli_query($conn, $sql); ?>
如果攻击者在邮箱输入框中输入admin@test.com' OR '1'='1,最终拼接的SQL语句会变成SELECT * FROM users WHERE email = 'admin@test.com' OR '1'='1',条件恒成立,会返回所有用户数据,造成信息泄露。
核心修复方案:使用参数化查询
参数化查询会把用户输入和SQL逻辑分离,数据库会把输入当作纯数据处理,不会解析为SQL指令,从根源上避免注入问题。不同语言的实现方式如下:
PHP场景(使用PDO)
<?php
// 正确示例:使用PDO参数化查询
$user_email = $_POST['email'];
// 准备带占位符的SQL语句
$stmt = $conn->prepare("SELECT * FROM users WHERE email = :email");
// 绑定参数,自动转义特殊字符
$stmt->bindParam(':email', $user_email);
$stmt->execute();
$result = $stmt->fetchAll();
?>
Java场景(使用PreparedStatement)
// 正确示例:使用PreparedStatement
String userEmail = request.getParameter("email");
String sql = "SELECT * FROM users WHERE email = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, userEmail); // 自动处理特殊字符,避免注入
ResultSet rs = pstmt.executeQuery();
Python场景(使用pymysql参数化)
# 正确示例:使用参数化查询
import pymysql
user_email = request.form.get("email")
cursor = conn.cursor()
# 使用%s作为占位符,参数单独传入
sql = "SELECT * FROM users WHERE email = %s"
cursor.execute(sql, (user_email,))
result = cursor.fetchall()
额外加固措施
除了使用参数化查询,还可以结合以下措施进一步提升安全性:
- 输入合法性校验:对邮件相关的输入做格式校验,比如邮箱必须符合邮箱格式,长度限制在正常范围内,过滤掉单引号、分号、注释符等危险字符。
- 最小权限原则:连接数据库的程序账号只赋予必要的查询、插入权限,不要使用root等最高权限账号,即使发生注入也无法执行删表、删库操作。
- 错误信息脱敏:不要将数据库报错信息直接返回给用户,避免攻击者通过报错信息推测表结构、字段名等信息。
- 定期安全审计:定期检查邮件发送功能相关的所有数据库查询代码,确认没有拼接用户输入的情况,及时修复潜在问题。
修复后验证方法
完成修复后,可以通过以下方式验证漏洞是否解决:
- 在邮件相关输入中尝试输入
test' OR 1=1 --这类常见注入 payload,观察查询是否会返回异常数据。 - 查看数据库执行日志,确认输入的payload是否被当作普通字符串处理,没有拆分SQL逻辑。
- 使用专业的安全扫描工具对邮件发送接口做注入测试,确认没有检出相关漏洞。
注意:所有涉及用户输入的数据库查询场景都应该使用参数化查询,不只是邮件发送功能,这是防范SQL注入的最有效手段。