django-security
Review and harden security in the AS v2 Django/DRF backend. Use when auditing RBAC access control, reviewing auth/OAuth/import code for vulnerabilities, or hardening an endpoint before deploy. Covers OWASP Top 10, IDOR, CSV injection, row-level scoping, and secrets — mapped to AS v2's session auth, capability RBAC, and Google OAuth.
- License 许可
- Apache-2.0
- 最近更新
- 2026-11-11
- Stars
- ★ 0
- Forks
- 0
01 前言 Front Matter
- name
- django-security
- description
- Review and harden security in the AS v2 Django/DRF backend. Use when auditing RBAC access control, reviewing auth/OAuth/import code for vulnerabilities, or hardening an endpoint before deploy. Covers OWASP Top 10, IDOR, CSV injection, row-level scoping, and secrets — mapped to AS v2's session auth, capability RBAC, and Google OAuth.
02 资源文件 3
- 参考文档reference/rbac-idor.md3.8 KB
- 参考文档references/detailed-guide.md4.1 KB
- 参考文档references/technical.md3.2 KB
03 SKILL.md 正文
Django Security — AS v2
Audit Procedure (RBAC access control)
Run in order; the audit is DONE only when every step passes.
- Lint passes — deterministic pre-pass:
cd v2/backend && python scripts/rbac_lint.py apps/(exit 1 = finding). It bansuser.groups.filter(name=...)andIs<Role>classes outside the whitelist. Same job as CI[required] backend rbac-lint. It is this skill's only scanner: IDOR and CSV injection were measured as scanners and failed (0% precision); prove what the lint cannot decide with a test. - Every ViewSet has explicit
permission_classes— enumerate the ViewSets touched; each must declareHasPerm("codename")(or a composition), never rely on the DRF default alone. Ifget_permissions()is overridden, confirm it does not silently drop an@action(permission_classes=...)decorator. get_queryset()enforces data-scope — non-privileged users see only their own rows (filter(usuario=request.user)); the full queryset is gated behind a capability check viauser_has_any_perm(...). Noobjects.all()leak. The IDOR bug lives in the scope's content (branch order, gerência filter, writable owner field). Prove the scope with a test in two halves; a bare 403 proves nothing, since the capability gate returns it whatever the queryset does:- Positive control — user A, holding the capability, hits an object in A's own scope with the same verb → 2xx (200; 204 on DELETE).
- Out of scope — the same user A does GET/PATCH/DELETE on user B's object → 404 (the scope lives in the queryset, so B's object does not exist for A). Expect 403 only where the design is "exists but you may not act on it".
- Secrets/config — no hardcoded keys; production flags set (see A05);
.gitignoreblocks the secret files (see Secrets Management). Leave secret scanning to CI[required] Secret Detection(gitleaks + TruffleHog) and GitHub push protection; no local secret scanner run.
DONE = steps 1–4 all pass for the code under review.
OWASP Top 10 — AS v2 Specific
A01: Broken Access Control
RBAC model, capability codenames, and the canonical ViewSet pattern that gates by capability then scopes the queryset: see reference/rbac-idor.md. That file is the single source for the IDOR / row-level pattern — link to it, do not re-derive it.
Audit-specific checks (beyond the Audit Procedure):
- [ ] Bulk approve/reject actions verify the capability per item, not once for the batch
- [ ]
select_for_update()on approval flows to prevent race conditions - [ ] No IDOR: a user cannot read
/solicitacoes/{id}/outside their scope - [ ] Frontend permission checks mirror backend (never trust client-side only)
A02: Cryptographic Failures
Our crypto surface:
GCAL_ENCRYPTION_KEY— Fernet encryption for OAuth tokensSECRET_KEY— Django session signingREDIS_PASSWORD— Redis auth in production
Checklist:
- [ ] OAuth tokens encrypted at rest with Fernet (
GoogleOAuthCredential.access_token_encrypted/refresh_token_encrypted) - [ ] Key rotation tested via the
rotate_gcal_encryption_keymanagement command - [ ]
SECRET_KEYfrom environment variable, never hardcoded - [ ] Sessions use HttpOnly + Secure + SameSite=Lax cookies
- [ ] No sensitive data in URL query parameters (use POST body)
A03: Injection
SQL injection vectors in Django:
# DANGEROUS — never do this
Solicitacao.objects.raw(f"SELECT * FROM core_solicitacao WHERE id = {user_input}")
Solicitacao.objects.extra(where=[f"status = '{request.GET['status']}'"])
# SAFE — parameterized queries
Solicitacao.objects.filter(id=user_input)
Solicitacao.objects.raw("SELECT * FROM core_solicitacao WHERE id = %s", [user_input])
CSV injection in exports — use the SSOT helper, do not reimplement:
# DANGEROUS — user-controlled data in CSV (a value like =CMD() executes in Excel)
writer.writerow([solicitacao.observacoes])
# SAFE — SEC-007 helper prefixes formula chars (= + - @ TAB CR LF) with a single quote
from apps.core.utils.csv_sanitize import sanitize_csv_value
writer.writerow([sanitize_csv_value(r.municipio.nome), r.aluno_qtde])
Sanitize free text only: the helper turns numbers into text (-5 → '-5).
A new export = helper + an injection test on the endpoint (model:
v2/backend/apps/core/tests/test_csv_export_injection.py).
A04: Insecure Design
Our design patterns:
- Approval flow (PA-01~07) enforced at service layer, not view layer
- Availability rules (RD-01~08) in
availability_service.py, validated server-side - Status transitions via state machine, not arbitrary updates
- AuditLog for all approval/rejection operations
A05: Security Misconfiguration
Production checklist:
- [ ]
DEBUG = False - [ ]
ALLOWED_HOSTSexplicitly set (not['*']) - [ ]
SECURE_SSL_REDIRECT = True - [ ]
SESSION_COOKIE_SECURE = True - [ ]
CSRF_COOKIE_SECURE = True - [ ]
CSRF_COOKIE_HTTPONLY = True - [ ]
X_FRAME_OPTIONS = 'DENY'(anti-clickjacking) - [ ]
SECURE_HSTS_SECONDS = 31536000 - [ ]
INCLUDE_DEV_TOOLS = False(CP-08) - [ ] Container runs as non-root (SEC-006)
- [ ]
read_only: trueon container filesystem - [ ] Debug toolbar, silk, nplusone behind try/except ImportError
A07: Authentication Failures
Our auth model:
- Session-based (Redis backend), NOT JWT for web
- CSRF token via HttpOnly cookie +
/api/csrf/endpoint - Account lockout: 10 attempts → 15 min block
- Rate limiting: login 30/min por IP (o escritório sai por um IP só) + bloqueio de conta (
ACCOUNT_LOCKOUT_THRESHOLD10 erros → 15 min); gcal_write 10/min
Checklist:
- [ ] Login endpoint rate-limited (DRF throttle + Nginx)
- [ ] Failed login attempts logged with IP + user-agent
- [ ] Session cookie:
asv2sid, age 7200s, SameSite=Lax - [ ] CSRF token refreshed after login
- [ ] Logout clears session + CSRF cache
A08: Software and Data Integrity
Our supply chain:
- Docker images signed with Cosign (keyless OIDC)
- SLSA provenance attestations
- Trivy container scanning in CI
- Gitleaks + TruffleHog secret detection
- pip-audit for Python dependencies
- npm audit for frontend dependencies
A09: Logging and Monitoring
Our logging:
- Structured JSON logging with request ID correlation
- AuditLog model for approval/rejection operations
- Login/logout audit with IP + user-agent
- Django-prometheus metrics at
/metrics/
Security events to always log:
- Authentication failures (with IP)
- Authorization failures (403s with user + resource)
- CSRF failures
- Rate limit triggers
- OAuth token operations (create, refresh, revoke)
Secrets Management
Never commit:
.envfiles with real credentialsservice_account.json(Google)GCAL_ENCRYPTION_KEYvaluesSECRET_KEYvalues- Database passwords
.gitignore already blocks (.env* local variants and Google creds):
.env
.env.local
.env.*.local
!.env.*.example
service_account.json
*service_account.json
backend/service_account.json # v2/.gitignore
Verify any new secret file is covered before committing — the ignore list is
explicit, not a blanket .env.*.
04 同仓库更多 Skills
writing-standards
Apply AS v2 technical-writing standards (clarity, brevity, active voice, Python/Django conventions). Use when writing a README, a docstring (PEP 257), a commit message, a PR description, an error message, or any technical doc.
verification-gate
Verify before claiming success. Use when about to say something works/is done, after implementing a feature, after a bugfix, or before pushing — run the proof command and confirm output first.
test-driven-development
Write the failing test before the implementation in AS v2 (pytest + Django/DRF). Use when implementing a feature or fixing a bug, before any production code is written.
subagent-development
Orquestra subagents no AS v2 — decompõe a tarefa em domínios independentes, paraleliza e revisa em 2 estágios. Use ao delegar exploração, implementação ou validação cross-arquivo para acelerar o trabalho e poupar o contexto principal.
security-scan
Automated security scanning for AS v2. Use before commits or when reviewing changes for vulnerabilities. Runs dependency audit (pip-audit, npm audit), secret detection (grep patterns), and code pattern scanning (SQL injection, XSS, eval, pickle, hardcoded secrets). Integrates with project hooks system.
resume-handoff
Resume work from a saved handoff document, validating its state against the current codebase before continuing. Use when resuming work from a saved handoff after a context clear or in a new session (path, session name, or GitHub issue).