Back to Feed
article 2m read

Company admins could rename the super-admin role

Opening the roles endpoint to company admins let them list, edit and delete the one role that wasn't theirs. Each operation names a role differently, so closing it took four checks, and the rename-by-id one is the one I'd have missed.

Article DepthPractice

Flow

Four role actions and the check on each

  1. 1List

    Points at nothing. The super-admin role is dropped from the reply.

  2. 2Create

    Points by new name. The name is trimmed, lowercased and compared.

  3. 3Update

    Points by id and new name. Both the new name and the stored name are checked.

  4. 4Delete

    Points by id. The stored name is looked up and checked.

Any company admin in my staff scheduling app could pull up the role list and see the super-admin role in it. They could also save over it or delete it.

One gate, no row check

That day I let company admins into parts of the admin API, so each company could run its own stores, users and roles. The roles endpoint had one gate: are you an admin of this company, or a super admin? Past that, it did whatever the request asked.

That gate checks who may use the endpoint. It never checks which rows you may touch. One row in that table isn't any company admin's business.

I'd bet on a mess rather than a takeover, but I haven't checked how much harm an edit to it could have done. Super-admin status rides on a flag in the login token, and setup hands the role to the first account by name.

A check for each action

Each action points at a role in its own way, so each got its own check for anyone who isn't a super admin:

  • Listing drops the protected role from what comes back.
  • Creating compares the new name, trimmed and lowercased, to the protected one.
  • Updating checks the new name and also looks up the name already stored for that id.
  • Deleting does that same lookup.

The lookup is the part I'd have missed. Without it, someone could take the protected role's id and rename it to something dull, and the request would look fine.

A refusal looks like any other "forbidden" answer. Super admins see no change, and the admin docs now say the built-in role is hidden from company admins.

Plain text, no test

New names are cleaned up before the compare, but the stored name isn't. If that row is ever saved with odd spacing or capitals, the id lookup stops matching.

The protected name is plain text typed into each check, and there's no test yet. A deliberate rename by a super admin would switch every one of those checks off.

When company admins save a user, they can only add roles from a short fixed list of store roles, so they can't hand out the protected one that way. That's the only other door I've checked.

Keep reading

Pick up a connected idea or branch into a nearby one.

5 paths forward