Security Tip: Don't Hardcode Admin Emails

[Tip#17] It's easy to forget to update the admins list when it changes...

Security Tip: Don't Hardcode Admin Emails

👉 Looking to dive deeper into Laravel security? Check out Practical Laravel Security, my hands-on security course that uses interactive hacking challenges to teach you about how vulnerabilities work, so you can avoid them in your own code! 🕵️

⚠️ Need a security audit but don't have the budget for a full penetration test? Book in a Laravel Security Review! 🕵️


Hardcoded admin emails (or usernames) are something I’ve seen in almost every codebase I’ve worked on and/or audited. That’s really not surprising though, since the documentation itself basically tells you to do it like this1. However when it comes to security, it’s a terrible idea.

Let’s take Laravel Nova as an example. The Nova documentation tells you to add a Gate definition into your `app/Providers/NovaServiceProvider.php` to define admin users.

Consider this Gate:

Gate::define('viewNova', function ($user) {
    return in_array($user->email, [
        'frodo@example.com',
        'samwise@example.com',
        'meriadoc@example.com',
        'peregrin@example.com',
        'fredegar@example.com',
    ]);
});

It looks simple and easy to update, but it’s 2 levels deep inside a system class, so it’s super easy to forget about. If one of the admins leaves the company (or loses admin access), there is a good chance their email address will be left in the list and retain admin access.

If their account is compromised or given to someone else, that admin access will be available when it shouldn’t be. This potentially opens up admin access to a hacker or someone with malicious intentions. In this example, Nova gives full access to modify the database - which gives the hacker full access to do whatever they want.

Consider the scenario where they are fired and want to get revenge: How quickly can you revoke access, if you need to make a code change, push through a PR, merge and test in CI, and then deploy to your fleet? (Assuming you remember to update the service provider at all!)

Also, if you’re using multiple packages with their own authorization lists, it’s easy to forget some of them when adding/removing admins, leaving lingering access in some areas.

The worst offender is putting the admin list in your javascript. This is sent to the browser, so anyone who discovers it has full access to your list of admins. It’s then trivial to look up credential stuffing lists to find potentially working passwords for a site admin account2. 😱

What To Do Instead

There are some really simple ways to avoid hardcoding admin emails. I usually either store admin permissions in the database, or define emails in configuration files. It doesn’t really matter how you do it as long as it’s not hardcoded, and is easy to change and review securely.

Here are two example solutions:

1. Define an admin flag on the user models:

Gate::define('viewNova', function ($user) {
    return $user->admin;
});

This also solves the Javascript problem, as the user model data passed to the browser can contain the admin flag too.

2. Store the admins list in .env:

// .env

APP_ADMINS="frodo@example.com,samwise@example.com,meriadoc@example.com,peregrin@example.com,fredegar@example.com"

// config/app.php

'admins' => explode(',', env('APP_ADMINS')),

// NovaServiceProvider

Gate::define('viewNova', function ($user) {
    return in_array($user->email, config('app.admins'));
});

  1. Horizon: https://laravel.com/docs/horizon#dashboard-authorization
    Nova: https://nova.laravel.com/docs/installation.html#authorizing-access-to-nova
    Spark Classic: https://spark-classic.laravel.com/docs/12.0/kiosk#configuration
    Telescope: https://laravel.com/docs/telescope#dashboard-authorization

  2. If you want to scare yourself, go to Have I Been Pwned and check all of your admin emails. I guarantee at least one will have been Pwned (if it’s not a newish company domain).
    Also, don’t forget to subscribe to alerts for your domain names , to receive alerts if anyone on your team is Pwned.