Security Tip: Non-production Mail Sending

[Tip#21] It may seem strange but non-production mail can be a security risk.

Security Tip: Non-production Mail Sending

🤓 Learn to Think Like a Hacker with my hands-on practical security course: Practical Laravel Security! 🕵️

👉 When was your last security audit or penetration test? Book in a Laravel Security Audit and Penetration Test today! 🕵️


It’s incredibly common to set up our non-production environments (i.e. staging, testing, pre-prod, etc) to match production and send out emails using our mail transport providers. This gives us full functionality and makes testing users easy, but it also introduces a huge risk.

How many non-production sites have randomly generated test users? Can you guarantee that all of those test user email addresses are going to a domain/inbox you control?

All it takes is for one external email address to be included and a notification to go out and you’ve announced your staging environment to someone external to your team. It’s especially easy to do if you import your production data into non-production systems (please don’t do this!1)…

It happens all the time:

If someone with malicious intent discovers your non-production environments, they may be able to figure out how to login (you can probably just use their existing login details, or a password-reset!).

So what’s the big deal?

Well, non-production systems have non-production code. There can and will be bugs, some may even be security vulnerabilities. Someone with malicious intent may discover a way to exploit a vulnerability not present on production and gain access to the admin accounts, or the server/infrastructure. If it’s a subdomain alongside your production site, they may be able to bypass CSRF protections and use that to pivot into an account on production. Your production site may be compromised through the staging site you told them about.

Sounds unlikely, but it has happened before.

What To Do?

There are a few options to choose from, depending your needs. Pick the option that works best for you and your team, and keep those non-production emails safe.

  1. Use Sandbox mode from your mail provider.
    Some mail providers, like Postmark, provide a sandbox mode that automatically sandboxes all emails it receives from your application, without any changes being made in your app. This allows you to fully test your integration and mail sending.

Use a local script on your server to catch all emails like Mailcatcher or MailHog.

Allows you to keep all emails isolated on the server, with common access for your team. There is no risk of any data being sent off your servers.

  1. Use an dedicated mail sandboxing service like HELO and MailTrap, or configure a one using the SMTP mail driver.
    These have the benefit of being specifically designed for sandboxing and testing emails and may support teams.

Use Laravel’s alwaysTo mail helper: Mail::alwaysTo('stephen@rees-carter.net');

This is a great option if you want all emails to end up in a common inbox.

  1. Use Laravel’s 'log' mail driver to write all mail to a log file.
    Note, this is only really useful in local development.
Note, the only tool/service listed that I have experience using is Mailcatcher. I have it set up on my local dev to catch everything PHP tries to send out.

I haven’t used any of the other services as I don’t have any staging sites that send email, so I cannot recommend any of them specifically. That said, I do use Postmark for my apps, and I’ll definitely use their sandbox if I need to catch emails from staging in the future.

  1. I’ll plan to cover this topic in an upcoming In Depth.