Inspired by generalized themes from a Botnet Security education-sector assessment. Campus Demo, its users, records, messages and code are fictional teaching examples—not customer evidence or an exploitable application.
The student is signed in. The document exists. Neither fact answers the important question: should this student be allowed to read it?
That gap is where many application security findings begin. Before thinking about payloads, learn to describe the decision the server should make. This guide follows a fictional education portal through three moments: opening a record, completing sign-in and explaining the evidence.
01 / A login is not a permission
Campus Demo separates students into school tenants. Maya belongs to North School. Her session is valid, but it grants no general right to read South School records. The mock screen below illustrates a policy failure: the application has displayed a synthetic record outside her permitted scope.
Admission record
Signed in as Maya · student
- Session tenant
- North School
- Record tenant
- South School
- Record data
- Synthetic sample only
- Policy
- Own school + own record
The issue is not that a record has an identifier. It is that the server did not enforce the relationship between actor, action and object. An opaque identifier may reduce guessability; it cannot grant or deny permission.
Object-level authorization asks whether Maya may read this record. IDOR describes a common exposure of a missing object check; BOLA is the broader API risk. Function-level authorization asks whether she may perform the action at all—for example, approving an admission. A hidden approval button does not enforce that rule on the server.
The repair belongs in the service handling the operation. Derive identity and tenant membership from validated authentication, apply the action policy and keep data retrieval within the allowed scope. Exports, downloads, background jobs and cache entries need the same boundary. A student-only ownership rule also needs a deliberate alternative for legitimately delegated staff access.
Pause and check: if changing an identifier produces a different response, what is still missing? The expected access policy, the object's ownership and evidence that protected data or state crossed that policy. A response difference alone is not enough.
02 / Sign-in has intermediate states
A login flow may accept a password and still require an additional factor. During that interval, the application can legitimately issue a short-lived challenge token. The problem is not the existence of that token; it is granting protected privileges before verification is complete.
This fictional request/response shows the expected defensive behavior. An incomplete sign-in cannot retrieve documents. The placeholder is not a real credential, and the example domain does not identify a target.
GET /api/me/documents HTTP/1.1
Host: campus.example
Authorization: Bearer <fictional-challenge-token>
HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: no-store
{
"error": "authentication_incomplete",
"next_step": "complete_required_factor"
}The exact status and error shape depend on the API contract. What matters is that no protected data or side effect is available. Similarly, a 200 may contain a legitimate rejection, while a 429 alone does not prove the operation was blocked. Read the application outcome alongside the status.
Verification challenges need an expiry, purpose and account binding, single-use handling and server-enforced attempt limits. Resending a code must not silently create unlimited verification attempts. Recovery and cooldown design must also avoid making legitimate accounts easy to lock out.
A public record-recovery screen has a related trap. A name or email identifies a possible record; it rarely proves entitlement to read it. Verify the requester through an appropriate channel, check entitlement and minimize the response. Rate limiting reduces repeated use but cannot fix excessive disclosure in even one response.
03 / Report the broken rule
Write the finding around a contradiction, not around a tool. In the fictional screen above: “A North School student received a South School record despite a same-school, own-record policy.” That sentence identifies the actor, object, rule and consequence.
Keep the claim bounded. One unauthorized record establishes that disclosure, not unrestricted access to every tenant. Use synthetic data and minimum sufficient evidence within written scope. Avoid retaining personal data simply to make a report look more convincing.
- Before the fix: document the policy, actor, object and actual server decision.
- After the fix: disallowed access returns no protected content and causes no unauthorized state change.
- Also verify: legitimate owners and delegated roles can still complete permitted work.
Take this with you: identify the user, complete the required authentication state, then authorize the specific action on the specific object. Three separate decisions—not one login check.
Further reading: OWASP authorization guidance · OWASP authentication guidance · Botnet's web application assessment model.
