php - 通过PHP向许多用户发送邮件的安全方法

标签 php mysql email e-commerce

让我解释一下标题中的意思。假设,例如,我正在为一个网上商店/目录创建一个小型的电子商务系统。客户可以选择是否希望接收新闻通讯。如果他们这样做了,那么从逻辑上考虑应该在新闻通讯形成并准备就绪时立即发送新闻通讯。

当然,可以通过从数据库中获取所有指定的用户电子邮件并使用for循环以循环方式通过mail函数发送邮件来完成此操作,但是问题是我被告知这是一种不好的做法。一种简单而又不便宜的方法是购买互联网服务来发送新闻,但是php程序员需要什么呢?

因此,我问您谦虚的同志们,从您的角度来看,什么可能是解决方案?

注意!您可能不会相信我,但这不是垃圾邮件。

UPD:我可能解释错了自己,但我想听听一个解决方案,不仅关于正确的邮件发送方式,而且关于正确的发送方式。由于并非总是发送所有邮件。
当然,有些原因是无法预测的。例如,在途中某处发生了故障并丢失了邮件(如果可能的话),但是还有其他可能受服务器或其他因素影响的原因。也许有必要与托管商谈谈吗?

最佳答案

尽管我不会使它成为webrequest / HTTP流程的一部分,但没有理由不能用PHP编写它。我已经成功实现了每个邮件送出或订阅50万个订阅者(取决于可用的本地数据,因为这是一个特定于位置的项目)。这是一个内部项目,因此很遗憾,没有适合您的代码/包,但是我遇到了一些提示:
设置投放

从phpmailer本身开始,负责格式,内容和标题的编码,附件的添加等。它的这一部分效果很好,我不想从头开始编写。
电子邮件本身的“发送”只是在数据库中设置了一些标志,指示是否/如何/向用户(部分)发送内容。
设置此标志后,它将由cronjob自动拾取,而不再涉及Web服务器。
我从一个污染严重的数据库开始,该数据库包含数百万个电子邮件地址,其中许多显然是无效的,因此首先要做的是验证所有电子邮件地址的格式,然后验证主机的格式:

订阅者上的filter_var($email, FILTER_VALIDATE_EMAIL);(显然存储了结果)摆脱了前几十万封无效电子邮件。
从电子邮件中分离出主机(并存储主机名),并进行验证(在DNS中它是否具有MX或至少A记录,但请记住:您可以将电子邮件发送到IP地址foo@[255.255.255.255] ,因此请确保这些有效)))。此处的电子邮件地址不是永久禁用的,但带有状态标志,指示由于域名/ ip而被禁用。
脚本已更改为在订阅/插入之前要求有效的电子邮件地址,在这种情况下,“您无法在任何地方得到它”的废话只是数据库中的荒谬。


现在,我得到了一个可能有效的电子邮件地址列表。从本质上讲,有3种方法可以检测到无效地址(请记住,所有方法都可能是暂时的):

服务器会立即拒绝它们。
较早确定的服务器只是不侦听流量。
在您认为已交付它们很久之后,它们便被弹起。


奇怪的是,反弹,每个电子邮件服务器似乎都具有另一种格式,并且一开始是要解析的地狱,实际上实际上很容易使用VERP捕获。而不是解析整个电子邮件,而是配置了专用的电子邮件地址(我们将其称为mailer@example.com),而不是传递到邮箱,然后通过命令将其发送,如果我们将电子邮件发送到user@server.tld,则< cc>设置为Return-Path。在收据上轻松解析,并在退回多少次之后(邮箱不存在,邮箱可能已满(是,仍然!),等等),您可以声明不可用的电子邮件地址由您决定。
现在,由服务器直接拒绝。也许我们本可以正确配置一些MTA和/或为它们编写插件,但是由于电子邮件是时间敏感的,因此我们必须在上次可用的传递时间上对每个邮件进行绝对可配置的控制(此后电子邮件不再用户的兴趣),每个接收服务器的节流以及通常的所有操作,大约需要用PHP编写一个我们更了解的邮件程序,该程序直接使用SMTP协议将SMTP协议连接到接收服务器上的套接字25。用最少的精力进行其他传输,然后内置了PHPMailer中的默认选项。 SMTP协议实际上非常简单,但是有一些警告:

许多接收服务器采用灰色列表:大多数垃圾邮件发送者并不真正在乎是否收到特定邮件,他们只是将它们搅出。因此,如果未知/尚未受信任的发件人发送邮件,它将被暂时拒绝。捕获该邮件(通常为代码451),然后将电子邮件放入队列中,以便稍后重试。
邮件服务器,尤其是较大的ISP和免费服务(gmail,hotmail / msn / live等)的邮件服务器,如果不作反击就不会代表大量的邮件:在头几百个/千个之后,它们开始拒绝您。以后再说。



加快速度

