强制 System.Net.Mail.SmtpClient 使用 Base64 对主题标头进行编码

发布于 2025-01-12 11:49:12 字数 2715 浏览 0 评论 0原文

我们使用 .NET Core 3.1 的默认 SMTP 客户端发送电子邮件,如下所示:

private async Task SendMail(string from, string to, string subject, string body)
{
    var message = new MailMessage();

    message.From = new MailAddress(from);
    var toAddress = new MailAddress(to);
    message.To.Add(toAddress);

    message.Subject = subject;
    message.SubjectEncoding = Encoding.UTF8;
    message.Body = body;
    message.BodyEncoding = Encoding.UTF8;
    message.IsBodyHtml = false;

    using var smtp = new SmtpClient();
    smtp.Host = SMTP_HOST;
    smtp.Port = SMTP_PORT;
    smtp.EnableSsl = false;
    smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
    smtp.DeliveryFormat = SmtpDeliveryFormat.International;
    smtp.UseDefaultCredentials = false;
    smtp.Credentials = new NetworkCredential(SMTP_USER, SMTP_PASSWORD);

    await smtp.SendMailAsync(message);
}

然后我们调用如下方法:

await this.SendMail(
    from: "[email protected]",
    to: "[email protected]",
    subject: "Caractères accentués",
    body: "dummy"
);

当使用网络分析器检查主题标头的编码方式时,我们观察到以下情况

  • :客户端连接到支持 SMTPUTF8 的 MTA,主题标头使用 UTF8 进行编码:Subject: Caractèresaccentués
  • 当客户端连接到不支持 SMTPUTF8 的 MTA 时,主题标头先以 Base64 编码,然后以 US-ASCII 编码:Subject: =?utf-8?B?Q2FyYWN0w6hyZXMgYWNjZW50dcOpcw==?=
  • 如果我们从 SmtpDeliveryFormat.International 切换到 SmtpDeliveryFormat.SevenBits,Subject 标头始终先用 Base64 编码,然后用 US-ASCII 编码(或者可能用引用的 url 编码,然后用 US-ASCII 编码) 。

这都是标准和预期的行为。

我们使用第三方电子邮件服务作为 SMTP 中继,该服务本身支持 SMTPUTF8。但他们的服务有一个错误,无法检测到收件人缺乏 SMTPUTF8 支持,导致电子邮件主题在包含非 ASCII 字符时在我们的客户邮箱中显示不正确。他们使用我们在 MTA 中使用的相同编码发送主题,在我们的例子中是 UTF8,因为我们需要 SmtpDeliveryFormat.International (为了与非 ASCII 电子邮件地址兼容)。

当我们使用 Base64 对主题标头进行编码时,该问题就消失了,因此我们希望将其作为一种解决方法,直到我们的提供商修复了该问题。使用 smtp.DeliveryFormat = SmtpDeliveryFormat.SevenBits 可以实现此目的,但它也阻止我们在电子邮件地址中使用非 ASCII 字符,这是一个比主题编码问题更大的问题。所以我们不能那样做。

有没有办法强制 .NET 客户端对主题标头使用 Base64 编码,同时使用 SmtpDeliveryFormat.International,即使 SMTP 中继支持 SMTPUTF8 也是如此?我尝试这样做:

message.Subject = $"=?utf-8?B?{Convert.ToBase64String(Encoding.UTF8.GetBytes(subject))}?=";

但是主题标头没有通过,它由 SmtpClient 解码,然后 UTF8 编码为 Caractèresaccentués,因此它不会改变任何内容。

We are using .NET Core 3.1's default SMTP client to send an email, like this:

private async Task SendMail(string from, string to, string subject, string body)
{
    var message = new MailMessage();

    message.From = new MailAddress(from);
    var toAddress = new MailAddress(to);
    message.To.Add(toAddress);

    message.Subject = subject;
    message.SubjectEncoding = Encoding.UTF8;
    message.Body = body;
    message.BodyEncoding = Encoding.UTF8;
    message.IsBodyHtml = false;

    using var smtp = new SmtpClient();
    smtp.Host = SMTP_HOST;
    smtp.Port = SMTP_PORT;
    smtp.EnableSsl = false;
    smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
    smtp.DeliveryFormat = SmtpDeliveryFormat.International;
    smtp.UseDefaultCredentials = false;
    smtp.Credentials = new NetworkCredential(SMTP_USER, SMTP_PASSWORD);

    await smtp.SendMailAsync(message);
}

Then we call the method like this:

