Finding and fixing insecure direct object references in Python
Tags: applicationsecurity, codesecurity, opensourcesecurity, python
Published: 2023-07-20 02:00:20

An insecure direct object reference (IDOR) is a security vulnerability that occurs when a system’s implementation allows attackers to directly access and manipulate sensitive objects or resources without authorization checks. For example, an IDOR can arise when an application provides direct access to objects based on user-supplied input, allowing an attacker to bypass authorization.


Understanding IDOR vulnerabilities
----------------------------------


We must tackle IDORs to maintain the confidentiality, integrity, and availability of the sensitive data handled by your Python applications. Developers must understand IDOR vulnerabilities to prevent unauthorized access, maintain user trust, and avoid potentially severe financial and legal consequences. 


### Types of IDOR


IDORs take different forms depending on application implementation and functionality.


#### **IDOR with reference to objects**


This form of IDOR occurs when an attacker can access or modify an unauthorized object. A common example is when a web application allows access to sensitive data such as bank accounts via a simple request, like `example.com/accounts?id={account_id}`. If the application doesn’t effectively verify the requester’s permissions, unauthorized users can view or manipulate account information.


#### **IDOR with reference to files**


This type of IDOR involves the unauthorized retrieval of files. For example, if a chat application stores confidential conversation logs as incrementing numbered files, an attacker could access private conversations by making similarly numbered requests — `example.com/1.log`, `example.com/2.log`, and so on.


#### **Blind IDOR**


Blind IDOR refers to cases where the exploitation isn’t directly visible in the server response, even though unauthorized modification may have occurred. With blind IDOR, an attacker can change another user’s private data without viewing it. For example, if a web application allows a user to modify their profile settings via an API call, like `example.com/users/update?id={user_id}`, an attacker could manipulate the `user_id` in the request, changing a user’s profile settings without directly viewing their data.


Common patterns to help us spot IDOR vulnerabilities in code
------------------------------------------------------------


Detecting IDOR vulnerabilities in code requires vigilance and an understanding of common patterns:


