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 MAI