~ 4 min read
Secure JavaScript Coding to Avoid Insecure Direct Object References (IDOR)
Insecure direct object references (IDOR) are an important web application security concept that every developer should understand. IDOR vulnerabilities allow attackers to access unauthorized data and functionality by manipulating object identifiers used in web applications. In this post, I’ll explain IDORs and provide examples to help you prevent these issues in your Node.js and JavaScript server-side applications.
What is an Insecure Direct Object Reference?
An insecure direct object reference occurs when a web application uses object identifiers directly to access backend database records or application functionality without any access control checks.
For example, consider a web app that displays user information based on the user ID passed in the URL, like this:
https://app.com/users?user_id=13579
You can also imagine a similar vulnerable Node.js app that uses the user ID to retrieve data from a database:
app.get('/users/:id', (req, res) => {
const user = db.users.find(req.params.id);
res.render('user', { user });
});
If the application doesn’t verify that the current user is authorized to view details for user 13579, an attacker could simply modify the user_id
parameter to view other users’ private data.
This is known as an IDOR vulnerability and allows attackers to access unauthorized data. It can enable serious attacks like horizontal and vertical privilege escalation.
Real-World Example: @clerk/nextjs IDOR Vulnerability
IDOR vulnerabilities were recently discovered in popular JavaScript authentication library @clerk/nextjs
. Versions between 4.7.0 and 4.29.3 were affected.
The issue was found in the auth()
method used in Clerk’s Next.js router handlers and getAuth()
method in page route handlers. These returned user authentication objects without properly verifying the calling user’s identity and permissions.
This could allow:
- Unauthorized access if attackers could guess other user IDs
- Privilege escalation by modifying the user ID to a higher privilege account
The methods assumed caller identity based only on the passed IDs without additional checks. By not verifying if callers were authorized to access other user data, the methods enabled IDOR attacks.
The vulnerability has been patched in @clerk/nextjs@4.29.3
by improving input validation and adding rate limiting protections.
This real-world vulnerability highlights why validating caller identity and permissions is critical when exposing user identifiers in applications and APIs.
As a JavaScript developer, be aware of this issue. Follow authorization best practices if your code exposes database record IDs or other sensitive object identifiers. Use framework protections where possible or implement your own access control checks in custom code.
To take your knowledge to the next level, be sure to check out my popular Secure Coding in Node.js book series.
More vulnerable IDOR Scenarios to consider
To better understand how IDOR issues can arise, consider these common examples:
Exposing record IDs in URLs:
https://app.com/record?id=1234
Attackers can modify ‘id’ to access other records.
Another use-case where IDORs pose a problem is by referencing user uploads on filesystem:
https://app.com/static/uploads/13579.png
The file name contains a user ID - attackers can access other users’ files. Or for example, imagine that the web application generates predictable URLs for user profile pictures based on user IDs.
👋 Just a quick break
I'm Liran Tal and I'm the author of the newest series of expert Node.js Secure Coding books. Check it out and level up your JavaScript
Should I Use Sequential IDs in My Database?
Using sequential, auto-incrementing IDs in database tables is standard practice. However, as we saw above, these IDs can introduce security issues if used improperly in web applications.
Kent C. Dodds asked on Twitter:
Please give me one good reason to use serial/auto-incrementing number IDs in a database table.
— Kent C. Dodds 🌌 (@kentcdodds) January 13, 2024
So now you know:
You CAN use auto incrementing IDs as primary keys in database tables. They offer performance benefits and preserve data integrity.
You SHOULD NOT expose these unprotected IDs in client-facing applications. Instead, use authorization checks, access control lists, hashed IDs, or indirect reference maps to securely call data.
So sequential IDs themselves are not insecure - the vulnerability arises from directly exposing them to users, which many developers might do without realizing the security implications.
IDOR Prevention Tips
Here are some tips to avoid introducing IDOR issues in your web applications:
- Leverage authorization frameworks and services - IDORs fall under the OWASP Top 10 category of Broken Access Control. Use authorization frameworks like Permit.io to implement access control checks and prevent unauthorized access.
- Always implement access control checks - Verify if the current user has permission to access any requested objects on the server-side before returning data.
- Hash IDs exposed to clients - Hash IDs before adding them to URLs or API responses users can access. This preserves back-end reference without exposing real IDs. In general, avoid exposing and basing any functionality on database IDs which are incremental in nature, and prefer UUIDs or other non-sequential identifiers.
- Use indirect reference maps - Instead of numerical IDs, reference objects by less guessable UUIDs or other tokens. Map these to internal IDs on the server.
We see that IDOR issues commonly occur due to relying on predictable identifiers without verification. Be vigilant about adding authorization checks when exposing database keys or other identifiers.
I hope this gives you a solid understanding of insecure direct object reference vulnerabilities!