PHP - 使用 STARTTLS 和自签名证书的 Swiftmailer

Posted

技术标签:

【中文标题】PHP - 使用 STARTTLS 和自签名证书的 Swiftmailer【英文标题】:PHP - Swiftmailer using STARTTLS and self signed certificates 【发布时间】:2015-01-09 20:29:19 【问题描述】:

我正在尝试使用 STARTTLS 使用 php 和 swiftmailer 发送电子邮件,但出现证书错误。我对 SMTP 服务器具有 root 访问权限,并且使用的证书是自签名的。 我在两台机器(Web 服务器和 smtp 服务器)上都使用 Debian

PHP message: PHP Warning: stream_socket_enable_crypto(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in [..]/lib/classes/Swift/Transport/StreamBuffer.php on line 97 PHP message: PHP Fatal error: Uncaught exception 'Swift_TransportException' with message 'Unable to connect with TLS encryption' in [..]/lib/classes/Swift/Transport/EsmtpTransport.php:294

我是否需要在某处添加自己的证书才能被接受?还是这是一些 OpenSSL 配置错误?

【问题讨论】:

【参考方案1】:

编者注:禁用 SSL 验证具有安全隐患。如果不验证 SSL/HTTPS 连接的真实性,恶意攻击者可以冒充一个受信任的端点(例如 GitHub 或其他一些远程 Git 主机),您将容易受到 Man-in-the-Middle Attack 的攻击。

在将其用作解决方案之前,请确保您完全了解安全问题。

Swiftmailer 现在已经更新为包含一个选项。现在可以使用 Swift_SmtpTransport 实例中的 setStreamOptions 方法解决它,而不是编辑 swift 类。

$transport = Swift_SmtpTransport::newInstance('smtp.server.com', 123, 'tls')
    ->setUsername('username')
    ->setPassword('password')
    ->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false)));

【讨论】:

你在哪里创建了这些行? @WilliamLepinski 您只需在代码中创建 Swift_SmtpTransport 实例的任何位置调用 setStreamOptions 即可。如果您不确定如何使用 Swift Mailer,请参阅此处的示例:swiftmailer.org/docs/sending.html 遵循该示例,但从 Swift_SmtpTransport 实例添加对 setStreamOptions 的调用。 这是你能做的最糟糕的事情,禁用 TLS 安全性的所有优点。【参考方案2】:

我在 Laravel 中使用 Swiftmailer 时遇到了同样的问题。

在 Swiftmailer 中似乎没有此选项。干净的解决方案是将您自己的root CA 添加到您的服务器并使用此 CA 签署您的邮件服务器证书。证书将在此之后有效。参见例如this tutorial。

编者注:禁用 SSL 验证具有安全隐患。如果不验证 SSL/HTTPS 连接的真实性,恶意攻击者可以冒充一个受信任的端点(例如 GitHub 或其他一些远程 Git 主机),您将容易受到 Man-in-the-Middle Attack 的攻击。

在将其用作解决方案之前,请确保您完全了解安全问题。

无论如何,一个你不应该使用的快速肮脏的技巧是编辑swiftmailer\swiftmailer\lib\classes\Swift\Transport\StreamBuffer.php。在_establishSocketConnection() 第 253 行替换:

$options = array();

类似这样的:

$options = array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false));

这将改变stream_context_create() 的ssl options($options 下面几行):

$this->_stream = @stream_socket_client($host.':'.$this->_params['port'], $errno, 
    $errstr, $timeout, STREAM_CLIENT_CONNECT, stream_context_create($options));

【讨论】:

