How to Fix an IDOR Broken Access Control Vulnerability

An IDOR vulnerability exists when an endpoint checks that the caller is logged in but never checks that the requested record actually belongs to them. Any authenticated user can change the id in the URL and read someone else's data. The fix is a single ownership check before the response.

Security Engineerpythonflaskidor

What makes an endpoint vulnerable to IDOR

Authentication answers "who are you." Authorization answers "are you allowed to do this." An endpoint that only authenticates - but never authorizes on the specific object - is vulnerable to IDOR.

Consider a billing API with a route like GET /invoices/<id>. The endpoint checks for a valid session, then returns whichever invoice matches the id:

@app.route("/invoices/<int:invoice_id>")
def get_invoice(invoice_id):
    user_id = current_user_id()
    if user_id is None:
        return jsonify({"error": "unauthorized"}), 401

    inv = INVOICES.get(invoice_id)
    if inv is None:
        return jsonify({"error": "not found"}), 404

    # BUG: any authenticated user can read any invoice
    return jsonify(inv)

An attacker with a valid session for user 1 can request /invoices/1002 and receive user 2's billing data. Invoice ids are sequential integers - trivial to enumerate with a loop.

The fix: compare owner_id to the caller's id

After fetching the object, check that the record's owner matches the authenticated caller. Return 403 Forbidden if it does not:

@app.route("/invoices/<int:invoice_id>")
def get_invoice(invoice_id):
    user_id = current_user_id()
    if user_id is None:
        return jsonify({"error": "unauthorized"}), 401

    inv = INVOICES.get(invoice_id)
    if inv is None:
        return jsonify({"error": "not found"}), 404

    # FIXED: ownership check - caller must own this invoice
    if inv["owner_id"] != user_id:
        return jsonify({"error": "forbidden"}), 403

    return jsonify(inv)

The check goes after the 404 guard - confirm the object exists first, then confirm the caller is allowed to see it. 200 only if both pass.

Why this matters: IDOR is #1 on OWASP

Broken access control is the top entry on the OWASP Top 10. IDOR is its most common form: the object reference is right there in the URL, and without a per-object ownership check, any authenticated user can read, update, or delete anyone else's data just by changing a number.

It is trivially exploitable and commonly missed in code review because the authentication logic is often correct - the bug is the missing second check.

Key patterns and variations

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What is an IDOR vulnerability?

IDOR (Insecure Direct Object Reference) is when an API endpoint uses a user-supplied id to fetch a record but never checks that the record belongs to the caller. Any authenticated user can change the id in the URL and read another user's data.

How do I fix an IDOR bug?

After fetching the object, compare its owner_id (or equivalent) to the authenticated caller's id. Return 403 Forbidden if they don't match. Authentication and authorization are two separate checks - being logged in is not enough.

How is IDOR different from being unauthenticated?

An IDOR vulnerability requires authentication - the attacker has a valid session. The bug is that the server never checks whether this authenticated user is allowed to access this specific record. The session is real; the permission is missing.

Does IDOR come under broken access control?

Yes. Insecure Direct Object Reference (IDOR) is a type of broken access control - it happens when an app uses a user-supplied ID to fetch a record without checking the requester owns it. The fix is a server-side ownership check.

How do you fix broken access control?

Enforce authorization on the server for every request: check the authenticated user owns or may access the specific record, deny by default, and never rely on hidden fields or client-side checks.

Keep learning

Close a SQL Injection in a Search EndpointSecurity projectNeutralize a Stored XSS in CommentsSecurity projectRemove Hardcoded Credentials From Source CodeSecurity projectSecurity roadmapStep by step to hiredSecurity interview questionsSTAR answersAll Security projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →