django-access-review

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<!-- Reference material based on OWASP Cheat Sheet Series (CC BY-SA 4.0) https://cheatsheetseries.owasp.org/ -->
<!-- 参考资料基于OWASP Cheat Sheet Series (CC BY-SA 4.0) https://cheatsheetseries.owasp.org/ -->

Django Access Control & IDOR Review

Django访问控制与IDOR审查

Find access control vulnerabilities by investigating how the codebase answers one question:
Can User A access, modify, or delete User B's data?
通过调查代码库如何回答以下问题来找出访问控制漏洞:
用户A能否访问、修改或删除用户B的数据?

Philosophy: Investigation Over Pattern Matching

核心理念:重视调查而非模式匹配

Do NOT scan for predefined vulnerable patterns. Instead:
  1. Understand how authorization works in THIS codebase
  2. Ask questions about specific data flows
  3. Trace code to find where (or if) access checks happen
  4. Report only what you've confirmed through investigation
Every codebase implements authorization differently. Your job is to understand this specific implementation, then find gaps.

不要扫描预定义的漏洞模式,而是:
  1. 理解此代码库中授权的实现方式
  2. 提出问题,针对特定数据流进行分析
  3. 追踪代码,找出访问检查的位置(或确认是否存在检查)
  4. 仅报告通过调查确认的问题
每个代码库的授权实现方式都不同。你的工作是理解当前代码的具体实现,然后找出其中的漏洞。

Phase 1: Understand the Authorization Model

阶段1:理解授权模型

Before looking for bugs, answer these questions about the codebase:
在寻找漏洞之前,先回答关于代码库的以下问题:

How is authorization enforced?

授权是如何实施的?

Research the codebase to find:
□ Where are permission checks implemented?
  - Decorators? (@login_required, @permission_required, custom?)
  - Middleware? (TenantMiddleware, AuthorizationMiddleware?)
  - Base classes? (BaseAPIView, TenantScopedViewSet?)
  - Permission classes? (DRF permission_classes?)
  - Custom mixins? (OwnershipMixin, TenantMixin?)

□ How are queries scoped?
  - Custom managers? (TenantManager, UserScopedManager?)
  - get_queryset() overrides?
  - Middleware that sets query context?

□ What's the ownership model?
  - Single user ownership? (document.owner_id)
  - Organization/tenant ownership? (document.organization_id)
  - Hierarchical? (org -> team -> user -> resource)
  - Role-based within context? (org admin vs member)
研究代码库以找到:
□ 权限检查在哪里实现?
  - 装饰器?(@login_required, @permission_required, 自定义装饰器?)
  - 中间件?(TenantMiddleware, AuthorizationMiddleware?)
  - 基类?(BaseAPIView, TenantScopedViewSet?)
  - 权限类?(DRF permission_classes?)
  - 自定义混入类?(OwnershipMixin, TenantMixin?)

□ 查询是如何限定范围的?
  - 自定义管理器?(TenantManager, UserScopedManager?)
  - 重写get_queryset()?
  - 设置查询上下文的中间件?

□ 所有权模型是什么?
  - 单用户所有权?(document.owner_id)
  - 组织/租户所有权?(document.organization_id)
  - 层级式?(组织 → 团队 → 用户 → 资源)
  - 上下文内的基于角色的模型?(组织管理员 vs 成员)

Investigation commands

调查命令

bash
undefined
bash
undefined

Find how auth is typically done

查找通常的授权实现方式

grep -rn "permission_classes|@login_required|@permission_required" --include="*.py" | head -20
grep -rn "permission_classes|@login_required|@permission_required" --include="*.py" | head -20

Find base classes that views inherit from

查找视图继承的基类

grep -rn "class Base.View|class.Mixin.:" --include=".py" | head -20
grep -rn "class Base.View|class.Mixin.:" --include=".py" | head -20

Find custom managers

查找自定义管理器

grep -rn "class.Manager|def get_queryset" --include=".py" | head -20
grep -rn "class.Manager|def get_queryset" --include=".py" | head -20

Find ownership fields on models

查找模型中的所有权字段

grep -rn "owner|user_id|organization|tenant" --include="models.py" | head -30

**Do not proceed until you understand the authorization model.**

---
grep -rn "owner|user_id|organization|tenant" --include="models.py" | head -30

**在理解授权模型之前,不要继续下一步。**

---

Phase 2: Map the Attack Surface

阶段2:梳理攻击面

Identify endpoints that handle user-specific data:
识别处理用户特定数据的端点:

