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:
- Understand how authorization works in THIS codebase
- Ask questions about specific data flows
- Trace code to find where (or if) access checks happen
- Report only what you've confirmed through investigation
Every codebase implements authorization differently. Your job is to understand this specific implementation, then find gaps.
不要扫描预定义的漏洞模式,而是:
- 理解此代码库中授权的实现方式
- 提出问题,针对特定数据流进行分析
- 追踪代码,找出访问检查的位置(或确认是否存在检查)
- 仅报告通过调查确认的问题
每个代码库的授权实现方式都不同。你的工作是理解当前代码的具体实现,然后找出其中的漏洞。
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
undefinedbash
undefinedFind 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
置信度等级
| Level | Meaning | Action |
|---|---|---|
| HIGH | Traced the flow, confirmed no check exists | Report with evidence |
| MEDIUM | Check may exist but couldn't confirm | Note for manual verification |
| LOW | Theoretical, likely mitigated | Do 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 resourceIf 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
undefinedmarkdown
undefinedAccess 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:
- Traced GET /api/documents/{pk}/ to DocumentViewSet
- Checked get_queryset() - returns Document.objects.all()
- Checked permission_classes - only IsAuthenticated
- Checked for has_object_permission() - not implemented
- 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的文档?
- 调查过程:
- 追踪GET /api/documents/{pk}/到DocumentViewSet
- 检查get_queryset() - 返回Document.objects.all()
- 检查permission_classes - 仅包含IsAuthenticated
- 检查has_object_permission() - 未实现
- 验证无相关中间件或基类检查
- 证据:[显示漏洞的代码片段]
- 影响:任何已认证用户都可以通过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
undefinedpython
undefinedScoped 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
undefinedDocument.objects.for_user(request.user) # 调查此方法的实现
undefinedPermission Enforcement
权限实施
python
undefinedpython
undefinedDRF 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()
undefinedif document.owner != request.user:
raise PermissionDenied()
undefinedOwnership Assignment
所有权分配
python
undefinedpython
undefinedServer-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来自何处?
- 数据在何处获取?
- 输入和数据访问之间存在哪些检查?
□ 我已通过检查父类和中间件验证调查结果
□ 我仅报告了通过调查确认的问题