Laravel Security In Depth

Share this post
Security Tip: Don't Hardcode Admin Emails
larasec.substack.com

Security Tip: Don't Hardcode Admin Emails

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

Stephen Rees-Carter
Mar 17
Comment
Share

Greetings friends! A big welcome to all the new subscribers, it’s great to have you here! 😀 As of the time of writing this, there are over 764 (free and paid) subscribers to Laravel Security in Depth, which is incredible. Thanks for your support and your desire to learn more about Laravel Security!

If you’re a free subscriber, please consider upgrading to a paid subscription to receive weekly security tips (like this one), and our big monthly In Depth emails. Our next In Depth goes out next week, and will cover Content Security Policies, and trust me, you’re going to want to read this one!

Subscribe now

One final thing before we move onto our tip. I’ve recently started doing Security Audits and Penetration tests for Laravel apps. If you’re interested in getting me to audit your apps and help you with security, please reach out!


Don’t Hardcode Admin Emails

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/9.x/horizon#dashboard-authorization
Nova: https://nova.laravel.com/docs/3.0/installation.html#authorizing-nova
Spark Classic: https://spark-classic.laravel.com/docs/12.0/kiosk#configuration
Telescope: https://laravel.com/docs/9.x/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.

CommentComment
ShareShare

Create your profile

0 subscriptions will be displayed on your profile (edit)

Skip for now

Only paid subscribers can comment on this post

Already a paid subscriber? Sign in

Check your email

For your security, we need to re-authenticate you.

Click the link we sent to , or click here to sign in.

TopNewCommunity

No posts

Ready for more?

© 2022 Stephen Rees-Carter
Privacy ∙ Terms ∙ Collection notice
Publish on Substack Get the app
Substack is the home for great writing