使用smtplib发送邮件时,DKIM验证失败

6
我正在尝试使用smtplib发送电子邮件,看起来它们能够成功发送。唯一的问题是DKIM失败,并且这些邮件通常会直接进入垃圾邮件文件夹。
我的共享托管(如果有所帮助,主机是a2hosting)启用了DKIM,在使用Thunderbird发送单个电子邮件时,该过程正常运行并通过了DKIM,这表明问题在于我这边。
我甚至尝试使用dkimpy使用私钥显式地签署电子邮件,但仍然在ARC-Authentication-Results下得到dkim=fail的结果。 我参考了一些帖子和答案,建议“登录”是解决方案,但我已经使用SMTP.login()进行登录,正如我之前提到的,电子邮件已经被发送。
我所参考的一个答案提到了服务器签署电子邮件是服务器的工作,值得一提的是,即使没有使用dkimpy显式签署,原始电子邮件输出也包括DKIM签名,表明服务器正在按预期签署电子邮件。
但问题仍然存在,DKIM失败影响了电子邮件的可交付性,而原始输出不提供有关为什么域的DKIM失败的详细信息。
我使用以下代码片段发送电子邮件:
    msg = MIMEMultipart()
    msg['From'] = 'myemail@mydomain.tld'
    msg['To'] = 'someemail@gmail.com'
    msg['Subject'] = "Subject"
    msg.attach(MIMEText("SomeText", "plain"))

    s = smtplib.SMTP_SSL("mydomain.tld:465")
    s.login("myemail@mydomain.tld", "mypassword")
    s.sendmail("myemail@mydomain.tld", 'someemail@gmail.com',msg.as_string())

我尝试按以下方式签署消息

headers = ["To", "From", "Subject"]
with open("cert.pem") as fh:
    dkim_private = fh.read()
sig = dkim.sign(
            message=msg.as_string().encode("ascii"),    
            selector=str(dkim_selector).encode("ascii"),
            domain="robogyan.tech".encode("ascii"),
            privkey=dkim_private.encode("ascii"),
            include_headers=headers,)
msg["DKIM-Signature"] = sig.decode("ascii").lstrip("DKIM-Signature: ")

使用上述代码,原始输出确实反映了签名,但DKIM仍然失败。

似乎身份验证没有任何问题,因为服务器回复“身份验证成功”。

编辑:

DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;

    d=mydomain.tld; s=default; h=Subject:To:From:MIME-Version:Content-Type:

    Sender:Reply-To:Date:Message-ID:Cc:Content-Transfer-Encoding:Content-ID:

    Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc

    :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:

    List-Subscribe:List-Post:List-Owner:List-Archive;

    bh=giCDGo/0duFr1Ex65l7Ixc3N45EAULK+gw5cHV8pO0k=; b=DR08Q+CjgOLqo8WkLJs/XROfTw

    Z7+ph+qnzi5p49cT3+UwQolcL1CKIVPk7XRkL8WZ3FFa9hZuc6TumquRSiYd5uR0AC5Z3lopEfnQe

    fdbOOTRnks2ZzoOnQusy/gmydUttypu8wTthFhy7vTWXMFcdI29X/HkrokCtiGKCoD2u2kWBtn2sm

    3/aP83lBbMpcWsNbvo3HTsL71o8QPd6bVKpqRGyAy89cAwMLwP4dnJ9WcCxxNzowlJNPQja3o5W16

    t3rG/KizcRehjaDUXhPPRF/4RdYUSIi/SGNwmIPwvkZNc17k3wQpszKeG6/Ujgax/i7Li7V7dLJBT

    Fu/x6xDA==;

Signed-by: myemail@mydomain.tld

Expected-Body-Hash: giCDGo/0duFr1Ex65l7Ixc3N45EAULK+gw5cHV8pO0k=

如果这有帮助的话,这是发送失败邮件的 DKIM。预期的正文哈希值和接收到的正文哈希值也匹配。我不确定问题出在哪里。

2个回答

12

经过大量的研究和蛮力尝试,我终于找到了解决问题的方法。 我需要在头部也包含消息ID和日期。 将以下代码添加到我的代码中有助于通过验证。

msg['Date'] = email.utils.formatdate()
msg['Message-ID'] = email.utils.make_msgid(domain='mydomain.tld')

许多MTA都有(WHM/CPanel/SpamExperts)模块,可以添加缺失的Message-Id头,从而使之前创建的DKIM签名无效。 - OzBob
至少在我的情况下,不需要设置自定义日期,只需要自定义Message-ID即可。但是是否有一种自动检测它的方法,例如(domain='[autodetect_domain]') - LWC
谢谢你提供的解决方案。我一直在抓狂并查阅文档,你帮我节省了很多时间。 - Fahad Ahammed

1
重要提示:您需要将SMTP客户端的机器IP地址添加到InternalHosts列表中,因为OpenDKIM将使用这些规则检查客户端的权限。
然后,您需要将以下行添加到/etc/opendkim.conf中:
InternalHosts    file:/etc/opendkim/TrustedHosts   # or any location you want

/etc/opendkim/TrustedHosts 的内容可能如下:

127.0.0.1
::1
localhost
<server_ip>
hostname.example1.com
example1.com
hostname.example2.com
example2.com
...

这只是一个例子。你需要在这里填写你的Python smtplib客户端机器的地址(IP/主机名)。

然后只需重新启动你的opendkim

$ sudo service opendkim restart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接