@miken32 感谢您的编辑和警告。您对此还不够清楚。永远不要在生产中禁用它!切勿使用此 hack 提交代码。 实际上,我将您所做的编辑中的文本复制到了另一个帖子中,哈哈。试图为这个问题找到一个重复的目标,并不断遇到所有这些“禁用验证”的答案。 :( @miken32 你成就了我的一天 :D 不确定这段文字是否真的来自我。无论如何,如果您需要接受您的自签名证书,您必须创建一个根/CA 证书作为源并将根添加到您的系统。 askubuntu.com/questions/73287/…【参考方案3】:

编者注:禁用 SSL 验证具有安全隐患。如果不验证 SSL/HTTPS 连接的真实性,恶意攻击者可以冒充一个受信任的端点(例如 GitHub 或其他一些远程 Git 主机),您将容易受到 Man-in-the-Middle Attack 的攻击。

在将其用作解决方案之前,请确保您完全了解安全问题。

您不需要编辑/vendor 文件。您可以在 config/mail.php 文件中指定(未记录的)选项:

'stream' => [
    'ssl' => [
        'allow_self_signed' => true,
        'verify_peer' => false,
        'verify_peer_name' => false,
    ],
],

你可以在vendor/laravel/framework/src/Illuminate/Mail/TransportManager.php在线~50自行查看:

...
if (isset($config['stream'])) 
    $transport->setStreamOptions($config['stream']);

...

不用说,绕过对等验证具有巨大的安全隐患(读取漏洞)。我想这个解决方案适用于一些开发或本地环境,从不在生产环境中,甚至在 Internet/公共可用的应用程序中。

【讨论】:

【参考方案4】:

编者注:禁用 SSL 验证具有安全隐患。如果不验证 SSL/HTTPS 连接的真实性,恶意攻击者可以冒充一个受信任的端点(例如 GitHub 或其他一些远程 Git 主机),您将容易受到 Man-in-the-Middle Attack 的攻击。

在将其用作解决方案之前,请确保您完全了解安全问题。

对我来说,我必须将 $transport->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false, 'verify_peer_name' => false))); 添加到 Mailer.php 文件中,请参阅:

    /**
     * Returns the SMTP transport
     *
     * @return \Swift_SmtpTransport
     */
    protected function getSmtpInstance(): \Swift_SmtpTransport 
            $transport = new \Swift_SmtpTransport();
            $transport->setTimeout($this->config->getSystemValue('mail_smtptimeout', 10));
            $transport->setHost($this->config->getSystemValue('mail_smtphost', '127.0.0.1'));
            $transport->setPort($this->config->getSystemValue('mail_smtpport', 25));
            if ($this->config->getSystemValue('mail_smtpauth', false)) 
                    $transport->setUsername($this->config->getSystemValue('mail_smtpname', ''));
                    $transport->setPassword($this->config->getSystemValue('mail_smtppassword', ''));
                    $transport->setAuthMode($this->config->getSystemValue('mail_smtpauthtype', 'LOGIN'));
            
            $smtpSecurity = $this->config->getSystemValue('mail_smtpsecure', '');
            if (!empty($smtpSecurity)) 
                    $transport->setEncryption($smtpSecurity);
            
            $streamingOptions = $this->config->getSystemValue('mail_smtpstreamoptions', []);
            if (is_array($streamingOptions) && !empty($streamingOptions)) 
                    $transport->setStreamOptions($streamingOptions);
            

            /* EDIT - allow self-signed mail cert */
            $transport->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false, 'verify_peer_name' => false)));
            /* EDIT end */

            return $transport;
    

我是从另一个链接得到的,现在找不到了。

一个认为我对其他答案所做的额外工作是指定'verify_peer_name' => false

【讨论】:

以上是关于PHP - 使用 STARTTLS 和自签名证书的 Swiftmailer的主要内容,如果未能解决你的问题,请参考以下文章

Ktor 客户端 IOS 和自签名证书

Web Server CA证书签名步骤和自签名测试,支持多域名

wget、自签名证书和自定义 HTTPS 服务器

iOS开发HTTPS实现之信任SSL证书和自签名证书

如何使用 Ionic Cordova 框架和自签名证书绕过 iOS 11 中的 SSL 检查

iOS开发HTTPS实现之信任SSL证书和自签名证书