Every critical flaw I have reported so far has been the same kind of bug. A government document registry, a university learning platform and a payments app, three very different organisations, all made one mistake: the server returned data without checking whether the person asking was allowed to see it.

That class of bug is called broken access control. It has been number one on the OWASP Top 10 since 2021, and in my experience it is the flaw Nigerian platforms are most likely to ship.

This article explains what it looks like, why it keeps happening, and what fixes it. There are no live endpoints, payloads or reproduction steps here, only the patterns.

What broken access control means

A web application answers two separate questions on every request:

  1. Authentication: who are you?
  2. Authorisation: are you allowed to do this, to this particular record?

Most teams get the first one right. Login pages, passwords and one-time codes are visible, so they get tested. The second question is invisible. It lives inside every endpoint that fetches, updates or deletes a record, and if one of those endpoints forgets to ask it, nothing on the screen looks wrong.

IDOR (Insecure Direct Object Reference) is the most common form. The application takes an identifier from the request, such as a document number, a student ID or an email address, and fetches that record directly, without checking that it belongs to the requester.

Three real patterns

1. Sequential identifiers with no check in front

In the Corporate Affairs Commission's document system, a retrieval path served corporate records without verifying that the requester was entitled to them. The identifiers were sequential, so the exposure was not limited to one record. Anyone who could fetch one document could, in principle, walk through the rest.

I reported it through ngCERT, which validated it within 48 hours.

The lesson: sequential IDs did not cause the flaw, the missing authorisation check did. But sequential IDs turn a single-record leak into a systemic one.

2. A lookup keyed on something that is not a secret

In the BillPoint payments app, an account lookup returned a user's profile, including phone number and wallet balance, to anyone who supplied that user's email address.

An email address is not a secret. It is printed on business cards and shared in WhatsApp groups. Treating it as proof of ownership means every account is effectively public to anyone who knows or guesses the address.

The lesson: a value that identifies a record is not the same as a value that proves you own it.

3. Student data behind a logged-in session

On a university learning platform, the flaw exposed personal data belonging to other students. Being logged in was treated as enough, when the real question was whose data this session may see.

The lesson: authentication is the start of access control, not the end of it.

Why it keeps happening

  • Frameworks handle login, not ownership. Authentication middleware is a one-line install. Object-level authorisation depends on your data model, so no framework can do it for you.
  • It passes every normal test. A developer testing with their own account only ever requests their own records, so the missing check never fails.
  • APIs multiply the surface. A mobile app and a web app often share one API, and every endpoint needs its own check.
  • Deadlines favour visible features. Nobody demos an authorisation check.

How to fix it

Check ownership on the server, on every request that touches a record. In practice:

  • Fetch records through the user, not by ID alone. Query for "invoice 1042 belonging to the logged-in user", never just "invoice 1042".
  • Deny by default. An endpoint with no explicit rule should refuse, not allow.
  • Centralise the check in one policy layer so new endpoints inherit it, instead of repeating if statements in every handler.
  • Never treat email addresses, phone numbers or account numbers as proof of identity.
  • Use non-sequential identifiers such as UUIDs. This does not fix IDOR, but it removes easy enumeration while you fix the real problem.
  • Rate-limit and log record lookups, so mass access is noticed quickly.
  • Add a test for every endpoint: user A requests user B's record and must get a 403 or 404.

A quick self-check for your team

Pick your five most sensitive endpoints and, for each, answer:

  1. Where exactly is the ownership check?
  2. What happens if I change the ID in the request to someone else's?
  3. Is there an automated test that proves question 2?

If any answer is "I'm not sure", that endpoint deserves a review this week.


If you run a platform in Nigeria and want a second pair of eyes on your access control, see security reviews. If you have found a flaw in something I built, email security@ndemafiawilsmith.com.