PHP - Swiftmailer using STARTTLS and self signed certificates - php

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?

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)));

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));

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.

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

Related

Laravel 5.5: Can't sent email via google (SSL operation failed) [duplicate]

This question already has answers here:
Laravel certificate verification errors when sending TLS email
(2 answers)
Closed 1 year ago.
I saw the bellow settings for .env in answers on similar questions:
MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=myemail#gmail.com
MAIL_PASSWORD=somePassword1234
MAIL_ENCRYPTION=tls
However, nothing works. With, tls encryption, I got error
stream_socket_enable_crypto(): SSL operation failed with code 1.
OpenSSL Error messages: error:14090086:SSL routines:
ssl3_get_server_certificate:certificate verify failed
With ssl encryption, I got error:
Swift_TransportException
Expected response code 220 but got code "", with message ""
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 Gmail, 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 must disable signing the letter with a certificate in config mail file: mail.php
return [
....
'stream' => [
'ssl' => [
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
],
],
....
];

Allowing insecure connections in PHP Mailer

I encountered the same problem mentioned here, and allowing insecure connections solved it, while nothing else did. Can you please inform me about what security issues I might face if i kept allowing these insecure connections?
I suggest this link:
PhpMailer not sending mails - TLS error?
if you have insecure connection troubles, let add this lines:
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
It prevent certifies checking and so on.
TLS fulfils two roles: authenticating who you're connecting to, and protecting data in transit. Disabling verification drops the former, but data is still encrypted in transit in exactly the same way as with a verified cert, so it is still substantially better than having no encryption.
The most obvious problem with disabling verification is that you lose the ability to detect interception of your connection. It's very common for ISPs to have a firewall config that redirects outbound SMTP connections to their own mail servers without telling you. If you don't verify the certificate, your script will not notice that it's connected to the wrong server, and will continue and submit your credentials (which will probably fail, since it's the wrong server) - but you've just submitted your ID and password to a man-in-the-middle, and you can't tell if it's your ISP or someone else intercepting your connection.
A better option if you're having these issues and you know the certificate name that it's being translated to... instead of disabling this checking, you can explicitly add what names are acceptable.
$mail->SMTPOptions = array(
'ssl' => array(
'peer_name' => <acceptable peer name>
));
So looking at the referenced example which through this error:
PHP Warning: stream_socket_enable_crypto(): Peer certificate
CN=*.mail.dreamhost.com' did not match expected
CN=mx1.sub4.homie.mail.dreamhost.com' in
/home/ikbb/domains/dev.ikbb.com/public_html/includes/phpmailer/5.2.10/class.smtp.php
You could set *.mail.dreamhost.com as an allowed name.

how to fix stream_socket_enable_crypto(): SSL operation failed with code 1