* **Lack of access control**: An application that takes user input to directly access resources — without verifying user authorization — probably has an IDOR vulnerability.
* **Guessable identifiers**: Easily guessable identifiers — such as sequential integers (`user_id=99001`) or short strings (`user_id=user1`) — increase the risk of IDOR vulnerabilities in an application. Using hard-to-guess identifiers, such as Universal Unique Identifiers ([UUIDs](https://docs.python.org/3/library/uuid.html)), can mitigate this risk.
* **Direct access to files**: An application that gives users direct access to any resources — such as images or CSV files — via URLs or request parameters is vulnerable to IDORs. We must ensure proper control mechanisms are in place to avoid giving users direct access to files.


Creating a Python application with IDOR using FastAPI
-----------------------------------------------------


In this section, we’ll create a web API vulnerable to IDOR using the Python framework FastAPI. Then, we’ll examine how to remediate the vulnerability.


To follow along with this hands-on tutorial, **please ensure you have Python 3.x installed** on your machine.


### Getting started


First, we need a new application. Create a new directory for your project and move into the directory:



```
mkdir fastapi-app; cd fastapi-app
```

Then, install [FastAPI](https://snyk.io/advisor/npm-package/fastapi), [Uvicorn](https://security.snyk.io/package/pip/uvicorn), [SQLAlchemy](https://security.snyk.io/package/pip/sqlalchemy), and [SQLite](https://snyk.io/advisor/npm-package/sqlite) with the following command:



```
pip install fastapi uvicorn sqlalchemy
```

Finally, create a file named `main.py` in the project directory.


### Creating a vulnerable API using FastAPI


Now that we’ve set up our directory structure and the requirements, let's write the code containing an IDOR vulnerability. First, copy the code below into `main.py`:



```
from fastapi import FastAPI, HTTPException, Depends
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session

app = FastAPI()

# SQLite database configuration
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

# User model
class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    uuid = Column(String, unique=True, index=True)
    username = Column(String, unique=True, index=True)
    email = Column(String, unique=True, index=True)

Base.metadata.create_all(bind=engine)

# Dependency for database session
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/users/{user_id}")
async def get_user_by_id(user_id: int, db: Session = Depends(get_db)):
    user = db.query(User).filter(User.id == user_id).first()
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user
```

The IDOR vulnerability is in the `/users/user_id` endpoint. As the application has no access controls, any user can view other users’ data by changing the `user_id` in the URL.


### Identifying and fixing IDOR vulnerabilities


Now, let’s find and fix the IDOR security flaw in our FastAPI application.


### Finding vulnerabilities


To identify IDOR vulnerabilities, we must look for specific patterns in the code. In the FastAPI application, we should examine the `/users/{user_id}` endpoint, which represents a potential IDOR vulnerability for the following reasons:


* **Unprotected direct object references**: In the `get_user_by_id` function, the `user_id` parameter directly refers to the `User` object in the database. Any client could manipulate the `user_id` value in the request to access the data of other users because no access control mechanisms protect this direct reference.
* **Lack of authorization**: In the same `get_user_by_id` function, there are no checks to verify whether the requesting user is authorized to access the requested user data. Any user can access any other user’s data by manipulating the `user_id` parameter.


### Fixing vulnerable code


To fix the IDOR vulnerability, we need to update the `/users/{user_id}` endpoint to have proper access control checks. 


Let’s simulate an authenticated user and only allow access if the `user_id` in the URL matches the authenticated user’s ID. For the purposes of demonstration, let’s assume that the authenticated user has an ID of `1`. Here’s the updated code:



```
@app.get("/users/{user_id}")
async def get_secure_user(user_id: int, db: Session = Depends(get_db)):
    # Assume user authentication and authorization are performed here
    authenticated_user_id = "hbecec-edeek-wxwexe-cdcece" # better to use UUIDs

    if authenticated_user_id != user_id:
        raise HTTPException(status_code=403, detail="Not authorized to access this user")

    user = db.query(User).filter(User.id == user_id).first()
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user
```

In this code block, we added an access control check that compares the `user_id` from the URL to the `authenticated_user_id`. The application raises an HTTP 403 Forbidden error if the IDs don’t match. This error prevents unauthorized users from accessing other user information, fixing the IDOR vulnerability.


Detecting and fixing security issues with Snyk
----------------------------------------------


[Snyk](https://snyk.io/) is a security solution with software composition analysis (SCA) and static application security testing (SAST) capabilities. Let’s use Snyk to help us find and fix security issues in our FastAPI application. 


### Installing and authenticating Snyk CLI


To get started, install the Snyk CLI on your machine, following the installation guide in the [documentation](https://docs.snyk.io/snyk-cli/install-the-snyk-cli).


After installing the CLI, you need to authenticate your Snyk account. Run the `snyk auth` command in the terminal, which will open a browser window where you can sign in with your GitHub or Google account.


### Scanning dependencies for vulnerabilities


To scan the project for vulnerabilities in the third-party libraries, we need a `requirements.txt` file. Create `requirements.txt` with the following command:



```
pip freeze > requirements.txt
```

Next, scan your project dependencies for vulnerabilities by executing the following command in the terminal:



```
snyk test --file=requirements.txt --skip-unresolved
```

This command analyzes your dependencies, then identifies and describes vulnerabilities, detailing their severity and giving remediation guidance. For instance, you might discover a medium-severity [denial of service (DoS)](https://security.snyk.io/vuln/SNYK-PYTHON-STARLETTE-3319937) vulnerability in `starlette@0.14.2`.


**Note**: Snyk can’t scan the project if a required package is missing. The `–skip-unresolved` flag overrides this behavior.


### Fixing vulnerabilities with Snyk


Snyk can also help us fix vulnerabilities. The `snyk fix` command can automatically solve some detected issues. Snyk will upgrade or patch the affected dependencies to secure versions, keeping applications up-to-date and secure.


### Using Snyk Code for static code analysis


Snyk also has a powerful static application security testing (SAST) solution. The `snyk code` command can analyze your application code, identify security issues, and provide detailed information on how to fix them.


To use `snyk code`, run the following command in your terminal:



```
snyk code test
```

Snyk Code will perform a deep analysis of your codebase, highlighting any security vulnerabilities present. It also offers remediation advice for these issues, helping you maintain a secure and reliable application.


IDOR vulnerabilities explained
------------------------------


As developers, we must ensure our applications are secure. IDORs threaten web applications, so we must learn how to find and fix them. With a good understanding of IDOR vulnerabilities, you can build Python applications that are both performant and secure. 


In this tutorial, you learned how to find and patch IDOR vulnerabilities in a Python application. We also examined how Snyk can help you identify and rectify security risks in your project dependencies and the application code itself. Snyk is a powerful security tool and integrating it into your development routine provides an extra layer of armor to keep your code sturdy.


Take a closer look at what Snyk has under the hood with a [free trial](https://app.snyk.io/login?cta=quick-start&loc=pricing-open-source-free&page=pricing).