first commit

This commit is contained in:
Anurag Jangid 2026-10-08 02:52:58 +05:30
commit 34ea15a54d
246 changed files with 18660 additions and 0 deletions

194
docs/SECURITY.md Normal file
View file

@ -0,0 +1,194 @@
# Security — Desi Clans
**Last reviewed:** 2026-04-04
This document describes security assumptions, controls, and operational requirements for the Desi Clans monorepo (FastAPI API + React SPA).
---
## Threat model (summary)
| Asset | Primary risks |
|-------|----------------|
| User accounts | Credential stuffing, session theft |
| Admin panel | Privilege abuse, catalog tampering |
| Orders / payments | Price tampering, replay, webhook forgery |
| Digital tickets | QR leakage, double entry, scanner abuse |
| Media uploads | Malware, path traversal (legacy paths only) |
**Out of scope for v1:** DDoS at scale (use CDN/WAF in production), PCI DSS Level 1 (Razorpay.js handles card data).
---
## Authentication & JWT
### Attendees (`/api/auth/*`)
- Passwords hashed with **bcrypt** (passlib). Plain passwords never stored or returned.
- JWT **HS256**, claim `type: "user"`, `sub` = numeric user id.
- Register/login: Pydantic validation (email normalized, password **≥ 8** characters).
- **Rate limit:** 20 login / 10 register requests per IP per minute (in-process).
### Admins (`/api/admin/auth/*`)
- Separate JWT claim `type: "admin"`; attendee tokens **cannot** call admin routes (tested).
- Roles: `editor`, `super_admin` — all admin API routes currently require `editor` or above.
- **Rate limit:** 20 login attempts per IP per minute.
### Frontend token storage
- Attendee token: `localStorage` (`desiclans_access_token`).
- Admin token: separate key (`desiclans_admin_token`).
- **Assumption:** XSS on the marketing or admin origin equals token compromise. Mitigate with CSP, dependency hygiene, and short TTL if needed.
### Production configuration
- `SECRET_KEY` ≥ 32 chars, not on blocklist (`config.py`).
- `ACCESS_TOKEN_EXPIRE_MINUTES` bounded 5–20160.
---
## Authorization
| Endpoint class | Enforcement |
|----------------|-------------|
| Public catalog | No auth |
| Orders, tickets (owner) | `CurrentUser` + `user_id` match on orders/tickets |
| Gate verify | `X-Check-In-Key` (+ rate limit) |
| Admin CRUD | `EditorAdmin` JWT |
| Admin ticket verify | **Editor JWT + same gate key** as public verify |
**QR reuse:** `verify_ticket_at_gate` locks ticket row (`SELECT … FOR UPDATE`), sets `used` once; second scan returns `valid: false` (HTTP 200).
**Order manipulation:** Clients send `event_id` + line items only; **prices and totals** come from `TicketType` rows under row lock. Ticket types must belong to the event.
**Payment verify:** User must own order; `provider_order_id` must match order; signature verified via Razorpay/mock HMAC; amount/currency compared to `order.total_paise`.
---
## Payments & webhooks
| Provider | Use |
|----------|-----|
| `mock` | Local dev / CI only |
| `razorpay` | Production |
- **`POST /api/webhooks/mock`** returns **404** unless `ENVIRONMENT=development`.
- Razorpay webhooks: signature verified (`RAZORPAY_WEBHOOK_SECRET`).
- Webhook events stored with **idempotency** on `provider_event_id`.
- `POST …/payment/mock-complete` only when `development` + `mock` provider.
**Production startup validation** (`Settings.production_hardening`):
- `PAYMENT_PROVIDER=razorpay` with Razorpay secrets set.
- `TICKET_VERIFY_API_KEY` ≥ 16 characters.
- `ADMIN_PASSWORD` not default.
- `CORS_ORIGINS` non-empty.
---
## CORS
- Explicit origin list from `CORS_ORIGINS` (no `*`).
- `allow_credentials=True` — **do not** add untrusted origins in production.
---
## SQL injection
- Business logic uses **SQLAlchemy ORM** / parameterized queries.
- Raw SQL limited to health check `SELECT 1` and Alembic migrations.
---
## Input validation
- Request bodies use **Pydantic** schemas (`schemas/*`).
- Ticket verify: `qr_token` length bounds.
- Order lines: quantity 1–20, max 20 lines.
---
## File uploads
- **Active `/api` router:** no upload endpoints.
- Legacy `app/routers/admin.py` (not mounted): extension whitelist, 8MB cap, PIL re-encode to WebP.
- **Assumption:** Keep legacy routers unmounted or remove before exposing uploads on production API.
---
## Secrets & environment
| Variable | Notes |
|----------|--------|
| `SECRET_KEY` | JWT signing; rotate with forced re-login |
| `PAYMENT_MOCK_SECRET` | Dev only |
| `RAZORPAY_*` | Production payments |
| `TICKET_VERIFY_API_KEY` | Gate scanners; constant-time compare |
| `DATABASE_URL` | Never commit; least-privilege DB user |
See `backend/.env.example` and `frontend/.env.example`.
---
## API error leakage
- Production: `debug=False`, OpenAPI disabled, generic 500 body.
- `AppError` returns intentional `detail` strings only.
- Development may expose exception text on 500.
---
## Rate limiting
In-process sliding window (`app/core/rate_limit.py`) on:
- Auth login/register
- Admin login
- Ticket verify
- Payment verify
**Limitation:** Per-process only; use reverse-proxy or WAF limits for multi-instance deployments.
---
## QR codes
- Opaque `qr_token` (url-safe random); payload is JSON `{"v":1,"token":…}` without embedded HMAC.
- Security relies on **HTTPS**, token secrecy, and server-side verify.
- Ticket detail (`GET /api/tickets/{id}`) owner-only; includes QR image for valid paid orders.
---
## Frontend (checkout)
- Razorpay **key id** only in browser; secrets stay on server.
- No payment card data touches Desi Clans backend.
---
## Scanner / admin verify UI
- Staff UI calls `POST /api/tickets/verify` with optional `VITE_TICKET_VERIFY_API_KEY` → `X-Check-In-Key` header.
- In development, if `TICKET_VERIFY_API_KEY` is unset, verify is open (local convenience only).
---
## Checklist before production
1. Set strong `SECRET_KEY`, `TICKET_VERIFY_API_KEY`, Razorpay secrets, non-default `ADMIN_PASSWORD`.
2. `ENVIRONMENT=production`, `PAYMENT_PROVIDER=razorpay`.
3. Tight `CORS_ORIGINS` to real site origin(s).
4. TLS everywhere; HSTS at edge.
5. Do not mount legacy `app/routers/*` without security review.
6. Enable edge rate limiting and monitoring on auth/webhook paths.
---
## Related tests
- `tests/test_security.py` — JWT types, bcrypt
- `tests/test_security_hardening.py` — production settings, mock webhook
- `tests/test_payments.py` — verify + webhook idempotency
- `tests/test_digital_tickets.py` — QR reuse
- `tests/test_admin_api.py` — attendee blocked from admin