In Laravel, you can use the
MAIL_ENCRYPTION
environment
variable to select what type of encryption you want your email
connections to use. The default config files use this to set the
encryption
option on the
smtp
mail
transport.
In previous versions of Laravel this
encryption
option
is given verbatim to the Swift Mailer library used for SMTP emails
1
:
if (!empty($config['encryption'])) {$transport->setEncryption($config['encryption']);}
With
$transport
a
Swift_SmtpTransport
instance. This works exactly as intended.
However, Swift Mailer is not maintained anymore and Laravel 9 now
uses Symphony Mailer. With this, the use of the
encryption
setting is now different
2
:
$transport=$factory->create(new Dsn(!empty($config['encryption']) &&$config['encryption'] ==='tls'? (($config['port'] ==465) ?'smtps':'smtp') :'', ...));
With
$factory
an
EsmtpTransportFactory
instance. The
encryption
setting is no longer passed
verbatim and instead used to set the scheme to one of three possible
values. Thus, we must look at how the factory uses this scheme
3
:
$tls='smtps'===$dsn->getScheme() ?true:null;...$transport=new EsmtpTransport($host,$port,$tls,$this->dispatcher,$this->logger);
Combining this information, we can determine that
$tls
is set to
true
if and only if the
encryption
setting is
tls
and the
port
setting is
465
. Otherwise
$tls
is set to
null
. Before this value is used, however, it is further
modified in the
EsmtpTransport
constructor
4
:
if (null===$tls) {if (465===$port) {$tls=true; } else {$tls= \defined('OPENSSL_VERSION_NUMBER') &&0===$port&&'localhost'!==$host; }}
Here, if the
port
setting is
465
but this
$tls
is
null
it will be set to
true
anyway regardless of the
encryption
setting. We will ignore the other part for now, as it’s a mostly
unrelated edge case. Next, we need to investigate how this
$tls
is used
5
:
if (!$tls) {$stream->disableTls();}
Thus, when
$tls
is
true
implicit TLS
remains enabled, when
$tls
is
null
implicit
TLS is disabled instead.
But what about STARTTLS? There is one final detail that needs to be covered 6 :
if (!$stream->isTLS() && \defined('OPENSSL_VERSION_NUMBER') && \array_key_exists('STARTTLS',$this->capabilities)) {$this->executeCommand("STARTTLS\r\n", [220]); ...}According to this code, when implicit TLS is not enabled and the server advertises STARTTLS, Symphony Mailer always tries to enable it. There is no possibility to enforce or disable this.
Conclusion
To put all this information together, when the
MAIL_PORT
environment variable is set to
465
then Symphony Mailer
will end up using implicit TLS. Otherwise, Symphony Mailer will try to
use STARTTLS if the server supports it. There is no way for a Laravel
user to change this behaviour. The
MAIL_ENCRYPTION
environment variable ends up not affecting this process. This leads to
the following problems:
- You cannot enable implicit TLS for any non-standard port, nor can you disable it on the standard port.
-
You cannot disable STARTTLS even when it is optional. This prevents
you from connecting to your mailserver through
localhost. - You cannot enforce STARTTLS when you know a server supports it. This makes Symphony Mailer vulnerable to even trivial downgrade attacks.
As for the edge case skipped earlier, when your
MAIL_PORT
is undefined or set to
0
it is
modified. When
MAIL_HOST
is set to
localhost
port 25 with STARTTLS is used, otherwise port 465