19

I'm trying to send an email with php and swiftmailer, using STARTTLS, but I'm getting a certificate error. I have root access to the SMTP server, and the certificate used is self-signed. I'm using Debian on both machines (web server and smtp server)

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

Do I need to add my own certificate somewhere to get it accepted? Or is this some OpenSSL configuration error?

David Ventura
  • 418
  • 2
  • 7
  • 20

4 Answers4

20

Editor's note: disabling SSL verification has security implications. Without verification of the authenticity of SSL/HTTPS connections, a malicious attacker can impersonate a trusted endpoint (such as GitHub or some other remote Git host), and you'll be vulnerable to a Man-in-the-Middle Attack.

Be sure you fully understand the security issues before using this as a solution.

Swiftmailer has now been updated to include an option for this. It can now be solved using the setStreamOptions method from your Swift_SmtpTransport instance rather than editing the swift class.

$transport = Swift_SmtpTransport::newInstance('smtp.server.com', 123, 'tls')
    ->setUsername('username')
    ->setPassword('password')
    ->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false)));
miken32
  • 42,008
  • 16
  • 111
  • 154
Dave Kennard
  • 477
  • 3
  • 10
  • 3
    Where did you created those lines? – William Lepinski May 07 '16 at 13:40
  • @WilliamLepinski You just make a call to setStreamOptions wherever it is in your code that you're creating the Swift_SmtpTransport instance. If you're not sure how to use Swift Mailer, see the example here: http://swiftmailer.org/docs/sending.html Follow that example, but add the call to setStreamOptions from the Swift_SmtpTransport instance. – Dave Kennard May 08 '16 at 17:31
  • 7
    This is a worst thing you can do, disable all advantages of TLS security. – Mike Doe Jul 21 '17 at 09:01
15

I got the same problem using Swiftmailer in Laravel.

Looks like there is no option for this in Swiftmailer. Clean solution would be to add your own root CA to your server and sign your mail server certificate with this CA. The certificate would be valid after this. See for example this tutorial.

Editor's note: disabling SSL verification has security implications. Without verification of the authenticity of SSL/HTTPS connections, a malicious attacker can impersonate a trusted endpoint (such as GitHub or some other remote Git host), and you'll be vulnerable to a Man-in-the-Middle Attack.

Be sure you fully understand the security issues before using this as a solution.

Anyway, a quick dirty hack you should not use would be to edit swiftmailer\swiftmailer\lib\classes\Swift\Transport\StreamBuffer.php. In _establishSocketConnection() line 253 replace:

$options = array();

with something like this:

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

This will change the ssl options of stream_context_create() (a few lines below $options):

$this->_stream = @stream_socket_client($host.':'.$this->_params['port'], $errno, 
    $errstr, $timeout, STREAM_CLIENT_CONNECT, stream_context_create($options));
miken32
  • 42,008
  • 16
  • 111
  • 154
PiTheNumber
  • 22,828
  • 17
  • 107
  • 180
  • @miken32 Thanks for the edit and the warnings. You can not be clear enough about this. Never disable this in production! Never commit code using this hack. – PiTheNumber Sep 23 '21 at 08:55
  • Actually I copied the text from an edit you'd made to another post lol. Was trying to find a duplicate target for this problem and kept on coming across all these "disable verification" answers. :( – miken32 Sep 23 '21 at 14:58
  • @miken32 You made my day :D Not sure this text is really from me. Anyway, if you need to accept your selft signed certificate you have to create a root/CA certificate as source and add the root to your system. https://askubuntu.com/questions/73287/how-do-i-install-a-root-certificate – PiTheNumber Sep 24 '21 at 07:42
6

Editor's note: disabling SSL verification has security implications. Without verification of the authenticity of SSL/HTTPS connections, a malicious attacker can impersonate a trusted endpoint (such as GitHub or some other remote Git host), and you'll be vulnerable to a Man-in-the-Middle Attack.

Be sure you fully understand the security issues before using this as a solution.

You do not need to edit /vendor files. You can specify (undocumented) options in your config/mail.php file:

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

You can check it yourself in vendor/laravel/framework/src/Illuminate/Mail/TransportManager.php on line ~50:

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

Needless to say, circumventing peer verification has huge security implications (read vulnerabilities). I imagine this solution for some dev or local environment, never in production or even in an Internet/Publicly available app.

Alex
  • 810
  • 9
  • 16
2

Editor's note: disabling SSL verification has security implications. Without verification of the authenticity of SSL/HTTPS connections, a malicious attacker can impersonate a trusted endpoint (such as GitHub or some other remote Git host), and you'll be vulnerable to a Man-in-the-Middle Attack.

Be sure you fully understand the security issues before using this as a solution.

For me, I had to add $transport->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false, 'verify_peer_name' => false))); to the Mailer.php file, see:

    /**
     * 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;
    }

I got this from another link, can't find it now.

One think that I did extra to the other answers was to specify the 'verify_peer_name' => false

miken32
  • 42,008
  • 16
  • 111
  • 154
Tudor
  • 163
  • 5