2

Project Context

Codebase Overview

Total files

1,842

Security-relevant

274
15% of indexed files

Languages

python
typescript
javascript
sql

Frameworks

Django
Django REST Framework
React
Next.js (admin)
Celery

Threat Model

Overview

Application Type

Web payments platform (Django + React SPA)

Architecture

Multi-tenant Django backend serving a React SPA. Orders/payments flow through Stripe; webhooks signed at the edge. Postgres for state, Redis for queues, S3 for receipts.

Authentication

Username + password, JWT-signed sessions, optional TOTP.

Authorization

Per-tenant role (owner/admin/manager/member) + cross-tenant Acme admin.

Threat Actors5

Anonymous web visitor
public

Unauthenticated traffic from the public internet (including bots).

View marketing pages
Initiate signup
Trust
1
Tenant admin
privileged

Customer-organization administrator with org-level write privileges.

Manage org members
Issue refunds
View order history
Trust
4
Tenant member
authenticated

Regular employee account scoped to their tenant.

Create orders
View own org reports
Trust
3
Stripe webhook caller
authenticated

External Stripe service posting payment-state changes.

Notify of payment events with valid signature
Trust
4
Internal admin (Acme staff)
system

Acme staff with cross-tenant admin tooling.

Investigate any tenant
Trigger refunds at platform level
Trust
5

Assets4

Customer PII
critical
data
Postgres `customers`
Owner: Tenant admin
Order ledger
critical
data
Postgres `orders` + `ledger_entries`
Owner: Platform
Webhook signing keys
critical
secret
Secrets manager (and a stale repo copy!)
Owner: Platform
Cardholder tokens
confidential
data
Stripe + cached tokens
Owner: Stripe

Trust Boundaries3

Public internet → Web edge
Public internet
Web edge

TLS-terminating reverse proxy in front of the Django app.

TLS 1.2+
WAF rate-limiting
No login throttle, lets credential stuffing through
Web edge → App tier
Web edge
App tier

gunicorn workers behind nginx; receives proxied requests.

Auth middleware
CSRF token check
Some admin views skip ownership checks (IDOR)
App tier → Postgres
App tier
Postgres

pgbouncer-fronted database access.

Per-tenant queryset filters
Raw SQL builders bypass tenant filter

Attack Vectors4

PriNameActorTarget
P0
Forged Stripe webhookAnonymous web visitorOrder ledger
P0
IDOR on order exportTenant memberOrder ledger
P1
Stored XSS in admin notesTenant memberCustomer PII
P2
Credential stuffing via loginAnonymous web visitorTenant accounts

Entry Points6

GET /
POST /auth/login
POST /auth/signup
POST /api/orders
POST /webhooks/stripe
GET /admin/users/search

Scope

In Scope

  • - payments/
  • - integrations/
  • - admin/

Out of Scope

  • - vendor/
  • - tests/fixtures/

Project Summary

Attack surface

The core attack surface is the Django web tier: login, payment webhooks, admin tooling, and the public order API. The webhook signing path is the highest-impact target — a forged event can mark orders paid. Admin tooling has multiple authorisation gaps (IDOR + XSS), and the login endpoint lacks a rate limit. The codebase also commits a real webhook secret and a stale fixture of real customer data, which broaden the attack surface beyond the live network.

Top risks

  1. CriticalHardcoded Stripe webhook secret in source — forged paid-events possible.
  2. HighSQL injection via `q` parameter on the admin user search endpoint.
  3. HighSSRF in webhook delivery — tenant-controlled URL → internal services.
  4. HighIDOR on `/api/orders/{id}/export` — cross-tenant read trivially enumerable.
  5. MediumDefault JWT signing key honored on staging deploys.

Recommendations

  1. Rotate the Stripe webhook secret and load it from Secrets Manager; add pre-commit secret scanning.
  2. Replace raw SQL composition in admin search with parameterised queries.
  3. Add an SSRF allowlist to webhook delivery (deny RFC1918, link-local, metadata server).
  4. Filter querysets by `request.user.tenant_id` everywhere; add cross-tenant 403 tests.
  5. Fail closed on missing/dev-default JWT secret; rotate the staging secret immediately.
  6. Apply per-account + per-IP rate limits to login and password-reset.
  7. Drop `|safe` on customer-rendered fields; add a CSP that blocks inline scripts.
  8. Default DEBUG=False outside of dev; replace verbose 500 with a generic template.
DEMO