mcpField
D
django-access-review

django-access-review

github.com/getsentryセキュリティ1.1k2.7k 回インストール5 日前に更新
リポジトリ

django-access-review について

django-access-review は、Django のアクセス制御と IDOR リスクを調べるセキュリティレビュー向けスキルです。認可モデルを把握して機密データの流れを追跡し、確認できた不備だけを報告します。

主な機能

  1. 01認可メカニズムの整理
  2. 02ユーザーデータを扱うエンドポイントと操作の特定
  3. 03入力からデータアクセスまでの ID の追跡
  4. 04所有権と権限チェックの確認
  5. 05実際に強制できる修正案の提示

活用シーン

  1. 01API エンドポイントのユーザー間データアクセスの確認
  2. 02一覧・詳細ビューの所有権スコープの確認
  3. 03一括操作や関連リソースの認可の追跡

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/getsentry/skills --skill django-access-review

インストール後、Claude Code で/django-access-reviewを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
django-orm.md
django-views.md
drf-permissions.md
tenant-isolation.md

詳細

Django のアクセス制御と IDOR のレビュー

コードベースが次の問いにどう答えるかを調査し、アクセス制御の脆弱性を見つけます。

ユーザー A はユーザー B のデータにアクセスしたり、変更したり、削除したりできますか?

基本方針:パターン照合ではなく調査

既知の脆弱なパターンを探すためにスキャンしてはいけません。代わりに、次の手順で進めます。

  1. 理解する:このコードベースで認可がどのように機能するかを把握します
  2. 質問する:具体的なデータフローについて質問します
  3. コードを追跡する:アクセスチェックがどこで行われているか(または行われていないか)を確認します
  4. 報告する:調査によって確認した内容のみを報告します

認可の実装方法はコードベースごとに異なります。この特定の実装を理解したうえで、不備を見つけることがあなたの役目です。


フェーズ 1:認可モデルを理解する

バグを探す前に、コードベースについて次の質問に答えます。

認可はどのように強制されていますか?

コードベースを調査して、次の情報を探します。

□ 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)
調査コマンド
# Find how auth is typically done
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

# Find custom managers
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

認可モデルを理解するまでは先に進まないでください。


フェーズ 2:攻撃対象領域を把握する

ユーザー固有のデータを扱うエンドポイントを特定します。

どのようなリソースがありますか?
□ 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?
どのような操作が公開されていますか?

各リソースについて、次の項目を整理します。

  • 一覧エンドポイント:どのデータが返されますか?
  • 詳細/取得エンドポイント:オブジェクトはどのように取得されますか?
  • 作成エンドポイント:所有者は誰が設定しますか?
  • 更新エンドポイント:ユーザーは他のユーザーのデータを変更できますか?
  • 削除エンドポイント:ユーザーは他のユーザーのデータを削除できますか?
  • カスタムアクション:何にアクセスしますか?

フェーズ 3:質問して調査する

ユーザーデータを扱う各エンドポイントについて、次の質問をします。

中核となる質問

「私がユーザー A で、ユーザー B のリソースの ID を知っている場合、そのリソースにアクセスできますか?」

コードを追跡して答えを確認します。

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
追加の質問
□ 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?

フェーズ 4:具体的なフローを追跡する

具体的なエンドポイントを選び、最初から最後まで追跡します。

調査例
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
追跡時に確認すること
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

フェーズ 5:調査結果を報告する

調査によって確認した問題のみを報告します。

信頼度
レベル意味対応
HIGHフローを追跡し、チェックが存在しないことを確認した根拠を示して報告する
MEDIUMチェックが存在する可能性はあるが、確認できなかった手動確認が必要な事項として記載する
LOW理論上の問題であり、すでに対策されている可能性が高い報告しない
修正案では、文書化ではなく実際に強制する

悪い修正案:「呼び出し元で権限を検証する必要がある」というコメントを追加する 良い修正案:権限を実際に検証するコードを追加する

コメントや docstring は認可を強制しません。修正案には、次の処理を実際に行うコードを含めてください。

  • 処理を続行する前に、ユーザーに権限があることを検証する
  • 権限がない場合に例外を発生させるか、エラーを返す
  • 不正アクセスを単に推奨しないだけでなく、実際に不可能にする

悪い修正案の例:

def get_resource(resource_id):
    # IMPORTANT: Caller must ensure user has access to this resource
    return Resource.objects.get(pk=resource_id)

良い修正案の例:

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

適切な強制方法を判断できない場合は、その旨を伝えてください。ただし、修正案として文書化を提案してはいけません。

報告形式
## Access Control Review: [Component]

### Authorization Model
[Brief description of how this codebase handles authorization]

### Findings

#### [IDOR-001] [Title] (Severity: High/Medium)
- **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]

### Needs Manual Verification
[Issues where authorization exists but couldn't confirm effectiveness]

### Areas Not Reviewed
[Endpoints or flows not covered in this review]

Django でよく使われる認可パターン

ここに示すのは、見つかる可能性のあるパターンであり、照合すべきチェックリストではありません。

クエリのスコープ設定
# Scoped to user
Document.objects.filter(owner=request.user)

# Scoped to organization
Document.objects.filter(organization=request.user.organization)

# Using a custom manager
Document.objects.for_user(request.user)  # Investigate what this does
権限の強制
# DRF permission classes
permission_classes = [IsAuthenticated, IsOwner]

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

# Django decorators
@permission_required('app.view_document')

# Manual checks
if document.owner != request.user:
    raise PermissionDenied()
所有権の割り当て
# Server-side (safe)
def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

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

調査チェックリスト

合否判定のチェックリストとしてではなく、レビューの指針として使用してください。

□ 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

コメント