agentsclimarketplace

Django security

Skill hlsitechio/claude-skills-security/appsec-stack-pack/django-security

Defensive security audit skills for Claude — tech-stack-keyed and audit-domain-keyed packs for SaaS apps.

Install
npx -y skills add hlsitechio/claude-skills-security --skill django-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Security audit for Django applications including settings.py (SECRET_KEY, DEBUG, ALLOWED_HOSTS), middleware order, ORM raw queries, template autoescape bypass, CSRF protection, Django Admin exposure, authentication backends, file upload handling, and Django-specific patterns. Use this skill whenever the user mentions Django, settings.py, manage.py, Django ORM, Django REST Framework, DRF, makemigrations, urls.py, views.py, or asks "audit my Django app", "Django security review", "Django settings safe". Trigger when the codebase contains `django` in `requirements.txt` / `pyproject.toml`, or `manage.py`, `settings.py`, `urls.py` files.

SKILL.md

8.4 KB, as published. Nobody here has run it

Django Security Audit

Audit Django applications including DRF (Django REST Framework) APIs.

When this skill applies

  • Reviewing Django settings.py files
  • Auditing views, models, and templates
  • Reviewing DRF serializers and viewsets
  • Checking authentication and permission classes
  • Auditing Django Admin exposure

Workflow

Follow ../_shared/audit-workflow.md.

Phase 1: Stack detection

grep -E '^Django|django' requirements.txt pyproject.toml 2>/dev/null
find . -name 'manage.py' -not -path '*/.venv/*'
find . -name 'settings.py' -o -name 'settings/*.py' 2>/dev/null
python -c "import django; print(django.get_version())" 2>/dev/null

Phase 2: Inventory

# Settings files
find . -name 'settings*.py' -not -path '*/.venv/*' -not -path '*/node_modules/*'

# Views
find . -name 'views.py' -o -name 'views/' -type d 2>/dev/null

# URL configurations
find . -name 'urls.py' 2>/dev/null

# Raw SQL usage
grep -rn '\.raw(\|cursor()\|connection\.cursor' . --include='*.py' 2>/dev/null

# Template autoescape disabling
grep -rn '|safe\|autoescape off\|mark_safe\|format_html' . --include='*.py' --include='*.html' 2>/dev/null

Phase 3: Detection — the checks

settings.py audit

  • DJG-SET-1 SECRET_KEY from environment variable, not committed:
    SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
    
    Audit: git log -p settings.py | grep -i secret_key — if any historical commit has a real secret, rotate.
  • DJG-SET-2 DEBUG = False in production. Confirmed via env-based settings split:
    DEBUG = os.environ.get('DJANGO_DEBUG', '0') == '1'
    
  • DJG-SET-3 ALLOWED_HOSTS not ['*'] in production. Specific hostnames listed.
  • DJG-SET-4 SECURE_* settings configured:
    • SECURE_SSL_REDIRECT = True
    • SESSION_COOKIE_SECURE = True
    • CSRF_COOKIE_SECURE = True
    • SECURE_HSTS_SECONDS = 31536000 (or more)
    • SECURE_HSTS_INCLUDE_SUBDOMAINS = True
    • SECURE_HSTS_PRELOAD = True (if you want preload list inclusion)
    • SECURE_CONTENT_TYPE_NOSNIFF = True
    • SECURE_BROWSER_XSS_FILTER — removed in Django 4 (XSS-Protection deprecated); use CSP instead
    • X_FRAME_OPTIONS = 'DENY' (or 'SAMEORIGIN')
  • DJG-SET-5 CSRF_TRUSTED_ORIGINS lists specific origins (not *).
  • DJG-SET-6 DATABASES config uses env vars for credentials.
  • DJG-SET-7 EMAIL_* config doesn't have plaintext credentials.
  • DJG-SET-8 MIDDLEWARE order:
    MIDDLEWARE = [
      'django.middleware.security.SecurityMiddleware',          # 1st
      'django.contrib.sessions.middleware.SessionMiddleware',
      'corsheaders.middleware.CorsMiddleware',                  # before CommonMiddleware
      'django.middleware.common.CommonMiddleware',
      'django.middleware.csrf.CsrfViewMiddleware',
      'django.contrib.auth.middleware.AuthenticationMiddleware',
      'django.contrib.messages.middleware.MessageMiddleware',
      'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    

CSRF

  • DJG-CSRF-1 CsrfViewMiddleware in MIDDLEWARE.
  • DJG-CSRF-2 No @csrf_exempt on state-changing views unless explicitly needed and documented (webhooks with their own signature verification are OK).
  • DJG-CSRF-3 DRF views using SessionAuthentication automatically apply CSRF. Token/JWT auth doesn't (and shouldn't, for non-cookie auth).

