Login
ChallengesLearn
Scoreboard
Teams
SPNZ

LearnAccess ControlAccess Control Hardening
Access Control·Lesson 5 of 12

Access Control Hardening

Server-side ownership checks, deny-by-default, centralised gate functions, and the audit query that finds every missing check before an attacker does.

Intermediate14 min
Access ControlHardeningRBAC
Loading lesson…
PreviousMass AssignmentNextAPI Access Control

© 2026 SPNZ.

Terms of ServicePrivacy PolicyCookie Policy

Hardening access control means moving from ad-hoc, per-endpoint checks to a systematic architecture where every request passes through a consistent authorisation layer. It combines centralised gate functions, deny-by-default policies, server-side ownership verification, and comprehensive audit logging.

What you'll be able to do
  • Design a centralised authorisation gate that enforces access control for every request.
  • Apply deny-by-default policies and server-side ownership checks consistently.
  • Analyse the 2018 Facebook “View As” bug as a case study in missing access checks.
  • Implement audit logging to detect and debug access control failures.
Key terms
Centralised gate
A reusable middleware or function that every protected route invokes to verify authorisation — prevents the common mistake of forgetting a check on a new endpoint.
Deny by default
A security principle where access is denied unless explicitly granted. The default behaviour for any unclassified request should be a 403 response.
Audit log
An immutable record of access control decisions — both allowed and denied — that enables post-incident analysis and detection of anomalous patterns.
What is it?

From ad-hoc checks to systematic defence

A single missing access check is all an attacker needs. When authorisation logic is scattered across individual route handlers, it is inevitable that some endpoints will be missed — especially as the codebase grows and new features are added under deadline pressure. Hardened access control replaces this fragile pattern with a structured layer that every request must pass through.

The four pillars of access hardening are: centralised gate functions that enforce role and ownership checks in one place; a deny-by-default policy that returns 403 for any request that does not have an explicit allow rule; server-side ownership checks that always derive identity from the session; and audit logging that records every access decision for later review.

Hardened access control flow
Mini Map
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
Try it

Explore the audit dashboard

The sandbox below simulates an audit dashboard that logs every access control decision. Toggle between a vulnerable configuration (missing ownership checks) and a hardened configuration (centralised gate + audit logging). Watch how the same attacker requests produce very different outcomes — and how the audit trail makes the difference visible.

prod/admin/audit?config=vulnerable
Access Audit Dashboard
Security Audit Dashboard — Vulnerable configuration (no centralised gate)2026-06-07
3 allowed0 denied4 IDOR
Vulnerable configuration: no centralised authorisation gate. Multiple IDOR and privilege escalation attacks succeeded. No ownership or role checks are enforced.
Access log
7 entries
All times simulated. Toggle between vulnerable and hardened config to compare how the same requests are handled differently.
Real-world relevance

Facebook 2018 “View As” — a missing check in one code path

In 2018, Facebook disclosed a vulnerability in the “View As” feature that allowed attackers to view any user's profile as if they were that user. The feature was designed to let users preview how their profile looks to a specific friend, but a rarely-used code path — triggered by a specific combination of video upload logic and birthday wishes — skipped the ownership check entirely.

The bug affected 50 million accounts and required Facebook to invalidate access tokens for all of them. The root cause was not that Facebook lacked access controls, but that the check was implemented in individual handlers rather than a centralised gate. One handler out of dozens was missing the check, and the attacker found it. This is the textbook argument for centralised authorisation: humans make mistakes, but a single gate function is much harder to forget.

Mitigation

Build a centralised authorisation layer

A hardened access control system uses a gate function that every protected route must call. The gate checks authentication, role permissions, and resource ownership. Any request that does not match an explicit allow rule is denied by default — not accidentally permitted.

javascriptvulnerable
// VULNERABLE - ad-hoc checks scattered across handlers
app.get('/api/tickets/:id', async (req, res) => {
  // developer forgot ownership check
  const ticket = await db.query(
    'SELECT * FROM tickets WHERE id = $1',
    [req.params.id],
  );
  res.json(ticket.rows[0]);
});

// HARDENED - centralised gate + deny-by-default
type Permission = { resource: string; action: string };

async function authorize(req: Request, resource: string, ownerId?: string) {
  const user = req.session.user;
  if (!user) throw new AuthError('unauthenticated', 401);

  // Deny by default
  const allowed = user.permissions.some(
    (p: Permission) => p.resource === resource,
  );
  if (!allowed) throw new AuthError('forbidden', 403);

  // Ownership check
  if (ownerId && user.id !== ownerId) {
    throw new AuthError('not owner', 403);
  }
}

app.get('/api/tickets/:id', async (req, res) => {
  await authorize(req, 'tickets');
  const ticket = await db.query(
    'SELECT * FROM tickets WHERE id = $1 AND assigned_agent_id = $2',
    [req.params.id, req.session.userId],
  );
  if (!ticket.rows[0]) return res.status(403).end();

  // Audit log
  console.log(`[AUDIT] user=${req.session.userId} ticket=${req.params.id} ALLOWED`);
  res.json(ticket.rows[0]);
});

Complement the gate with structured audit logging. Log every authorisation decision — both allowed and denied — with the user ID, resource ID, action, and timestamp. In a security incident, the audit log is the fastest way to determine what happened, what data was exposed, and which users were affected.

Further reading
  • Facebook Security Incident (2018) — "View As" Bug(Facebook)
  • OWASP Cheat Sheet: Access Control(OWASP)
  • NIST SP 800-53: Access Control (AC) Family(NIST)
Key takeaways

What to remember

  • Scattered access checks guarantee that some endpoint will be missed. Centralise authorisation logic in a reusable gate function.
  • Deny by default: if a request does not match an explicit allow rule, respond with 403.
  • Server-side ownership checks must derive the user identity from the session, never from client-supplied parameters.
  • Audit every access control decision. A complete audit trail is invaluable for incident response and compliance.
  • The Facebook 2018 breach shows that even sophisticated platforms are vulnerable when access checks are not uniformly applied.

Knowledge check

0/3 answered · 0 correct
  1. 1.What is the primary advantage of a centralised authorisation gate?

  2. 2.What does "deny by default" mean in access control?

  3. 3.A developer adds an audit log that only records successful requests. What is the problem?