August 26, 2026
I Watched a Paid Course for Free (A Short IDOR Story)
Found this on a course-selling website (letβs say example.com).

By Ahmed yasser
1 min read
Free courses had a "Resume" button. Clicking it sent a POST request to:
POST /contentload
programId=xxxx&
contentId=xxxx&
subscriptionId=xxxx&
_token=xxxx&viewtype=truePOST /contentload
programId=xxxx&
contentId=xxxx&
subscriptionId=xxxx&
_token=xxxx&viewtype=trueThree IDs, one token, and boom β video loads.
Naturally, I asked the smartest question in security: "what if I just change the IDs?"
programId= the coursecontentId= the specific lesson
So I went to a paid course's page (one I never subscribed to). Turns out the page happily lists every lesson's contentId right there in the HTML β locked or not, paid or not, doesn't matter, it's all sitting in plain sight like a menu at a restaurant you're not allowed to eat at.
I grabbed one, dropped it into my free-course request, replaced programId and contentId, hit send.
The paid lesson played. Full video. Zero payment. Zero subscription. My browser had literally just shown me a "please subscribe" popup for that exact same course minutes earlier.
The problem: the server checks if you have a valid session and a valid-looking request. It never checks if you specifically are allowed to access that specific course/lesson combo. It just trusts whatever IDs you send it.
This is a classic IDOR β Insecure Direct Object Reference. The lock exists. The lock just doesn't check whose key it is.
The fix: on every /contentload request, the server should verify that the logged-in user actually has an active, valid subscription for that exact programId/contentId β not just that some subscription exists somewhere.
Moral of the story: a "subscribe now" popup in your UI means nothing if your API will hand out the content anyway to anyone who asks nicely with the right ID.