ORM and raw SQL

  • DJG-ORM-1 .raw(sql, params) uses parameterized queries (the params list). String concatenation is SQL injection.
    # BAD
    User.objects.raw(f"SELECT * FROM auth_user WHERE id = {user_id}")
    
    # GOOD
    User.objects.raw("SELECT * FROM auth_user WHERE id = %s", [user_id])
    
  • DJG-ORM-2 connection.cursor() queries use parameterized queries.
  • DJG-ORM-3 extra(where=[...]) with user input is injection-prone; prefer Q() filters.
  • DJG-ORM-4 objects.filter(...) with field lookups is safe; the ORM parameterizes.

Mass assignment

  • DJG-MA-1 Model.objects.create(**request.POST) patterns flagged. Use forms / serializers with explicit field allowlists.
  • DJG-MA-2 DRF ModelSerializer with fields = '__all__' is mass-assignable. Use explicit fields = ['id', 'name', ...].
  • DJG-MA-3 Form Meta.fields and Meta.exclude reviewed.

Templates

  • DJG-TPL-1 Django templates auto-escape by default. {{ var|safe }} filter and {% autoescape off %} disable it — review every occurrence.
  • DJG-TPL-2 mark_safe(...) and format_html(...) in Python code mark strings as safe — confirm content is trusted.
  • DJG-TPL-3 SSTI: don't render user input as a template (Template(user_input).render(...)).

Authentication

  • DJG-AUTH-1 AUTH_PASSWORD_VALIDATORS configured with reasonable validators.
  • DJG-AUTH-2 PASSWORD_HASHERS first entry is Argon2 or BCrypt (Django default Argon2 is good).
  • DJG-AUTH-3 Custom backends (subclasses of BaseBackend) review authenticate() for timing attacks.
  • DJG-AUTH-4 django.contrib.auth.password_validation.validate_password called before set_password.

DRF (Django REST Framework)

  • DJG-DRF-1 Global DEFAULT_PERMISSION_CLASSES set to IsAuthenticated (or stricter); not AllowAny.
  • DJG-DRF-2 Per-view permission classes set; no view relying solely on URL routing for auth.
  • DJG-DRF-3 ViewSet.queryset filtered to tenant/user scope, not returning all objects.
  • DJG-DRF-4 Serializers define fields explicitly, exclude sensitive (password, is_superuser, user_permissions if not needed).
  • DJG-DRF-5 Throttling configured (DEFAULT_THROTTLE_RATES).
# REST_FRAMEWORK settings.py snippet
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'],
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.UserRateThrottle',
        'rest_framework.throttling.AnonRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'user': '1000/hour',
        'anon': '100/hour',
    },
}

File uploads

  • DJG-UP-1 FILE_UPLOAD_MAX_MEMORY_SIZE and DATA_UPLOAD_MAX_MEMORY_SIZE set.
  • DJG-UP-2 FILE_UPLOAD_PERMISSIONS restrictive (0o644).
  • DJG-UP-3 MEDIA_ROOT not within web-served path that could expose uploaded files without auth.
  • DJG-UP-4 Uploaded file validated by content (magic bytes), not just content_type.

Django Admin

  • DJG-ADM-1 Admin reachable only by staff users (is_staff=True).
  • DJG-ADM-2 Admin URL changed from /admin/ to a less-discoverable path (defense in depth).
  • DJG-ADM-3 Admin behind VPN / IP allowlist for production sites with regular user traffic.
  • DJG-ADM-4 Custom admin actions reviewed for unintended privilege.

Static and media files

  • DJG-STA-1 DEBUG = False means Django doesn't serve static files; deployed via nginx / S3 / CloudFront — confirm headers set there.
  • DJG-STA-2 collectstatic output doesn't include accidentally-committed sensitive files.

Logging

  • DJG-LOG-1 Logging config doesn't include 'level': 'DEBUG' for production. Don't log request bodies containing PII.
  • DJG-LOG-2 Django's default email-error-to-admins (when DEBUG=False) sends tracebacks — verify recipients are correct, no broad mailing lists.

Dependencies

  • DJG-DEP-1 Django version is on a current Long Term Support (LTS) line (4.2 LTS, 5.2 LTS) or current main. Old versions unpatched.
  • DJG-DEP-2 pip list --outdated reviewed. safety check or pip-audit run periodically.

Phase 4: Triage

Critical: DEBUG = True in production; ALLOWED_HOSTS = ['*']; raw query with string concatenation; admin publicly accessible with weak password policy.

Phase 5: Report

Use ../_shared/findings-schema.md. Prefix IDs with DJG-.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.