Course / 09 Web security / Lesson 07 of 28

Access control and IDOR

Estimated time: 16 min reading · 12 min activity

A request can name a record without granting permission to read it. Access control checks whether the caller may perform the requested action on that resource. An object identifier, or ID, selects the record. Insecure direct object reference, abbreviated IDOR, is an access-control failure in which a caller-controlled reference reaches another object without the required permission check.

The fictional policy allows only the owner to read a private record, while shared records may be read by A or B. It maps allowed reads to HTTP 200 and forbidden reads to HTTP 403. Those are this fixture's response rules; a real service may choose another permitted response, including hiding a resource with 404.

Before you begin

Estimated time
16 min reading, 12 min activity. Setup and optional practice may take longer.
Where you work
Read the original fictional requests, source fragments and state tables in a current desktop or mobile browser. The required activity needs no server, database, account or installed security tool. Do not send the printed requests to any service.
Without a lab
Use the complete printed evidence to trace the data or state on paper. Write the decision, the fact supporting it and one limit of the model, then compare with the activity rubric. This does not establish live service behavior. Reading works without JavaScript. Checking requires JavaScript and a secure browser context; saving also requires usable browser storage.

After this lesson you can:

  • Apply the private-owner rule to A's read of row 42.
  • Apply the same rule to A's read of row 7.
  • Add a shared-row reading without moving the check into the browser.

01Example

Use a shared record to demonstrate the policy, then apply its private-record rule to the different activity and checkpoint cases.

Record 9: visibility shared
Reader: B
Policy: shared reads allowed for A or B
Decision: allowed; fixture status 200
The permission comes from the policy, rather than the record number.

02The id is not the grant

The worked shared record is available to both listed readers because the stored policy permits it. The activity then switches to a private record and requires comparing reader with owner. Identity, object and action are separate facts; a successful sign-in only supplies the identity.

The server should check the stored ownership or sharing rule for the requested resource on each relevant action. Hiding links or choosing hard-to-guess identifiers may change discoverability, but neither replaces that decision. The supplied request matrix is fictional evidence, not permission to try changing IDs on another service.

Decide a read from the owner and the visibility, after authentication has already succeeded.Model. A teaching model drawn by hand. It shows the idea, not real tool output.

Read the full explanation below.

Reader B reads record 9Consider record number alone
Shared-read policy

Policy: shared reads allowed for A or B

met · B is explicitly one of A or B.
Record visibility

Record 9: visibility shared

met · Record 9 is shared.
Apply the stated rule
Allowed; fixture status 200

Policy and visibility grant this read; the object identifier alone does not.

Object policy grants the read; changing an identifier is not a permission check or a request here.

Policy and visibility grant this read; the object identifier alone does not.

Follow the explanation

  1. Policy and visibility grant this read; the object identifier alone does not.
  2. The supplied allowed decision depends on the explicit A-or-B policy, not on guessing another ID.
Read every state and value

1. Policy and visibility grant this read; the object identifier alone does not.

Case
reader
Conditions inspected
Shared-read policy · Record visibility
Decision shown
Yes

2. The supplied allowed decision depends on the explicit A-or-B policy, not on guessing another ID.

Case
id-only
Conditions inspected
Shared-read policy · Record visibility
Decision shown
Yes
Object policy grants the read; changing an identifier is not a permission check or a request here.

03Decide A's read of row 7

Read the evidence before comparing your reasoning with the activity rubric. Use the matrix and the policy sentences.

id,owner,visibility,reader,action
42,B,private,A,read
7,A,private,A,read
7,A,private,B,read
9,A,shared,B,read

Download the object matrix

Original fictional permission policy. No object was requested.
An authorized read returns 200; a forbidden read returns 403.
Only the owner may read a private row. Shared rows may be read by A or B.
Every caller in the matrix is already authenticated.

Worked case: B reads shared row 9. The sharing rule allows this read.
Activity evidence: A reads private row 7, owner A. Compare reader and owner.
Checkpoint evidence is stated in the question. Decide it independently.

Download the request decisions

  1. Find the row where reader A acts on id 7. Write the owner and the visibility.
  2. Apply the private-owner rule.
  3. Compare your reasoning with the private-owner rule: the activity reader and stored owner match, so the allowed-read branch applies. Keep the later record-42 checkpoint separate.
  4. Stop. Do not request a real object's id.

04Optional challenge

Add the shared rule for row 9 and say who may read it. The rubric is: A or B may read it, and the server still makes that decision. About ten minutes.

05Fresh cases for the checks

Use these fictional cases for the checks below. They have different inputs from the worked examples. Read them here or make notes; no installation or external service is needed.

Object case A: new policy matrixOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.

All callers are authenticated. Allowed reads return 200; forbidden reads return 403.
Only the owner may read a private row; shared rows may be read by Lina or Oren.
P: caller Lina reads private row 63, owner Oren.
Q: caller Oren reads private row 64, owner Oren.
R: caller Lina reads shared row 65, owner Oren.

Object case B: independent changed conditionOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.

Row 81 is private, owned by Lina; caller Oren requests read.
The server then changes only visibility to shared, with the same Lina/Oren sharing rule.
The interface has hidden its link; the submitted read is still evaluated on the server.

06Explain your evidence

Optional written self-check: explain one decision using the supplied case. No tool run is required.

Check your explanation: identify caller, object, owner and action; apply private and shared rules; derive the mapped status; place enforcement on the server independently of interface visibility.

07Common mistakes

If you forbid A's read of row 7, you used row 42's owner. Row 7 is owned by A.

If you allow every authenticated read, you stopped at identity. The card still has a private rule.

If you treat a random-looking id as the control, guessing is harder and the check is still required.

References

  • RFC 9110: HTTP semantics

    Sections 15 and 15.5.4. 200 means the request succeeded. 404 can stand in for 403. This fixture states its own 200 and 403 mapping.

  • PortSwigger: Access control

    Explains the failure mechanism and prevention. Use it for further reading after reasoning from this page's original model; a topic page is not evidence that a lab was launched.