stream_socket_enable_crypto(): SSL operation failed with code 1.
OpenSSL Error messages: error:14090086:SSL
routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
Im using Laravel 4.2, PHP 5.6, Apache 2.4
I have GoDaddy SSL installed in Amazon ec2 Linux.
SSL working fine when i visit the site with https.
The error happened when I call my function :
<?php
public function sendEmail()
{
\Mail::send ( 'emails.code.code', $data, function ($sendemail) use($email) {
$sendemail->from ( 'info#me.com', 'Me Team' );
$sendemail->to ( $email, '' )->subject ( 'Activate your account' );
} );
}
?>
I read some articles about this, they said that there are things we should make some changes, they put that code but i don't know where to insert it.
Been reading this: https://www.mimar.rs/en/sysadmin/2015/php-5-6-x-ssltls-peer-certificates-and-hostnames-verified-by-default/
and this documentation of php http://php.net/manual/en/migration56.openssl.php which is hard to understand.
So my question is how to solve this problem?
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 Gmail, 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 can add below code in /config/mail.php ( tested and worked on laravel 5.1, 5.2, 5.4 )
'stream' => [
'ssl' => [
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
],
],
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 Gmail, 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.
I have also this error in laravel 4.2 I solved like this way. Find out StreamBuffer.php. For me I use xampp and my project name is itis_db for this my path is like this. So try to find according to your one
C:\xampp\htdocs\itis_db\vendor\swiftmailer\swiftmailer\lib\classes\Swift\Transport\StreamBuffer.php
and find out this function inside StreamBuffer.php
private function _establishSocketConnection()
and paste this two lines inside of this function
$options['ssl']['verify_peer'] = FALSE;
$options['ssl']['verify_peer_name'] = FALSE;
and reload your browser and try to run your project again. For me I put on like this:
private function _establishSocketConnection()
{
$host = $this->_params['host'];
if (!empty($this->_params['protocol'])) {
$host = $this->_params['protocol'].'://'.$host;
}
$timeout = 15;
if (!empty($this->_params['timeout'])) {
$timeout = $this->_params['timeout'];
}
$options = array();
if (!empty($this->_params['sourceIp'])) {
$options['socket']['bindto'] = $this->_params['sourceIp'].':0';
}
$options['ssl']['verify_peer'] = FALSE;
$options['ssl']['verify_peer_name'] = FALSE;
$this->_stream = #stream_socket_client($host.':'.$this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, stream_context_create($options));
if (false === $this->_stream) {
throw new Swift_TransportException(
'Connection could not be established with host '.$this->_params['host'].
' ['.$errstr.' #'.$errno.']'
);
}
if (!empty($this->_params['blocking'])) {
stream_set_blocking($this->_stream, 1);
} else {
stream_set_blocking($this->_stream, 0);
}
stream_set_timeout($this->_stream, $timeout);
$this->_in = &$this->_stream;
$this->_out = &$this->_stream;
}
Hope you will solve this problem.....
Try changing the app/config/email.php
smtp to mail
How to fix on Laravel (5,6,7 at least), WordPress (and other PHP + cURL implementations I guess):
Download the latest cacert.pem file from cURL website.
wget https://curl.haxx.se/ca/cacert.pem
Edit php.ini (you can do php --ini to find it), update (or create if they don't exist already) those two lines:
curl.cainfo="/path/to/downloaded/cacert.pem"
...
openssl.cafile="/path/to/downloaded/cacert.pem"
Those lines should already exist but commented out, so uncomment them and edit both values with the path to the downloaded cacert.pem
Restart PHP and Nginx/Apache.
Edit: You may need to chown/chmod the downloaded certificate file so PHP (and the user running it) can read it.
source
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 Gmail, 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.
Easy fix for this might be editing config/mail.php and turning off TLS
'encryption' => env('MAIL_ENCRYPTION', ''), //'tls'),
Basically by doing this
$options['ssl']['verify_peer'] = FALSE;
$options['ssl']['verify_peer_name'] = FALSE;
You should loose security also, but in first option there is no need to dive into Vendor's code.
edit your .env and add this line after mail config lines
MAIL_ENCRYPTION=""
Save and try to send email
Finally! it was my AVG antivirus, it has a feature called email shield, disabled it and the error was gone.
For Laravel 9, following is enough to disable ssl check:
'verify_peer' => false,
Example:// config/mail.php
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
'port' => env('MAIL_PORT', 587),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'auth_mode' => null,
'verify_peer' => false,
],
...
This start to happen today in one of my servers using wordpress with a plugin that uses PHPMailer, with no recent changes.
The solution: sudo yum install ca-certificates
Now It works again perfectly, I did also an httpd restart (not sure if needed)
I can't figure out the real problem, I suspect that was a hardcoded date in the old ca-certificates package.
To resolve this problem you first need to check the SSL certificates of the host your are connecting to. For example using ssllabs or other ssl tools. In my case the intermediate certificate was wrong.
If the certificate is ok, make sure the openSSL on your server is up to date. Run openssl -v to check your version. Maybe your version is to old to work with the certificate.
In very rare cases you might want to disable ssl security features like verify_peer, verify_peer_name or allow_self_signed. Please be very careful with this and never use this in production. This is only an option for temporary testing.
change encryption type from SSL to TLS works form me.
in my case i did following
$mail = new PHPMailer;
$mail->isSMTP();
$mail->Host = '<YOUR HOST>';
$mail->Port = 587;
$mail->SMTPAuth = true;
$mail->Username = '<USERNAME>';
$mail->Password = '<PASSWORD>';
$mail->SMTPSecure = '';
$mail->smtpConnect([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
]
]);
$mail->smtpClose();
$mail->From = '<MAILFROM#MAIL.COM>';
$mail->FromName = '<MAIL FROM NAME>';
$mail->addAddress("<SENDTO#MAIL.com>", '<SEND TO>');
$mail->isHTML(true);
$mail->Subject= '<SUBJECTHERE>';
$mail->Body = '<h2>Test Mail</h2>';
$isSend = $mail->send();
with symfony I modify the dotEnv to include some small information and it works great
MAILER_DSN=smtp://user:pass#container_name:25?verify_peer=false&verify_peer_name=false&allow_self_signed=true
I my case the issue occurred on websites hosted on VPS with cPanel' WHM. After an update all the emails sent via Gmail SMTP stopped working.
As a solution, in the WHM I had to turn off
Restrict outgoing SMTP to root, exim, and mailman (FKA SMTP Tweak)
setting under
Home / Server Configuration / Tweak Settings
See pic.
I guess after WHM update this settings was turn on somehow or probably this is a new settings, I am not sure.
Reading app/config/mailphp
Supported : "smtp", "mail", "sendmail"
Depending on your mail utilities installed on your machine, fill in the value of the driver key. I would do
'driver' => 'sendmail',
for Laravel 5.4
for gmail
in .env file
MAIL_DRIVER=mail
MAIL_HOST=mail.gmail.com
MAIL_PORT=587
MAIL_USERNAME=<username>#gmail.com
MAIL_PASSWORD=<password>
MAIL_ENCRYPTION=tls
in config/mail.php
'driver' => env('MAIL_DRIVER', 'mail'),
'from' => [
'address' => env(
'MAIL_FROM_ADDRESS', '<username>#gmail.com'
),
'name' => env(
'MAIL_FROM_NAME', '<from_name>'
),
],
Go to vendor\swiftmailer\swiftmailer\lib\classes\Swift\Transport\StreamBuffer.php
comment line 250 and add this line:
//$options = [];
$options['ssl'] = array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true);
$default = [ ... ];
$turnOffSSL = [
'stream' => [
'ssl' => [
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
],
],
];
$environment = env('APP_ENV');
if ($environment === 'local') {
return array_merge($default, $turnOffSSL);
}
return $default;