What resources exist?

存在哪些资源?

□ What models contain user data?
□ Which have ownership fields (owner_id, user_id, organization_id)?
□ Which are accessed via ID in URLs or request bodies?
□ 哪些模型包含用户数据?
□ 哪些模型有所有权字段(owner_id, user_id, organization_id)?
□ 哪些资源通过URL或请求体中的ID进行访问?

What operations are exposed?

暴露了哪些操作?

For each resource, map:
  • List endpoints - what data is returned?
  • Detail/retrieve endpoints - how is the object fetched?
  • Create endpoints - who sets the owner?
  • Update endpoints - can users modify others' data?
  • Delete endpoints - can users delete others' data?
  • Custom actions - what do they access?

针对每个资源,梳理:
  • 列表端点 - 返回哪些数据?
  • 详情/获取端点 - 对象是如何获取的?
  • 创建端点 - 谁设置所有者?
  • 更新端点 - 用户能否修改他人的数据?
  • 删除端点 - 用户能否删除他人的数据?
  • 自定义操作 - 它们访问哪些资源?

Phase 3: Ask Questions and Investigate

阶段3:提出问题并展开调查

For each endpoint that handles user data, ask:
针对每个处理用户数据的端点,提出:

The Core Question

核心问题

"If I'm User A and I know the ID of User B's resource, can I access it?"
Trace the code to answer this:
1. Where does the resource ID enter the system?
   - URL path: /api/documents/{id}/
   - Query param: ?document_id=123
   - Request body: {"document_id": 123}

2. Where is that ID used to fetch data?
   - Find the ORM query or database call

3. Between (1) and (2), what checks exist?
   - Is the query scoped to current user?
   - Is there an explicit ownership check?
   - Is there a permission check on the object?
   - Does a base class or mixin enforce access?

4. If you can't find a check, is there one you missed?
   - Check parent classes
   - Check middleware
   - Check managers
   - Check decorators at URL level
"如果我是用户A,且知道用户B的资源ID,我能否访问该资源?"
追踪代码以回答此问题:
1. 资源ID是如何进入系统的?
   - URL路径:/api/documents/{id}/
   - 查询参数:?document_id=123
   - 请求体:{"document_id": 123}

2. 该ID用于何处获取数据?
   - 找到ORM查询或数据库调用

3. 在(1)和(2)之间,存在哪些检查?
   - 查询是否限定为当前用户?
   - 是否有明确的所有权检查?
   - 是否对对象有权限检查?
   - 基类或混入类是否强制实施访问控制?

4. 如果找不到检查,是否有遗漏的检查?
   - 检查父类
   - 检查中间件
   - 检查管理器
   - 检查URL级别的装饰器

Follow-Up Questions

后续问题

□ For list endpoints: Does the query filter to user's data, or return everything?

□ For create endpoints: Who sets the owner - the server or the request?

□ For bulk operations: Are they scoped to user's data?

□ For related resources: If I can access a document, can I access its comments?
  What if the document belongs to someone else?

□ For tenant/org resources: Can User in Org A access Org B's data by changing
  the org_id in the URL?

□ 对于列表端点:查询是否过滤到用户的数据,还是返回所有数据?

□ 对于创建端点:谁设置所有者 - 服务器还是请求?

□ 对于批量操作:它们是否限定为用户的数据?

□ 对于关联资源:如果我能访问某个文档,能否访问其评论?
  如果该文档属于他人呢?

□ 对于租户/组织资源:组织A的用户能否通过修改URL中的org_id来访问组织B的数据?

Phase 4: Trace Specific Flows

阶段4:追踪具体流程

Pick a concrete endpoint and trace it completely.
选择一个具体的端点并完整追踪其流程。

Example Investigation

调查示例

Endpoint: GET /api/documents/{pk}/

1. Find the view handling this URL
   → DocumentViewSet.retrieve() in api/views.py

2. Check what DocumentViewSet inherits from
   → class DocumentViewSet(viewsets.ModelViewSet)
   → No custom base class with authorization

3. Check permission_classes
   → permission_classes = [IsAuthenticated]
   → Only checks login, not ownership

4. Check get_queryset()
   → def get_queryset(self):
   →     return Document.objects.all()
   → Returns ALL documents!

5. Check for has_object_permission()
   → Not implemented

6. Check retrieve() method
   → Uses default, which calls get_object()
   → get_object() uses get_queryset(), which returns all

7. Conclusion: IDOR - Any authenticated user can access any document
端点:GET /api/documents/{pk}/