现在,我们有一个可以运行的交付系统,但是它需要快速。如果您只有10,000个地址要发送,那么在一小时内发送10,000封电子邮件就可以了,但是我们要求的最低发送速度是每小时200,000封。它的开始是一台专用服务器(无论您做什么,实际上它的功耗都可能很低,大部分电子邮件传递时间都在网络中,而不是在服务器上)。
IP的缓存:还记得我们从电子邮件地址中的主机名请求的所有IP吗?我们显然存储了这些信息,并且一次又一次地查询其IP会导致相当大的滞后。但是,IP可能会发生变化:一个DNS记录在那里,另一个MX位于另一个位置...数据变得过时。大多数情况下,服务器实际上没有发送任何内容(订阅新闻简报会突然爆发),低优先级的cronjob正在运行,以检查所有具有过时IP的主机名(我们选择了早于1天的过时地址)作为IP地址,包括那些以前没有的域名(新域名一直都在注册,所以为什么在有人已​​经热情地订购新的电子邮件地址后的第二天就不能使用该域名?或者解决了某个域名的服务器问题,等等。)。实际上,现在发送电子邮件不再需要任何域查找。
重复使用SMTP连接:直接与端口25通话时,建立与服务器的连接会花费大量时间来发送电子邮件。您不必为每封电子邮件都设置新的连接,可以通过相同的连接发送下一个。有点反复无常的情况导致将此处的默认值设置为每个连接大约50封电子邮件(假设您为该域设置了更多或更多)。但是,在电子邮件地址失败的情况下,关闭并重新打开连接以重试有时会有所帮助。总而言之,这确实有助于加快进度。
一些明显的,如此明显的我几乎忘了提及:必须当场创建电子邮件的正文是一种浪费:如果是普通邮件,请准备好正文(我对PHPMailer进行了一些改动,以便能够使用缓存的电子邮件),可能要提前几天(如果您知道要在星期五发送邮件,并且您的服务器处于空闲状态,为什么不准备在星期三进行准备呢?如果是个性化的,您仍然可以事先准备好足够的信息)如果没有的话,至少要让非个性化部分等待。
多个过程。我是否提到发送电子邮件花费在网络上的时间很多?一个邮件发送过程几乎无法从您的电子邮件服务器获得最大收益,几乎看不到负载,并且邮件在不断扩散。尝试通过多个过程将队列的不同部分邮寄出去,以了解适合您的服务器/连接的内容,但请记住两点非常重要的事情:

不同的过程使您非常容易受到竞争条件的影响:要绝对确保您拥有一个完整的系统,该系统永远不会两次发送相同的邮件(三次甚至更多)。它不仅严重惹恼了用户,而且您的垃圾邮件上升了一个档次。
尽可能将域保持在一起:从队列中随机选择,您将失去与接收域电子邮件的服务器保持开放连接的优势。



避免废品

您将发送大量邮件。这正是垃圾邮件发送者的工作。但是,您不想被视为垃圾邮件发送者(毕竟,您不是吗?)?有多种机制可以彻底提高您对接收服务器的信任度:
拥有适当的反向DNS:如果二级域名匹配,则非常检查发送该电子邮件的IP的DNS,就像第二级域匹配:您代表example.com发送邮件吗?确保服务器的反向DNS类似于somename.example.com。
发布您的域的SPF记录:明确指示允许和预期使用带有“发件人/返回路径”标头的邮件发送机器,并期望该机器发送邮件。
记住拒绝:服务器不希望它一遍又一遍地告诉您不存在其他电子邮件地址。当我们处理所有(不再)存在的未验证电子邮件地址时,无论是自动机制,甚至是人工管理员都阻止了我们的服务器。直到后来,我们才采用双重选择加入的方式,因此该数据库被打字错误,人们切换IP以及由此造成的电子邮件地址,恶作剧电子邮件地址等污染了。确保捕获那些无效对象,并给出足够的或严重的故障,然后取消订阅。他们对您没有好处,他们在浪费资源,如果他们真的希望您发送邮件,并且邮箱稍后可用,则只需重新订阅即可。
DKIM是可以增加您的信任度的另一种机制,但是由于我们尚未实施(因此),因此我无法告诉您太多信息。
MX记录:如果您的发送服务器也是该域的接收服务器,则某些服务器仍然喜欢它。到那时,我们只有1 MX,并且由于邮件服务器仍然不是那么繁忙,因此我们将其称为域的后备MX服务器。普通的MX服务器不是发送订阅的服务器,因为您正试图向其发送重要电子邮件的服务器(客户端等)被服务器暂时阻止,这很烦人,因为您已经发送了一些不那么重要的邮件。它确实是接收MX的最高优先级,但是如果失败,我们将获得可观的好处,即订阅发送服务器仍将作为后备交付,因此在危机中我们仍然可以使用它,从而避免了试图尝试的客户遇到尴尬的反弹到达我们。
告诉他们关于你的事。说真的免费电子邮件地址(例如live.com)中的许多主要参与者都为您提供了以某种方式注册的机会,或者如果您的电子邮件被拒绝,则可以通过某些联系点寻求帮助和支持。我有发送大量电子邮件的正当理由,而且可以相信您有很多订阅者,很可能他们会严重增加每小时可以发送到其服务器的电子邮件数量。如果您足够有说服力和诚实,那么只有1,000个微不足道的东西可能会变成一万个甚至更高。可能存在合同,您必须满足的要求,并承诺您必须制定(并保留)此要求。互联网服务提供商(ISP)的品牌不同,其他所有参与者都不同。通常不要打扰他们,因为99%的时间里,您只能找到唯一的电话号码,这些人只会对您的Internet连接进行故障排除,而他们对其他方面却一无所知。 mailer+user=server.tld@example.com电子邮件地址是一个不错的起点,但是请查看是否可以从某个地方钻取更直接的电子邮件地址。准确,诚实和完整:大约有多少订阅者拥有与该ISP的电子邮件地址,您尝试发送邮件的频率是多少,收到的错误或拒绝是什么,订阅和取消订阅的过程如何,以及您实际为客户提供的服务。同样,要好:发送这些邮件对您的企业来说有多重要,对此感到恐慌并声称遭受严重损失与他们无关。礼貌地陈述事实和愿望,询问它们是否可以帮助而不是要求解决方案,要走很长一段路。
节流:与您尝试过的一样,某些服务器每小时和/或每天仅会接收一定数量的邮件。了解这些数字(无论如何,我们都会记录成功与失败),将它们设置为正常域的合理默认值,并将它们设置为针对更大参与者的商定限制。

