Null Pointer Dereference in Fastapi with Basic Auth
Null Pointer Dereference in Fastapi with Basic Auth
A null pointer dereference in a FastAPI application using HTTP Basic Authentication occurs when code attempts to access a property or method on an object that is None. This can happen when authentication parsing or user extraction yields no valid user, and the response path does not guard against a missing principal before use.
Consider a FastAPI route that relies on a Basic Auth helper to retrieve a current_user object. If the credentials are missing, malformed, or fail verification, the helper may return None. Accessing attributes such as current_user.id without a None check will trigger an AttributeError at runtime, which an unauthenticated scanner may interpret as an unstable endpoint or information leak, contributing to a higher security risk score in categories like Authentication and Input Validation.
Example of a vulnerable pattern:
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
def get_current_user(credentials: HTTPBasicCredentials = Depends(security)):
# In a real app, this would validate against a database or identity provider
if credentials.username == 'alice' and credentials.password == 'secret':
return {'username': credentials.username}
# Returns None when credentials are invalid
return None
@app.get('/profile')
def read_profile(current_user: dict = Depends(get_current_user)):
# Vulnerable: no None check before attribute access
username = current_user['username']
return {'message': f'Hello, {username}'}
If an attacker sends a request without credentials or with wrong credentials, get_current_user returns None. The line username = current_user['username'] then raises a TypeError (or AttributeError depending on runtime types), which may surface as a 500 error. Such behavior can expose stack traces or internal logic, increasing the Data Exposure and Authentication findings in a scan.
In the context of OpenAPI/Swagger spec analysis, a null pointer dereference may not be evident in the spec if the developer does not document the 401/403 or error paths. A scanner that cross-references spec definitions with runtime behavior can flag mismatches when endpoints produce unexpected error responses for missing authentication, indicating potential stability or information disclosure issues.
Remediation focuses on explicit None checks and consistent error handling, which also aligns with the OWASP API Top 10 category '2023-A1: Broken Object Level Authorization' when missing authorization leads to unexpected crashes or data exposure.
Basic Auth-Specific Remediation in Fastapi
To prevent null pointer dereference in FastAPI with Basic Auth, ensure every code path that uses the authenticated user explicitly handles the None case. Use HTTPException to return stable, secure error responses, and avoid exposing internal details to the client.
Secure example with proper validation and error handling:
from fastapi import FastAPI, Depends, HTTPException, status, Security
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
def get_current_user(credentials: HTTPBasicCredentials = Security(security)):
if credentials.username == 'alice' and credentials.password == 'secret':
return {'username': credentials.username}
return None
@app.get('/profile')
def read_profile(current_user: dict = Depends(get_current_user)):
if current_user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail='Invalid authentication credentials',
headers={'WWW-Authenticate': 'Basic'}
)
# Safe: current_user is guaranteed non-None here
username = current_user['username']
return {'message': f'Hello, {username}'}
This pattern ensures that missing or invalid credentials result in a 401 response with the WWW-Authenticate header, preventing runtime attribute access on None. It also reduces the likelihood of Authentication and BOLA/IDOR findings by making authorization boundaries explicit.
For more complex scenarios, consider using a dependency that raises HTTPException directly on failure, or use a user model with an id field that is validated before use:
from fastapi import Depends
from pydantic import BaseModel
class User(BaseModel):
username: str
def get_current_user_or_403(credentials: HTTPBasicCredentials = Security(security)):
if credentials.username == 'alice' and credentials.password == 'secret':
return User(username=credentials.username)
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='Not enough permissions')
@app.get('/items/')
def list_items(current_user: User = Depends(get_current_user_or_403)):
# current_user is always a User instance
return {'owner': current_user.username, 'items': []}
Using the CLI tool (middlebrick scan