1. 找到处理此URL的视图
   → api/views.py中的DocumentViewSet.retrieve()

2. 检查DocumentViewSet继承自哪些类
   → class DocumentViewSet(viewsets.ModelViewSet)
   → 没有带授权的自定义基类

3. 检查permission_classes
   → permission_classes = [IsAuthenticated]
   → 仅检查登录状态,不检查所有权

4. 检查get_queryset()
   → def get_queryset(self):
   →     return Document.objects.all()
   → 返回所有文档!

5. 检查has_object_permission()
   → 未实现

6. 检查retrieve()方法
   → 使用默认实现,调用get_object()
   → get_object()使用get_queryset(),后者返回所有文档

7. 结论:IDOR漏洞 - 任何已认证用户都可以访问任何文档

What to look for when tracing

追踪时需要关注的点

Potential gap indicators (investigate further, don't auto-flag):
- get_queryset() returns .all() or filters without user
- Direct Model.objects.get(pk=pk) without ownership in query
- ID comes from request body for sensitive operations
- Permission class checks auth but not ownership
- No has_object_permission() and queryset isn't scoped

Likely safe patterns (but verify the implementation):
- get_queryset() filters by request.user or user's org
- Custom permission class with has_object_permission()
- Base class that enforces scoping
- Manager that auto-filters

潜在漏洞指标(需进一步调查,不要自动标记):
- get_queryset()返回.all()或未按用户过滤
- 直接使用Model.objects.get(pk=pk)且查询中未包含所有权条件
- 敏感操作的ID来自请求体
- 权限类仅检查认证状态,不检查所有权
- 未实现has_object_permission()且查询集未限定范围

相对安全的模式(但需验证实现):
- get_queryset()按request.user或用户所属组织过滤
- 自定义权限类实现了has_object_permission()
- 基类强制实施范围限定
- 管理器自动过滤

Phase 5: Report Findings

阶段5:报告调查结果

Only report issues you've confirmed through investigation.
仅报告通过调查确认的问题。

Confidence Levels

置信度等级

LevelMeaningAction
HIGHTraced the flow, confirmed no check existsReport with evidence
MEDIUMCheck may exist but couldn't confirmNote for manual verification
LOWTheoretical, likely mitigatedDo not report
等级含义操作
已追踪完整流程,确认无检查机制附上证据进行报告
可能存在检查机制但无法确认标注为需手动验证
理论上的漏洞,可能已被缓解不进行报告

Suggested Fixes Must Enforce, Not Document

建议的修复方案必须强制实施,而非仅文档说明

Bad fix: Adding a comment saying "caller must validate permissions" Good fix: Adding code that actually validates permissions
A comment or docstring does not enforce authorization. Your suggested fix must include actual code that:
  • Validates the user has permission before proceeding
  • Raises an exception or returns an error if unauthorized
  • Makes unauthorized access impossible, not just discouraged
Example of a BAD fix suggestion:
python
def get_resource(resource_id):
    # IMPORTANT: Caller must ensure user has access to this resource
    return Resource.objects.get(pk=resource_id)
Example of a GOOD fix suggestion:
python
def get_resource(resource_id, user):
    resource = Resource.objects.get(pk=resource_id)
    if resource.owner_id != user.id:
        raise PermissionDenied("Access denied")
    return resource
If you can't determine the right enforcement mechanism, say so - but never suggest documentation as the fix.
不良修复:添加注释说明“调用者必须验证权限” 良好修复:添加实际验证权限的代码
注释或文档字符串无法强制实施授权。你的修复建议必须包含实际代码,以:
  • 在执行前验证用户是否拥有权限
  • 若未授权则抛出异常或返回错误
  • 确保未授权访问完全无法进行,而非仅不鼓励
不良修复示例:
python
def get_resource(resource_id):
    # 重要提示:调用者必须确保用户拥有访问此资源的权限
    return Resource.objects.get(pk=resource_id)
良好修复示例:
python
def get_resource(resource_id, user):
    resource = Resource.objects.get(pk=resource_id)
    if resource.owner_id != user.id:
        raise PermissionDenied("Access denied")
    return resource
如果你无法确定正确的强制实施机制,请说明这一点,但绝不要建议仅通过文档来修复。

Report Format

报告格式

markdown
undefined
markdown
undefined

Access Control Review: [Component]

访问控制审查:[组件名称]

Authorization Model

授权模型

[Brief description of how this codebase handles authorization]
[此代码库中授权实现方式的简要说明]

Findings

调查结果

[IDOR-001] [Title] (Severity: High/Medium)

[IDOR-001] [标题] (严重程度:高/中)

  • Location:
    path/to/file.py:123
  • Confidence: High - confirmed through code tracing
  • The Question: Can User A access User B's documents?
  • Investigation:
    1. Traced GET /api/documents/{pk}/ to DocumentViewSet
    2. Checked get_queryset() - returns Document.objects.all()
    3. Checked permission_classes - only IsAuthenticated
    4. Checked for has_object_permission() - not implemented
    5. Verified no relevant middleware or base class checks
  • Evidence: [Code snippet showing the gap]
  • Impact: Any authenticated user can read any document by ID
  • Suggested Fix: [Code that enforces authorization - NOT a comment]
  • 位置
    path/to/file.py:123
  • 置信度:高 - 通过代码追踪确认
  • 问题:用户A能否访问用户B的文档?
  • 调查过程
    1. 追踪GET /api/documents/{pk}/到DocumentViewSet
    2. 检查get_queryset() - 返回Document.objects.all()
    3. 检查permission_classes - 仅包含IsAuthenticated
    4. 检查has_object_permission() - 未实现
    5. 验证无相关中间件或基类检查
  • 证据:[显示漏洞的代码片段]
  • 影响:任何已认证用户都可以通过ID读取任何文档
  • 建议修复:[强制实施授权的代码 - 而非注释]

Needs Manual Verification

需手动验证的问题

[Issues where authorization exists but couldn't confirm effectiveness]
[存在授权机制但无法确认有效性的问题]

Areas Not Reviewed

未审查的领域

[Endpoints or flows not covered in this review]

---
[本次审查未覆盖的端点或流程]

---

Common Django Authorization Patterns

常见Django授权模式

These are patterns you might find - not a checklist to match against.
这些是你可能会遇到的模式 - 而非用于匹配的检查清单。

Query Scoping

查询范围限定

python
undefined
python
undefined

Scoped to user

限定为当前用户

Document.objects.filter(owner=request.user)
Document.objects.filter(owner=request.user)

Scoped to organization

限定为所属组织

Document.objects.filter(organization=request.user.organization)
Document.objects.filter(organization=request.user.organization)

Using a custom manager

使用自定义管理器

Document.objects.for_user(request.user) # Investigate what this does
undefined
Document.objects.for_user(request.user) # 调查此方法的实现
undefined

Permission Enforcement

权限实施

python
undefined
python
undefined

DRF permission classes

DRF权限类

permission_classes = [IsAuthenticated, IsOwner]
permission_classes = [IsAuthenticated, IsOwner]

Custom has_object_permission

自定义has_object_permission

def has_object_permission(self, request, view, obj): return obj.owner == request.user
def has_object_permission(self, request, view, obj): return obj.owner == request.user

Django decorators

Django装饰器

@permission_required('app.view_document')
@permission_required('app.view_document')

Manual checks

手动检查

if document.owner != request.user: raise PermissionDenied()
undefined
if document.owner != request.user: raise PermissionDenied()
undefined

Ownership Assignment

所有权分配

python
undefined
python
undefined

Server-side (safe)

服务器端设置(安全)

def perform_create(self, serializer): serializer.save(owner=self.request.user)
def perform_create(self, serializer): serializer.save(owner=self.request.user)

From request (investigate)

从请求中获取(需调查)

serializer.save(**request.data) # Does request.data include owner?

---
serializer.save(**request.data) # 请求体中是否包含owner字段?

---

Investigation Checklist

调查检查清单

Use this to guide your review, not as a pass/fail checklist:
□ I understand how authorization is typically implemented in this codebase
□ I've identified the ownership model (user, org, tenant, etc.)
□ I've mapped the key endpoints that handle user data
□ For each sensitive endpoint, I've traced the flow and asked:
  - Where does the ID come from?
  - Where is data fetched?
  - What checks exist between input and data access?
□ I've verified my findings by checking parent classes and middleware
□ I've only reported issues I've confirmed through investigation
使用此清单指导审查,但不要将其作为通过/失败的检查标准:
□ 我已理解此代码库中通常的授权实现方式
□ 我已明确所有权模型(用户、组织、租户等)
□ 我已梳理处理用户数据的关键端点
□ 对于每个敏感端点,我已追踪流程并提出以下问题:
  - ID来自何处?
  - 数据在何处获取?
  - 输入和数据访问之间存在哪些检查?
□ 我已通过检查父类和中间件验证调查结果
□ 我仅报告了通过调查确认的问题