await this.SendMail(
    from: "[email protected]",
    to: "[email protected]",
    subject: "Caractères accentués",
    body: "dummy"
);

When using a network analyzer to check how the Subject header is encoded, we are observing the following:

  • When the client connects to a MTA that supports SMTPUTF8, the Subject header is encoded using UTF8: Subject: Caractères accentués.
  • When the client connects to a MTA that does not support SMTPUTF8, the Subject header is encoded in Base64 then in US-ASCII: Subject: =?utf-8?B?Q2FyYWN0w6hyZXMgYWNjZW50dcOpcw==?=.
  • If we switch from SmtpDeliveryFormat.International to SmtpDeliveryFormat.SevenBits, the Subject header is always encoded in Base64 then in US-ASCII (or maybe quoted-url then US-ASCII).

This is all standard and excpected behaviour.

We are using a third party email service as a SMTP relay, which supports SMTPUTF8 itself. But their service has a bug and fails to detect the lack of SMTPUTF8 support on recipient's side, resulting in email Subjects being improperly displayed in our clients mailboxes when they contain non-ASCII characters. They send the Subject with the same encoding we used with their MTA, which in our case is UTF8, because we need SmtpDeliveryFormat.International (for compatibility with non-ASCII email addresses).

The issue disappears when we encode the Subject header using Base64, so we would like to do that as a workaround until our provider fixes the issue on their side. Using smtp.DeliveryFormat = SmtpDeliveryFormat.SevenBits achieves this, but it also prevents us from using non-ASCII characters in email addresses, which is an even bigger problem than the Subject encoding issue. So we can't do that.

Is there a way to force the .NET client to use Base64 encoding for the Subject header while also using SmtpDeliveryFormat.International, even when the SMTP relay supports SMTPUTF8? I tried to do this:

message.Subject = 
quot;=?utf-8?B?{Convert.ToBase64String(Encoding.UTF8.GetBytes(subject))}?=";

But the subject header is not passed through, it is decoded by the SmtpClient then UTF8-encoded as Caractères accentués, so it doesn't change anything.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

二智少女猫性小仙女 2025-01-19 11:49:12

不是解决方案,而是解决方法:

如果、且仅当本地部分(即在目标邮件地址的“@”)包含非 ASCII 字符。这具有以下效果:

  • 对于[电子邮件受保护],主题将被正确编码。

  • 对于 recipient@éxample.com,一切都会正常工作:MailAddress 类可以使用 punycode 对邮件地址的主机部分进行编码,而不需要 SMTPUTF8。

  • 对于[电子邮件受保护] ,主题不会进行 Base64 编码。 但是,由于本地部分包含非 ASCII 字符,因此收件人邮件服务器极有可能支持 SMTPUTF8,因此可以正确解释您的主题行。< /p>

示例代码:

public static void EnableNonAsciiMailAddressesIfNecessary(
    SmtpClient client, MailMessage message)
{
    var allAddresses =
        (message.From != null
            ? new[] { message.From }
            : Array.Empty<MailAddress>())
        .Concat(message.To)
        .Concat(message.CC)
        .Concat(message.Bcc)
        .Concat(message.ReplyToList);

    var smtpUtf8Required = allAddresses.Any(a => a.User.Any(c => c > 0x7f));

    if (smtpUtf8Required)
    {
        client.DeliveryFormat = SmtpDeliveryFormat.International;
    }
}

Not a solution, but a workaround:

Set DeliveryFormat to SmtpDeliveryFormat.International if, and only if, the local part (i.e. the part before the "@") of the target mail address contains a non-ascii character. This has the following effect:

  • For [email protected], the subject will be properly encoded.

  • For recipient@éxample.com, everything will work as well: The MailAddress class can encode the host-part of a mail address with punycode without requiring SMTPUTF8.

  • For [email protected], the subject won't be Base64-encoded. However, since local part contains a non-ASCII character, it is highly likely that the recipient mail server supports SMTPUTF8, and, thus, can interpret your subject line correctly.

Example code:

public static void EnableNonAsciiMailAddressesIfNecessary(
    SmtpClient client, MailMessage message)
{
    var allAddresses =
        (message.From != null
            ? new[] { message.From }
            : Array.Empty<MailAddress>())
        .Concat(message.To)
        .Concat(message.CC)
        .Concat(message.Bcc)
        .Concat(message.ReplyToList);

    var smtpUtf8Required = allAddresses.Any(a => a.User.Any(c => c > 0x7f));

    if (smtpUtf8Required)
    {
        client.DeliveryFormat = SmtpDeliveryFormat.International;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文