| name | django-security-testing |
| description | Security testing playbook for Django applications covering ORM injection, middleware gaps, auth/session flaws, and template issues |
Django Security Testing
penkit51 AI — professional penetration testing skill pack. Authorized testing only.
Deep Exploitation Guide
Django
Security testing for Django web applications and Django REST Framework (DRF) APIs. Focus on ORM/raw query misuse, middleware ordering, permission class gaps, and session/auth configuration across views, admin, and channels.
Attack Surface
Core Components
- URL routing (
urls.py), class-based and function views, middleware stack
- ORM (QuerySet filters), raw SQL,
extra(), RawSQL, annotations
- Templates (Django template language, Jinja2 if configured)
- Forms, ModelForms, serializers (DRF)
Authentication
- Session framework,
AuthenticationMiddleware, @login_required, DRF permission_classes
- Token auth, JWT (djangorestframework-simplejwt), OAuth integrations
- Django admin (
/admin/), staff/superuser flags
Deployment
DEBUG=True exposure, ALLOWED_HOSTS, SECRET_KEY leakage
- Static/media serving, reverse proxies, ASGI (Channels, Daphne, Uvicorn)
High-Value Targets
/admin/ — brute force, credential stuffing, IDOR on admin objects
- API endpoints with mixed permission classes across ViewSets
- File upload (
FileField, ImageField), import/export (django-import-export)
- Search/filter endpoints using
filter(), Q objects, or raw SQL
- Password reset, email verification, invitation tokens
- WebSocket consumers (Django Channels) with weaker auth than HTTP equivalents
- Celery task triggers accepting user IDs without ownership checks
Reconnaissance
Fingerprinting
curl -I https://target/ -H "Cookie: sessionid=test"
# X-Frame-Options, Set-Cookie (sessionid, csrftoken), Server header
GET /admin/login/
GET /api/ /api/v1/ /swagger/ /api/schema/
Settings Leakage (when DEBUG=True or misconfigured)
- Yellow debug page exposes
SECRET_KEY, database credentials, installed apps
/static/, error pages with stack traces revealing paths and ORM queries
OpenAPI / DRF
GET /api/schema/
GET /swagger.json
Map endpoints, authentication classes, and permission classes per route.
Key Vulnerabilities
Authentication & Authorization
Permission Class Gaps
- ViewSet with
list protected but retrieve/update missing permission_classes
- Custom permissions checking authentication but not object ownership (IDOR)
@api_view without explicit permissions inheriting permissive defaults
- Admin actions or custom management commands without staff checks
Session Issues
SESSION_COOKIE_SECURE=False on HTTPS sites; missing HttpOnly
- Session fixation if session key not rotated on login
- Weak or leaked
SECRET_KEY → forge session cookies (django.contrib.sessions.backends.signed_cookies)
JWT (simplejwt)
- RS256→HS256 confusion if algorithm pinning is misconfigured
- Missing
user_id/token blacklist on logout
- Refresh token rotation not enforced
Injection
ORM SQL Injection
Vulnerable patterns (more common in legacy code):
User.objects.raw(f"SELECT * FROM auth_user WHERE username = '{user_input}'")
User.objects.extra(where=[f"username = '{user_input}'"])
Test: ' OR 1=1 --, time-based payloads, database-specific syntax.
DRF Filter Backends
django-filter with unsafe field exposure: ?username__icontains= on unintended columns
- Ordering injection via
?ordering= if field whitelist missing
Template Injection
Django templates auto-escape by default; risk rises with:
mark_safe(user_input)
|safe filter in templates
Template(user_input).render(...)
Jinja2 backend without autoescape: {{7*7}}, RCE gadgets if sandbox misconfigured.
CSRF
@csrf_exempt on state-changing views
- DRF session authentication without CSRF enforcement on unsafe methods
- CSRF cookie not set (
CSRF_USE_SESSIONS, trusted origins misconfiguration)
CSRF_TRUSTED_ORIGINS too broad
Test: Cross-origin POST with victim session cookie; JSON endpoints with session auth.
IDOR and Mass Assignment
DRF Serializers
fields = '__all__' exposing is_staff, is_superuser, role, balance
read_only_fields missing on sensitive ModelSerializer fields
- Nested writes updating foreign keys across tenants
Object-Level Permissions
get_object() without filtering queryset by request.user
- Generic views with
queryset = Model.objects.all() and weak permissions
File Handling
MEDIA_ROOT served directly in DEBUG or via misconfigured nginx
- Path traversal in custom file download views using user-supplied paths
- SVG/HTML uploads served with
Content-Type that enables XSS
- Missing file size/type validation on uploads
SSRF
requests.get(user_url) in webhooks, preview, import features
- Celery tasks fetching user URLs server-side
- Test loopback, metadata IPs, redirect chains
Host Header / Password Reset
ALLOWED_HOSTS = ['*'] or permissive subdomain patterns
- Password reset emails built from
Host header → poisoned reset links
- Cache poisoning via unkeyed Host header on cached pages
Django Admin
- Default
/admin/ path with weak credentials
has_add_permission / has_change_permission overrides with logic bugs
- ModelAdmin exposing sensitive fields in list_display or export
Channels / WebSocket
- Consumer accepts connection without session/auth parity to HTTP
- Group name derived from user input → subscribe to other users' channels
- Missing origin validation on WebSocket handshake
Bypass Techniques
- Content negotiation: JSON vs form data hitting different parser/permission paths
- HTTP method override or trailing slash routing to alternate view
- Parameter pollution: duplicate
id fields in query and body
- Race on state transitions (coupon redemption, inventory) via parallel requests
- Versioned API (
/api/v1/ vs /api/v2/) with weaker auth on older version
Testing Methodology
- Map surface — URLs, DRF schema, admin, static/media paths
- Auth matrix — Unauthenticated/user/staff for each endpoint and method
- Object ownership — Swap IDs across two user accounts on every CRUD route
- Serializer audit — Identify writable sensitive fields and nested relations
- Middleware order — Confirm auth runs before business logic; check CSRF on session APIs
- Channel parity — Same authorization on WebSocket actions as REST equivalents
- Settings review (white-box) — DEBUG, ALLOWED_HOSTS, SECRET_KEY, session/cookie flags
Validation
- Side-by-side requests proving unauthorized access (IDOR, privilege escalation)
- CSRF PoC executing state change with victim session (for session-authenticated endpoints)
- SQLi/template injection with deterministic oracle (error, timing, or
7*7 equivalent)
- Document view/serializer/permission class where enforcement failed
- Show admin or staff capability gained from regular user context if applicable
False Positives
queryset.filter(user=request.user) consistently applied including nested routes
- Object-level permission class correctly validates ownership on all actions
- DEBUG=False and generic error pages with no settings leakage confirmed
- Mark_safe used only on server-generated trusted content
- CSRF correctly enforced on all session-authenticated unsafe methods
Impact
- Account takeover via session forgery or password reset poisoning
- Horizontal/vertical privilege escalation through IDOR and mass assignment
- Data breach via ORM/SQL injection or excessive serializer fields
- Server compromise via SSTI, pickle in cache (if used), or SSRF to internal services
Pro Tips
- DRF ViewSets often protect
list but forget destroy or custom @action routes
- Check
APIView subclasses for missing permission_classes — common oversight
- Test
?format= and browsable API HTML responses for CSRF on session auth
django.contrib.admin uses separate auth — don't assume API auth covers admin
- Compare ASGI WebSocket consumers against REST permissions for the same resource
Tooling
Static analysis is the fastest way to reach the sinks above in white-box scope. The sandbox ships python/pipx, semgrep, bandit, ast-grep, and ripgrep.
- bandit (preinstalled) — Python security linter; flags
mark_safe, extra(), RawSQL, subprocess, weak crypto, hardcoded secrets: bandit -r . -ll
- semgrep (preinstalled) with the Django ruleset — higher-signal than bandit for framework-specific bugs (
.extra(), RawSQL, |safe, csrf_exempt, ALLOWED_HOSTS=['*']): semgrep --config p/django .
- pip-audit (PyPA) — dependency CVE scanner for known-vuln Django/DRF/simplejwt versions:
pipx install pip-audit && pip-audit -r requirements.txt
- ast-grep (preinstalled) — quick structural grep for risky calls without a full SAST run:
ast-grep run -p 'mark_safe($X)' -l python
For the SECRET_KEY → signed-cookie/reset-token forgery path noted under Session Issues, Django's own django.core.signing is the "tool": with a leaked key you can mint valid signing.dumps() values (session cookies, password-reset tokens, and PickleSerializer-backed session RCE).
Summary
Django's defaults help (CSRF middleware, template auto-escape) but DRF, raw SQL, custom permissions, and deployment settings introduce frequent gaps. Test every endpoint with role-separated principals and verify object-level enforcement on querysets, not just authentication presence.
Platform Methodology
Django Security Testing
penkit51 AI — professional penetration testing skill pack. Authorized testing only.
Deep Exploitation Guide
Django
Security testing for Django web applications and Django REST Framework (DRF) APIs. Focus on ORM/raw query misuse, middleware ordering, permission class gaps, and session/auth configuration across views, admin, and channels.
Attack Surface
Core Components
- URL routing (
urls.py), class-based and function views, middleware stack
- ORM (QuerySet filters), raw SQL,
extra(), RawSQL, annotations
- Templates (Django template language, Jinja2 if configured)
- Forms, ModelForms, serializers (DRF)
Authentication
- Session framework,
AuthenticationMiddleware, @login_required, DRF permission_classes
- Token auth, JWT (djangorestframework-simplejwt), OAuth integrations
- Django admin (
/admin/), staff/superuser flags
Deployment
DEBUG=True exposure, ALLOWED_HOSTS, SECRET_KEY leakage
- Static/media serving, reverse proxies, ASGI (Channels, Daphne, Uvicorn)
High-Value Targets
/admin/ — brute force, credential stuffing, IDOR on admin objects
- API endpoints with mixed permission classes across ViewSets
- File upload (
FileField, ImageField), import/export (django-import-export)
- Search/filter endpoints using
filter(), Q objects, or raw SQL
- Password reset, email verification, invitation tokens
- WebSocket consumers (Django Channels) with weaker auth than HTTP equivalents
- Celery task triggers accepting user IDs without ownership checks
Reconnaissance
Fingerprinting
curl -I https://target/ -H "Cookie: sessionid=test"
# X-Frame-Options, Set-Cookie (sessionid, csrftoken), Server header
GET /admin/login/
GET /api/ /api/v1/ /swagger/ /api/schema/
Settings Leakage (when DEBUG=True or misconfigured)
- Yellow debug page exposes
SECRET_KEY, database credentials, installed apps
/static/, error pages with stack traces revealing paths and ORM queries
OpenAPI / DRF
GET /api/schema/
GET /swagger.json
Map endpoints, authentication classes, and permission classes per route.
Key Vulnerabilities
Authentication & Authorization
Permission Class Gaps
- ViewSet with
list protected but retrieve/update missing permission_classes
- Custom permissions checking authentication but not object ownership (IDOR)
@api_view without explicit permissions inheriting permissive defaults
- Admin actions or custom management commands without staff checks
Session Issues
SESSION_COOKIE_SECURE=False on HTTPS sites; missing HttpOnly
- Session fixation if session key not rotated on login
- Weak or leaked
SECRET_KEY → forge session cookies (django.contrib.sessions.backends.signed_cookies)
JWT (simplejwt)
- RS256→HS256 confusion if algorithm pinning is misconfigured
- Missing
user_id/token blacklist on logout
- Refresh token rotation not enforced
Injection
ORM SQL Injection
Vulnerable patterns (more common in legacy code):
User.objects.raw(f"SELECT * FROM auth_user WHERE username = '{user_input}'")
User.objects.extra(where=[f"username = '{user_input}'"])
Test: ' OR 1=1 --, time-based payloads, database-specific syntax.
DRF Filter Backends
django-filter with unsafe field exposure: ?username__icontains= on unintended columns
- Ordering injection via
?ordering= if field whitelist missing
Template Injection
Django templates auto-escape by default; risk rises with:
mark_safe(user_input)
|safe filter in templates
Template(user_input).render(...)
Jinja2 backend without autoescape: {{7*7}}, RCE gadgets if sandbox misconfigured.
CSRF
@csrf_exempt on state-changing views
- DRF session authentication without CSRF enforcement on unsafe methods
- CSRF cookie not set (
CSRF_USE_SESSIONS, trusted origins misconfiguration)
CSRF_TRUSTED_ORIGINS too broad
Test: Cross-origin POST with victim session cookie; JSON endpoints with session auth.
IDOR and Mass Assignment
DRF Serializers
fields = '__all__' exposing is_staff, is_superuser, role, balance
read_only_fields missing on sensitive ModelSerializer fields
- Nested writes updating foreign keys across tenants
Object-Level Permissions
get_object() without filtering queryset by request.user
- Generic views with
queryset = Model.objects.all() and weak permissions
File Handling
MEDIA_ROOT served directly in DEBUG or via misconfigured nginx
- Path traversal in custom file download views using user-supplied paths
- SVG/HTML uploads served with
Content-Type that enables XSS
- Missing file size/type validation on uploads
SSRF
requests.get(user_url) in webhooks, preview, import features
- Celery tasks fetching user URLs server-side
- Test loopback, metadata IPs, redirect chains
Host Header / Password Reset
ALLOWED_HOSTS = ['*'] or permissive subdomain patterns
- Password reset emails built from
Host header → poisoned reset links
- Cache poisoning via unkeyed Host header on cached pages
Django Admin
- Default
/admin/ path with weak credentials
has_add_permission / has_change_permission overrides with logic bugs
- ModelAdmin exposing sensitive fields in list_display or export
Channels / WebSocket
- Consumer accepts connection without session/auth parity to HTTP
- Group name derived from user input → subscribe to other users' channels
- Missing origin validation on WebSocket handshake
Bypass Techniques
- Content negotiation: JSON vs form data hitting different parser/permission paths
- HTTP method override or trailing slash routing to alternate view
- Parameter pollution: duplicate
id fields in query and body
- Race on state transitions (coupon redemption, inventory) via parallel requests
- Versioned API (
/api/v1/ vs /api/v2/) with weaker auth on older version
Testing Methodology
- Map surface — URLs, DRF schema, admin, static/media paths
- Auth matrix — Unauthenticated/user/staff for each endpoint and method
- Object ownership — Swap IDs across two user accounts on every CRUD route
- Serializer audit — Identify writable sensitive fields and nested relations
- Middleware order — Confirm auth runs before business logic; check CSRF on session APIs
- Channel parity — Same authorization on WebSocket actions as REST equivalents
- Settings review (white-box) — DEBUG, ALLOWED_HOSTS, SECRET_KEY, session/cookie flags
Validation
- Side-by-side requests proving unauthorized access (IDOR, privilege escalation)
- CSRF PoC executing state change with victim session (for session-authenticated endpoints)
- SQLi/template injection with deterministic oracle (error, timing, or
7*7 equivalent)
- Document view/serializer/permission class where enforcement failed
- Show admin or staff capability gained from regular user context if applicable
False Positives
queryset.filter(user=request.user) consistently applied including nested routes
- Object-level permission class correctly validates ownership on all actions
- DEBUG=False and generic error pages with no settings leakage confirmed
- Mark_safe used only on server-generated trusted content
- CSRF correctly enforced on all session-authenticated unsafe methods
Impact
- Account takeover via session forgery or password reset poisoning
- Horizontal/vertical privilege escalation through IDOR and mass assignment
- Data breach via ORM/SQL injection or excessive serializer fields
- Server compromise via SSTI, pickle in cache (if used), or SSRF to internal services
Pro Tips
- DRF ViewSets often protect
list but forget destroy or custom @action routes
- Check
APIView subclasses for missing permission_classes — common oversight
- Test
?format= and browsable API HTML responses for CSRF on session auth
django.contrib.admin uses separate auth — don't assume API auth covers admin
- Compare ASGI WebSocket consumers against REST permissions for the same resource
Tooling
Static analysis is the fastest way to reach the sinks above in white-box scope. The sandbox ships python/pipx, semgrep, bandit, ast-grep, and ripgrep.
- bandit (preinstalled) — Python security linter; flags
mark_safe, extra(), RawSQL, subprocess, weak crypto, hardcoded secrets: bandit -r . -ll
- semgrep (preinstalled) with the Django ruleset — higher-signal than bandit for framework-specific bugs (
.extra(), RawSQL, |safe, csrf_exempt, ALLOWED_HOSTS=['*']): semgrep --config p/django .
- pip-audit (PyPA) — dependency CVE scanner for known-vuln Django/DRF/simplejwt versions:
pipx install pip-audit && pip-audit -r requirements.txt
- ast-grep (preinstalled) — quick structural grep for risky calls without a full SAST run:
ast-grep run -p 'mark_safe($X)' -l python
For the SECRET_KEY → signed-cookie/reset-token forgery path noted under Session Issues, Django's own django.core.signing is the "tool": with a leaked key you can mint valid signing.dumps() values (session cookies, password-reset tokens, and PickleSerializer-backed session RCE).
Summary
Django's defaults help (CSRF middleware, template auto-escape) but DRF, raw SQL, custom permissions, and deployment settings introduce frequent gaps. Test every endpoint with role-separated principals and verify object-level enforcement on querysets, not just authentication presence.
Validation & Reporting
- Confirm every finding with reproducible PoC before reporting
- Document: severity (CVSS), affected asset, steps, evidence, remediation
- Use
record_vulnerability when running inside the penkit51 platform
- Chain low-severity findings into higher-impact attack paths
- Never report without evidence — distinguish hypothesis from confirmed vuln
Validation & Reporting
- Confirm every finding with reproducible PoC before reporting
- Document: severity (CVSS), affected asset, steps, evidence, remediation
- Use
record_vulnerability when running inside the penkit51 platform
- Chain low-severity findings into higher-impact attack paths
- Never report without evidence — distinguish hypothesis from confirmed vuln