PHPMailer - SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

Have encountered an issue where email should be sent from an mail server which has self signed certificate, the error which I get is :
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 class.smtp.php on line 327.
Has anyone encountered anything similar?
EDIT:
I have also tried to set the stream_context params (params: SSL context options):
$options['ssl']['verify_peer'] = false;
$options['ssl']['verify_peer_name'] = false;
$options['ssl']['allow_self_signed'] = true;
No luck, it still fails with the same error as pointed above.
Thanks.
PHP 5.6 introduces SSL certificate verification, so if your config is broken, it will fail with this error. You should fix your SSL, but you can revert to the old behaviour by setting the SMTPOptions property to not verify certificates:
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
Editing the library defeats the entire point of libraries - and if you do as Kaf's answer suggests, your code will break when you upgrade. Really, don't do that.
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.
I have the same problem. So i changed the file class.smtp.php in line 238:
public function connect($host, $port = null, $timeout = 30, $options = array()) {
if (count($options) == 0) {
$options['ssl'] = array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true);
}
now it works fine!
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.
I had the same problem. It turned out that my Postfix config was missing the intermediates and root certificates setting:
smtpd_tls_CAfile=/etc/ssl/certs/intermediate-root-bundle.crt
Even though this Postfix config has worked for years with Outlook and Thunderbird, PHP was more picky and failed the SSL check.
So even though you might be tempted to hack PHPMailer, please don't, and fix the underlying problem.
Just wanted to put my 2 cents in since I've been looking for a fix for days until I tried Kaf's solution and it worked!! Thanks #Kaf
Anyways... For me, PHPMailer was working fine until I decided to upgrade PHP to PHP5.6
Changes were made to open ssl in PHP 5.6. Here is the official docs:
http://php.net/manual/en/migration56.openssl.php
From the docs it says to set verify_peer and verify_peer_name to false
So just follow Kaf's answer and see if that works for you.
Editor's note: The doc also says this is not recommended! 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.

Does PHP's fopen() protect against typical attacks when accessing https resources?

If I use PHP's fopen() function to retrieve data from a HTTPS website, is that what one would call a secure HTTPS connection. i.e. Does it provide protection against man-in-the-middle and eavesdropping attacks?
Not by default, no.
It will always provide some form of protection against simple eavesdropping attacks as the data will always be encrypted (as long as the SSL server you are connecting to allows at least one encrypted cipher to be used - yes, null-encryption ciphers are allowed in HTTPS connections :roll-eyes:) However, by default, it will not protect against man-in-the-middle as it doesn't validate the server's certificates, therefore you cannot have any confidence that you have connected to the intended server.
Certificate validation can be switched on. To do so, you will need to provide a root certificate bundle and use the fourth argument to fopen that allows you to specify a stream context. The stream context allows you to modify the behaviour of the stream. The example below switches causes certificates to be validated against the root certificates in the specified bundle file.
$context = stream_context_create( array(
'ssl' => array(
'cafile' => 'ca_bundle.crt',
'verify_peer' => true
)
));
$file = fopen( $url, 'r', false, $context );

Categories