1000-ft view: access control fails when apps don’t consistently enforce who can access which resources or functions. These secure demos show how to handle: IDOR (numeric), IDOR (guessable “UUID”), Function-level auth, Mass assignment, and Direct object access (DOR).
Uses the shared DB with module-scoped tables (a01_*). Seed data lives in db/seed.sql.
Compare with the Insecure version.
Secure idea: when fetching a note by numeric id,
include the current user’s id in the query (e.g., WHERE id=? AND user_id=?). Never fetch first then filter in PHP.
Secure idea: if using a public identifier like public_id,
make it unguessable (e.g., 128-bit random encoded) and still enforce access rules.
Don’t rely on obscurity alone.
public_id you own.Secure idea: don’t rely on hidden buttons or client checks. The server must verify the caller’s role (e.g., admin) inside the action.
current_user()->is_admin first.Secure idea: ignore unexpected fields from clients;
update only an allowlist (e.g., display_name). Never accept is_admin or credit blindly.
is_admin=1 or credit=9999 are ignored/clamped.Secure idea: fetch files by a DB record that encodes ownership
(files.user_id). Check owner in SQL, then resolve a canonical path under a safe root.