213 lines
7.5 KiB
Markdown
213 lines
7.5 KiB
Markdown
# LexMind Security Audit Report
|
|
**Date:** 2026-02-01
|
|
**Auditor:** Automated Security Audit
|
|
**Status:** ✅ COMPLETED
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
Comprehensive security audit of the LexMind application (Next.js 16 + Prisma + PostgreSQL + NextAuth + Stripe). Found and fixed **12 vulnerabilities** across 7 categories. Zero npm vulnerabilities remain.
|
|
|
|
---
|
|
|
|
## 1. NPM Vulnerabilities
|
|
**Status:** ✅ FIXED
|
|
|
|
| Before | After |
|
|
|--------|-------|
|
|
| 21 high severity | 0 vulnerabilities |
|
|
|
|
- **Root cause:** `fast-xml-parser` 5.2.5 (via AWS SDK) had RangeError DoS bug
|
|
- **Fix:** Added npm override for `fast-xml-parser@5.3.4` in package.json
|
|
|
|
---
|
|
|
|
## 2. SQL Injection / Prisma
|
|
**Status:** ✅ CLEAN
|
|
|
|
- No `$queryRaw` or `$executeRaw` usage found
|
|
- All database access uses Prisma's parameterized queries
|
|
- No raw SQL anywhere in the codebase
|
|
|
|
---
|
|
|
|
## 3. XSS (Cross-Site Scripting)
|
|
**Status:** ✅ CLEAN
|
|
|
|
- No `dangerouslySetInnerHTML` or `innerHTML` usage found
|
|
- React's default escaping protects against XSS
|
|
- Added Content-Security-Policy header (see Section 9)
|
|
|
|
---
|
|
|
|
## 4. CSRF Protection
|
|
**Status:** ✅ VERIFIED
|
|
|
|
- NextAuth CSRF tokens working correctly
|
|
- Cookies use `__Host-` prefix with `HttpOnly; Secure; SameSite=Lax`
|
|
- All mutating API routes require authenticated session
|
|
|
|
---
|
|
|
|
## 5. Authentication & Authorization
|
|
**Status:** ✅ FIXED (2 critical issues)
|
|
|
|
### 🔴 CRITICAL: Unauthenticated Checkout Route
|
|
- **File:** `/api/checkout/route.ts`
|
|
- **Issue:** No `getServerSession` check — anyone could create Stripe checkout sessions
|
|
- **Fix:** Added authentication check, uses session email instead of user-provided email
|
|
|
|
### 🔴 CRITICAL: Unauthenticated DOCX Export Route
|
|
- **File:** `/api/export/docx/route.ts`
|
|
- **Issue:** No authentication — anyone could generate DOCX documents
|
|
- **Fix:** Added `getServerSession` check
|
|
|
|
### Auth Coverage (all routes verified):
|
|
| Route | Auth | IDOR Protected |
|
|
|-------|------|----------------|
|
|
| /api/admin/stats | ✅ ADMIN check | N/A |
|
|
| /api/analise-processo | ✅ session.user.id | ✅ userId filter |
|
|
| /api/analise-processo/[id] | ✅ session.user.id | ✅ userId filter |
|
|
| /api/auditoria | ✅ session.user.id | ✅ userId filter |
|
|
| /api/auditoria/[id] | ✅ session.user.id | ✅ userId filter |
|
|
| /api/chat | ✅ session.user.id | ✅ userId filter |
|
|
| /api/chat/[chatId] | ✅ session.user.id | ✅ userId filter |
|
|
| /api/checkout | ✅ **FIXED** | N/A |
|
|
| /api/documents | ✅ session.user.id | ✅ userId filter |
|
|
| /api/documents/[id] | ✅ session.user.id | ✅ userId filter |
|
|
| /api/documents/generate | ✅ session.user.id | N/A |
|
|
| /api/export/docx | ✅ **FIXED** | N/A |
|
|
| /api/jurisprudencia | ✅ session.user.id | N/A (public data) |
|
|
| /api/jurisprudencia/search | ✅ session.user.id | N/A (public data) |
|
|
| /api/keys | ✅ session.user.id | ✅ userId filter |
|
|
| /api/keys/[id] | ✅ session.user.id | ✅ userId filter |
|
|
| /api/prazos | ✅ session.user.id | ✅ userId filter |
|
|
| /api/prazos/[id] | ✅ session.user.id | ✅ userId filter |
|
|
| /api/register | N/A (public) | N/A |
|
|
| /api/stripe/checkout | ✅ session.user | ✅ |
|
|
| /api/stripe/portal | ✅ session.user | ✅ |
|
|
| /api/stripe/webhook | N/A (Stripe sig) | ✅ signature verified |
|
|
| /api/templates | ✅ session.user.id | ✅ userId filter |
|
|
| /api/uploads | ✅ session.user.id | ✅ userId filter |
|
|
| /api/uploads/[id] | ✅ session.user.id | ✅ userId filter |
|
|
|
|
---
|
|
|
|
## 6. Rate Limiting
|
|
**Status:** ✅ VERIFIED
|
|
|
|
Nginx rate limiting active:
|
|
- Auth routes: 5 req/min (`zone=auth`)
|
|
- API routes: 20 req/sec (`zone=api`)
|
|
- General: 30 req/sec (`zone=general`)
|
|
- Connection limit: 20 per IP (`conn_limit`)
|
|
- Scanner/bot blocking via User-Agent filter
|
|
|
|
---
|
|
|
|
## 7. Input Validation
|
|
**Status:** ✅ FIXED (5 improvements)
|
|
|
|
- **Created:** `src/lib/validate.ts` with sanitization utilities
|
|
- **Register route:** Added input length limits for all fields
|
|
- **Chat route:** Added 10,000 char message limit
|
|
- **Auditoria route:** Added title (500) and content (100,000) limits
|
|
- **Prazos route:** Added title (500) and description (5,000) limits
|
|
- **Pagination:** Bounded page/limit params in uploads and jurisprudencia routes
|
|
- **Uploads:** Added server-side file extension validation (defense in depth)
|
|
|
|
---
|
|
|
|
## 8. Sensitive Data Exposure
|
|
**Status:** ✅ FIXED
|
|
|
|
- **Created `.gitignore`** — `.env` was not being excluded (no `.gitignore` existed!)
|
|
- **Stripe error messages:** Stopped leaking `error.message` to client in checkout/portal routes
|
|
- **API responses:** Verified no password hashes or internal IDs are exposed
|
|
- **API keys:** Properly hashed (SHA-256), only shown once on creation, masked in listings
|
|
- **NEXT_PUBLIC vars:** Only publishable Stripe key and app URL (safe)
|
|
- **Error handling:** All routes return generic error messages, details logged server-side
|
|
|
|
---
|
|
|
|
## 9. Security Headers
|
|
**Status:** ✅ FIXED (2 new headers added)
|
|
|
|
### Headers now active:
|
|
| Header | Value | Status |
|
|
|--------|-------|--------|
|
|
| X-Frame-Options | SAMEORIGIN | ✅ existing |
|
|
| X-Content-Type-Options | nosniff | ✅ existing |
|
|
| X-XSS-Protection | 1; mode=block | ✅ existing |
|
|
| Referrer-Policy | strict-origin-when-cross-origin | ✅ existing |
|
|
| Strict-Transport-Security | max-age=31536000; includeSubDomains | ✅ existing |
|
|
| Content-Security-Policy | Full CSP policy | ✅ **ADDED** |
|
|
| Permissions-Policy | camera=(), microphone=(), geolocation=() | ✅ **ADDED** |
|
|
| server_tokens | off | ✅ existing |
|
|
|
|
---
|
|
|
|
## 10. Database Security
|
|
**Status:** ✅ VERIFIED
|
|
|
|
- PostgreSQL listens only on localhost (default, `listen_addresses = 'localhost'`)
|
|
- `pg_hba.conf` uses `scram-sha-256` for TCP connections
|
|
- Local connections use `peer` authentication
|
|
- No remote access configured
|
|
|
|
---
|
|
|
|
## 11. File Upload Security
|
|
**Status:** ✅ VERIFIED + IMPROVED
|
|
|
|
- MIME type whitelist: PDF, DOC, DOCX, TXT only
|
|
- **Added:** File extension validation (defense in depth)
|
|
- Max size: 50MB (enforced both in app and nginx `client_max_body_size`)
|
|
- Storage limits per plan (1GB-20GB)
|
|
- File paths sanitized via `buildKey()` — strips all special chars
|
|
- No path traversal possible (`../` becomes `___`)
|
|
- Files stored as `private` ACL on DigitalOcean Spaces
|
|
- Access via signed URLs (15 min expiry)
|
|
|
|
---
|
|
|
|
## 12. Session Security
|
|
**Status:** ✅ IMPROVED
|
|
|
|
- Cookies: `__Host-` prefix, `HttpOnly`, `Secure`, `SameSite=Lax`
|
|
- **Reduced session maxAge from 30 days to 7 days** (more appropriate for legal app)
|
|
- JWT strategy with strong NEXTAUTH_SECRET (44 chars, base64)
|
|
- CSRF token verified on all auth requests
|
|
|
|
---
|
|
|
|
## 13. Other Fixes
|
|
|
|
### Duplicate Webhook Route Removed
|
|
- **Deleted:** `/api/webhook/stripe/route.ts` (incomplete, only logged events, no DB updates)
|
|
- **Active:** `/api/stripe/webhook/route.ts` (fully functional with DB updates)
|
|
|
|
### Next.js Middleware Added
|
|
- **Created:** `src/middleware.ts` — adds security headers at application level as backup
|
|
|
|
---
|
|
|
|
## 14. Remaining Notes (Low Risk)
|
|
|
|
| Item | Risk | Notes |
|
|
|------|------|-------|
|
|
| `typescript: { ignoreBuildErrors: true }` in next.config | Low | Could hide type errors; recommend fixing eventually |
|
|
| AWS SDK DoS vuln was in XML parsing | Info | Fixed via override, but only exploitable if attacker controls S3 responses |
|
|
| File upload MIME check trusts client header | Low | Mitigated by extension whitelist + private storage |
|
|
| No email verification on registration | Medium | Users can register with unverified emails |
|
|
|
|
---
|
|
|
|
## Deployment Status
|
|
- ✅ All fixes applied
|
|
- ✅ `npm audit`: 0 vulnerabilities
|
|
- ✅ Build successful
|
|
- ✅ PM2 restarted
|
|
- ✅ Nginx reloaded with new headers
|
|
- ✅ Application verified working
|