避免被标记为垃圾邮件

第一条规则:不要垃圾邮件!
第二条规则:永远!不是“一次过”,不是“他们还没有订阅,但这可能是他们一辈子的事”,不是出于最好的意图,人们不得不要求您提供电子邮件。
显然建立了正确的双重加入订阅机制。
PHPMailer确实会自行设置适当的标题,
通过Web(在每封邮件中都包含一个指向其的链接)建立简单的退订机制,如果有的话,还可以通过电子邮件和客户服务建立。确保客户服务可以直接取消订阅。
如前所述:退订(过多)失败并反弹。
避免使用垃圾邮件“一辈子交易”的措辞。
谨慎使用电子邮件中的url。
避免添加指向您控制范围之外的域的链接,除非您完全确定可以信任它们,否则即使它们...
为用户提供价值:通过google / yahoo / live webmail中的用户交互被标记为垃圾邮件,客户端会严重损害未来的成功(在网站上注:如果您进行注册,则live / msn / hotmail会将所有邮件转发给您由您的网域发送并被用户标记为垃圾邮件。学会爱用它,并且一如既往:退订他们,他们显然不想要您的购物中心,并且正在损害您的垃圾邮件评级)。
监控IP的黑名单。如果您出现在其中之一,那就再见了,因此在清除您的姓名和确定案子时都需要采取立即行动。

衡量成功率

在整个过程都在您的控制之下,您可以合理地确定电子邮件最终出现在某个地方(尽管可能是MX的bitbucket或垃圾邮件文件夹),或者您已记录了失败及其原因。这要考虑“实际交付”的数字。
有人会说服您在电子邮件中添加指向在线图像的链接(真实的或著名的1x1透明gif),以衡量实际阅读您的电子邮件的人数。由于较高的百分比阻止了这些图像,因此这些数字充其量是不稳定的,我们相信我们不应该理会它们,因为它们的数量完全不可靠。
如果您希望用户做某事,那么衡量实际成功率的最佳选择就容易得多。在邮件的链接中添加参数,以便您可以衡量有多少用户到达您链接的站点,他们是否执行了所需的操作(观看视频,发表评论,购买商品)。

总而言之,所有的日志记录,用户界面,每个域/电子邮件/用户的可配置设置等。我们花了大约1.5个工作月的时间来构建和解决这些怪癖。与外包电子邮件相比,这可能是一笔大投资,但可能并非如此,这完全取决于交易量和业务本身。
现在,让我开始大笑起来,我是一个用PHP编写MTA的傻瓜,我彻底享受了它(这是我编写大量文本的原因之一),以及每个主机极其通用的日志记录和设置功能基于故障百分比等的警报使实时发布变得如此容易;)

关于php - 通过PHP向许多用户发送邮件的安全方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3820079/

相关文章:

javascript - 公共(public)文件夹中的 Laravel 图像

php - 更改 mysql 中的字符,转换失败 - 仍然出现 Não

mysql - 在mysql docker中拒绝用户 'root' @'localhost'的访问

php - 在 CodeIgniter 上创建 json 编码时无法分组

java - 当域名包含连字符时,电子邮件地址验证失败

php - 管理员自定义页面 opencart 中的权限被拒绝

php - 如何在zend框架中嵌套动态查询?

mysql - 如何删除格式化 mysql 查询中的反引号?

php - AbstractSmtpTransport.php 中的 Swift_TransportException 第 404 行 : Connection to smtp. gmail.com:465 超时

php - Mailjet:将电子邮件添加到列表不起作用