78b57273e2
- Add password hashing with bcrypt - Add AuthService with proper login - Add password strength validation - Add RBAC middleware (AdminOnly, ManagerOrAdmin) - Add tenant isolation middleware - Update CRM handler with tenant filtering - Add JWT fallback for development mode - Add user context helpers - Build successful
700 lines
20 KiB
Markdown
700 lines
20 KiB
Markdown
# BOC Security Readiness Report
|
|
|
|
> **Datum:** 2026-08-10
|
|
> **Auditor:** Bernt (AI Security Audit)
|
|
> **System:** BOC (Business Operations Center)
|
|
> **Scope:** Full stack — backend (Go), frontend (React/Vite), infrastructure, deployment
|
|
|
|
---
|
|
|
|
## EXECUTIVE SUMMARY
|
|
|
|
**SLUTSTATUS: SECURITY NOT READY**
|
|
|
|
Systemet har **flera kritiska säkerhetsbrister** som blockerar produktionsdeplojering. De mest allvarliga är:
|
|
|
|
1. **Secrets i Git-historik** — JWT_SECRET och DB_PASSWORD committade
|
|
2. **Autentisering är bruten** — debug-endpoint genererar admin-tokens, fallback till HS256 med hårdkodad secret
|
|
3. **Ingen auktorisation** — alla autentiserade användare har tillgång till all data (IDOR)
|
|
4. **SQL injection** — strängkonkatenering i journal.go countQuery
|
|
5. **XSS** — dangerouslySetInnerHTML med osaniterat innehåll
|
|
6. **Ingen tenant isolation** — multi-tenancy är ej implementerat trots påstödd support
|
|
7. **CORS tillåter wildcard** — med credentials=true
|
|
|
|
---
|
|
|
|
## 1. SECRETS & CREDENTIALS
|
|
|
|
### 🔴 CRITICAL — .env-fil med secrets i Git
|
|
|
|
**Problem:** `.env` innehåller:
|
|
```
|
|
JWT_SECRET=aamos-…tion
|
|
DB_PASSWORD=boc_secret_2026
|
|
```
|
|
|
|
**Fil:** `boc/.env` (committad i Git, commit af874040c)
|
|
|
|
**Severity:** CRITICAL
|
|
**Attackvektor:** Alla med läsåtkomst till repot har full åtkomst till JWT-signering och databas.
|
|
|
|
**Åtgärd:**
|
|
1. Radera `.env` från Git-historiken (git filter-repo eller BFG Repo-Cleaner)
|
|
2. Rotera JWT_SECRET och DB_PASSWORD omedelbart
|
|
3. Lägg till `.env` i `.gitignore`
|
|
4. Använd miljövariabler injicerade av deployment-plattform
|
|
|
|
**Regression test:**
|
|
```bash
|
|
git log --all --full-history -- .env # ska returnera inget
|
|
```
|
|
|
|
### 🔴 CRITICAL — Hårdkodade credentials i källkod
|
|
|
|
**Problem:** `config/config.go` har hårdkodade fallback-värden:
|
|
```go
|
|
LedgerDBURL: "postgres://wavult_admin:efG15aKjqgu7uotZoAiLTRBtBDMoXITxIe9Hi6EB@platform-identity-core.cvi0qcksmsfj.eu-north-1.rds.amazonaws.com:5432/amos?sslmode=disable"
|
|
JWTSecret: "w+Qkf/CoDda3Ba7vZLKokrGHiwUV5Ak/3tiBmFAvRC8="
|
|
```
|
|
|
|
**Severity:** CRITICAL
|
|
**Attackvektor:** Källkoden är publik (eller kan läcka). Credentials finns i binären.
|
|
|
|
**Åtgärd:**
|
|
1. Ta bort ALLA fallback-värden för secrets
|
|
2. Använd `requireEnv()` för alla secrets
|
|
3. Panic om secret saknas — tvinga explicit konfiguration
|
|
|
|
### 🟡 MEDIUM — Docker Compose exponerar secrets
|
|
|
|
**Problem:** `docker-compose.yml` har:
|
|
```yaml
|
|
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET must be set}
|
|
```
|
|
Men miljövariabler syns i `docker inspect` och process-listor.
|
|
|
|
**Åtgärd:** Använd Docker secrets eller extern secret manager (AWS Secrets Manager, HashiCorp Vault).
|
|
|
|
---
|
|
|
|
## 2. AUTHENTICATION
|
|
|
|
### 🔴 CRITICAL — Debug-endpoint genererar admin-tokens
|
|
|
|
**Problem:** `/debug/token` finns aktivt:
|
|
```go
|
|
if cfg.Port == "9092" || cfg.Port == "9096" {
|
|
r.Get("/debug/token", handlers.DebugTokenHandler(cfg.JWTSecret))
|
|
}
|
|
```
|
|
|
|
**Fil:** `backend/main.go:145-147`
|
|
|
|
**Severity:** CRITICAL
|
|
**Attackvektor:** Vem som helst kan generera en giltig admin-token genom att anropa `/debug/token`.
|
|
|
|
**Åtgärd:**
|
|
1. Ta bort debug-endpoint helt
|
|
2. Om nödvändigt för utveckling — kräv env-var `ENABLE_DEBUG=true` och logga varning
|
|
|
|
### 🔴 CRITICAL — Login-endpoint genererar token utan lösenordskontroll
|
|
|
|
**Problem:** `/api/v1/auth/login`:
|
|
```go
|
|
r.Post("/api/v1/auth/login", func(w http.ResponseWriter, r *http.Request) {
|
|
// ...decode email...
|
|
token, err := jwtService.GenerateToken("3847477b-3d56-4975-9157-ae8f9ce52aa7", req.Email, "admin")
|
|
// Returnerar admin-token för VILKEN EMAIL SOM HELST
|
|
})
|
|
```
|
|
|
|
**Fil:** `backend/main.go:149-175`
|
|
|
|
**Severity:** CRITICAL
|
|
**Attackvektor:** Vem som helst kan logga in som admin med valfri email.
|
|
|
|
**Åtgärd:**
|
|
1. Implementera riktig lösenordsverifiering mot databas
|
|
2. Använd bcrypt.CompareHashAndPassword
|
|
3. Returnera generiskt felmeddelande oavsett om email eller lösenord är fel
|
|
|
|
### 🔴 CRITICAL — JWT fallback till HS256 med hårdkodad secret
|
|
|
|
**Problem:** `middleware/jwt.go`:
|
|
```go
|
|
// Fallback: Tillåt HS256 tokens för utveckling
|
|
jwtSecret := os.Getenv("JWT_SECRET")
|
|
if jwtSecret == "" {
|
|
jwtSecret = "w+Qkf/CoDda3Ba7vZLKokrGHiwUV5Ak/3tiBmFAvRC8="
|
|
}
|
|
```
|
|
|
|
**Severity:** CRITICAL
|
|
**Attackvektor:** Angripare kan signera egna HS256-tokens med den hårdkodade secret.
|
|
|
|
**Åtgärd:**
|
|
1. Ta bort HS256-fallback helt
|
|
2. Kräv RS256 med JWKS från ouroboros-identity
|
|
3. Panic om JWKS_URL saknas i produktion
|
|
|
|
### 🟡 MEDIUM — Token lagras i localStorage
|
|
|
|
**Problem:** Frontend lagrar JWT i localStorage:
|
|
```typescript
|
|
localStorage.setItem('amos_token', token)
|
|
```
|
|
|
|
**Risk:** XSS kan stjäla token. Men eftersom systemet redan har XSS-brister (se §6) är detta förstärkande.
|
|
|
|
**Åtgärd:**
|
|
1. Använd httpOnly cookies för tokens
|
|
2. Implementera CSRF-skydd om cookies används
|
|
3. Eller: Använd token rotation med refresh tokens
|
|
|
|
### 🟡 MEDIUM — Token har för lång livstid
|
|
|
|
**Problem:** Token är giltig i 30 dagar:
|
|
```go
|
|
"exp": now.Add(30 * 24 * time.Hour).Unix()
|
|
```
|
|
|
|
**Åtgärd:** Minska till 15-60 minuter. Implementera refresh tokens.
|
|
|
|
---
|
|
|
|
## 3. AUTHORIZATION / ACCESS CONTROL
|
|
|
|
### 🔴 CRITICAL — Ingen resource-level authorization (IDOR)
|
|
|
|
**Problem:** INGEN handler verifierar att användaren äger resursen. Exempel:
|
|
|
|
**crm.go GetCustomer:**
|
|
```go
|
|
func (h *CRMHandler) GetCustomer(w http.ResponseWriter, r *http.Request) {
|
|
id := chi.URLParam(r, "id")
|
|
// Ingen kontroll av vem som frågar!
|
|
err := h.DB.QueryRow(`SELECT ... FROM boc_customers WHERE id = $1`, id)
|
|
}
|
|
```
|
|
|
|
**hr.go GetEmployee:**
|
|
```go
|
|
func (h *HRHandler) GetEmployee(w http.ResponseWriter, r *http.Request) {
|
|
id := chi.URLParam(r, "id")
|
|
// Ingen kontroll!
|
|
err := h.DB.QueryRow(`SELECT ... FROM boc_employees WHERE id = $1`, id)
|
|
}
|
|
```
|
|
|
|
**Severity:** CRITICAL
|
|
**Attackvektor:** Användare A kan läsa/använda User B:s customers, employees, deals, invoices, etc. genom att bara byta ID.
|
|
|
|
**Åtgärd:**
|
|
1. Lägg till `tenant_id` eller `org_id` på ALLA queries
|
|
2. Verifiera att claims.OrgID matchar resursens tenant
|
|
3. Exempel:
|
|
```go
|
|
claims, _ := middleware.FromContext(r.Context())
|
|
err := h.DB.QueryRow(`SELECT ... FROM boc_customers WHERE id = $1 AND tenant_id = $2`, id, claims.OrgID)
|
|
```
|
|
|
|
### 🔴 CRITICAL — RBAC middleware används inte
|
|
|
|
**Problem:** `middleware/security.go` definierar `RBACMiddleware` men den används INGENSTANS i `main.go`.
|
|
|
|
Alla routes under `r.Use(authMiddleware)` har samma åtkomst för alla autentiserade användare.
|
|
|
|
**Åtgärd:**
|
|
1. Applicera RBAC på alla routes:
|
|
```go
|
|
r.With(middleware.RBACMiddleware(middleware.ResCustomers, middleware.PermRead))
|
|
.Get("/api/v1/crm/customers", crmH.ListCustomers)
|
|
```
|
|
|
|
### 🔴 CRITICAL — Admin-panel saknar admin-verifiering
|
|
|
|
**Problem:** `/api/v1/amos/engines/{id}/restart` och liknande admin-endpoints har ingen roll-kontroll.
|
|
|
|
**Åtgärd:** Lägg till `authService.RequireRole("admin")` på alla admin-endpoints.
|
|
|
|
---
|
|
|
|
## 4. DATABASE SECURITY
|
|
|
|
### 🔴 CRITICAL — SQL injection i journal.go
|
|
|
|
**Problem:** `backend/handlers/journal.go` — countQuery använder strängkonkatenering:
|
|
```go
|
|
countQuery := `SELECT COUNT(*) FROM journal_entries WHERE 1=1`
|
|
if accountFilter != "" {
|
|
countQuery += ` AND EXISTS (
|
|
SELECT 1 FROM journal_lines jl
|
|
JOIN accounts a ON jl.account_id = a.id
|
|
WHERE jl.journal_entry_id = journal_entries.id AND a.code = '` + accountFilter + `'
|
|
)`
|
|
}
|
|
```
|
|
|
|
**Severity:** CRITICAL
|
|
**Attackvektor:** `accountFilter` kan innehålla SQL injection.
|
|
|
|
**Åtgärd:** Använd parameterized queries:
|
|
```go
|
|
countQuery := `SELECT COUNT(*) FROM journal_entries WHERE 1=1`
|
|
if accountFilter != "" {
|
|
countQuery += ` AND EXISTS (
|
|
SELECT 1 FROM journal_lines jl
|
|
JOIN accounts a ON jl.account_id = a.id
|
|
WHERE jl.journal_entry_id = journal_entries.id AND a.code = $1
|
|
)`
|
|
args = append(args, accountFilter)
|
|
}
|
|
```
|
|
|
|
### 🟡 MEDIUM — Ingen RLS (Row Level Security)
|
|
|
|
**Problem:** PostgreSQL RLS är inte aktiverat. Alla queries körs med samma databasanvändare.
|
|
|
|
**Åtgärd:**
|
|
1. Aktivera RLS på alla tabeller:
|
|
```sql
|
|
ALTER TABLE boc_customers ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY tenant_isolation ON boc_customers
|
|
USING (tenant_id = current_setting('app.current_tenant')::UUID);
|
|
```
|
|
|
|
### 🟢 PASS — Parameterized queries används i de flesta fall
|
|
|
|
De flesta handlers använder `$1, $2` etc. korrekt.
|
|
|
|
---
|
|
|
|
## 5. INPUT VALIDATION
|
|
|
|
### 🟡 MEDIUM — Bristfällig input-validering
|
|
|
|
**Problem:** Många handlers validerar bara grundläggande format (email regex) men inte:
|
|
- Maxlängd på strängar
|
|
- Tillåtna värden för enums (status, stage)
|
|
- Numeriska range
|
|
- SQL wildcards i sökparametrar
|
|
|
|
**Exempel:** `crm.go CreateCustomer` validerar inte längd på name, company, etc.
|
|
|
|
**Åtgärd:** Implementera schema-baserad validering med ett bibliotek som `go-playground/validator`.
|
|
|
|
---
|
|
|
|
## 6. XSS / HTML / CONTENT SECURITY
|
|
|
|
### 🔴 CRITICAL — dangerouslySetInnerHTML med osaniterat innehåll
|
|
|
|
**Problem:** `web-v2/src/pages/LegalPage.tsx`:
|
|
```tsx
|
|
<div
|
|
className="text-sm text-text-secondary"
|
|
dangerouslySetInnerHTML={{
|
|
__html: renderPlaceholders(section.content, selectedContract.variables || {})
|
|
}}
|
|
/>
|
|
```
|
|
|
|
`renderPlaceholders` ersätter `{{variable}}` med `<strong>value</strong>` men saniterar INTE `value`.
|
|
|
|
**Severity:** CRITICAL
|
|
**Attackvektor:** Om en contract variable innehåller `<script>alert('xss')</script>` körs det.
|
|
|
|
**Åtgärd:**
|
|
1. Använd DOMPurify innan dangerouslySetInnerHTML
|
|
2. Eller bättre: rendera utan HTML, använd React-komponenter
|
|
|
|
### 🟡 MEDIUM — CSP är för restriktiv men saknar viktiga direktiv
|
|
|
|
**Problem:** `middleware/security.go`:
|
|
```go
|
|
w.Header().Set("Content-Security-Policy", "default-src 'self'")
|
|
```
|
|
|
|
Detta blockerar inline-scripts men frontend använder möjligen inline (vite byggda bundles).
|
|
|
|
**Åtgärd:**
|
|
1. Generera nonce-baserad CSP
|
|
2. Eller använd hash-baserad CSP för kända scripts
|
|
|
|
---
|
|
|
|
## 7. CORS
|
|
|
|
### 🔴 CRITICAL — CORS tillåter wildcard med credentials
|
|
|
|
**Problem:** `middleware/cors.go`:
|
|
```go
|
|
for _, o := range origins {
|
|
if o == "*" || o == origin {
|
|
allowed = true
|
|
break
|
|
}
|
|
}
|
|
if allowed {
|
|
w.Header().Set("Access-Control-Allow-Origin", origin)
|
|
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
|
}
|
|
```
|
|
|
|
Om `CORSOrigins` innehåller `"*"` (vilket är default i config.go: `CORSOrigins: []string{"http://localhost:3000"}` men kan ändras), tillåts wildcard med credentials.
|
|
|
|
**Severity:** CRITICAL
|
|
**Attackvektor:** CSRF-liknande attacker från vilken domän som helst.
|
|
|
|
**Åtgärd:**
|
|
1. Förbjud `"*"` i CORS-konfiguration
|
|
2. Kräv explicita origins
|
|
3. Validera origin strikt
|
|
|
|
---
|
|
|
|
## 8. RATE LIMITING / ABUSE PROTECTION
|
|
|
|
### 🟡 MEDIUM — Rate limiting är för generöst
|
|
|
|
**Problem:** `middleware/security.go`:
|
|
```go
|
|
limiter = rate.NewLimiter(rate.Every(time.Second), 10) // 10 req/s
|
|
```
|
|
|
|
Detta är per IP och gäller alla endpoints. Login har inget separat rate limit.
|
|
|
|
**Åtgärd:**
|
|
1. Separata limits för olika endpoints:
|
|
- Login: 5 försök / 15 minuter
|
|
- API: 100 req / minut
|
|
- Expensive operations: 10 req / minut
|
|
2. Använd Redis-baserad rate limiting för distribuerade deployment
|
|
|
|
---
|
|
|
|
## 9. FILE UPLOAD SECURITY
|
|
|
|
### 🟢 PASS — Ingen filuppladdning hittad
|
|
|
|
Systemet verkar inte ha filuppladdningsfunktionalitet i nuläget.
|
|
|
|
---
|
|
|
|
## 10. IDs & RESOURCE ACCESS
|
|
|
|
### 🔴 CRITICAL — Predictable integer IDs används inte, men UUID skyddar inte
|
|
|
|
**Problem:** Systemet använder UUID (bra) men verifierar INTE ownership (kritiskt).
|
|
|
|
**Åtgärd:** Se §3 — lägg till tenant_id/org_id på alla queries.
|
|
|
|
---
|
|
|
|
## 11. WEBHOOK SECURITY
|
|
|
|
### 🟢 PASS — Inga webhooks hittade
|
|
|
|
Systemet verkar inte ha webhook-funktionalitet i nuläget.
|
|
|
|
---
|
|
|
|
## 12. LOGGING & ERROR HANDLING
|
|
|
|
### 🟡 MEDIUM — Error messages exponerar intern information
|
|
|
|
**Problem:** Vissa handlers returnerar databasfel direkt:
|
|
```go
|
|
writeError(w, http.StatusInternalServerError, "failed to create deal: "+err.Error())
|
|
```
|
|
|
|
**Åtgärd:** Returnera generiska felmeddelanden till klienten, logga detaljer server-side.
|
|
|
|
### 🟡 MEDIUM — Audit log saknar user_id korrekt
|
|
|
|
**Problem:** `middleware/security.go`:
|
|
```go
|
|
if userID, ok := r.Context().Value("user_id").(string); ok {
|
|
event.UserID = userID
|
|
}
|
|
```
|
|
|
|
Men context-nyckeln är `"user"` i auth middleware, inte `"user_id"`.
|
|
|
|
**Åtgärd:** Använd konsekventa context-nycklar.
|
|
|
|
---
|
|
|
|
## 13. PASSWORD SECURITY
|
|
|
|
### 🟢 PASS — bcrypt används
|
|
|
|
`auth.go` använder `bcrypt.CompareHashAndPassword` korrekt.
|
|
|
|
### 🟡 MEDIUM — Ingen password strength policy
|
|
|
|
**Åtgärd:** Implementera minst 8 tecken, blandade case, siffror, specialtecken.
|
|
|
|
---
|
|
|
|
## 14. DEPENDENCIES
|
|
|
|
### 🟡 MEDIUM — Dependencies behöver audit
|
|
|
|
**Kända paket:**
|
|
- `github.com/golang-jwt/jwt/v5` — OK, senaste
|
|
- `golang.org/x/crypto` — OK, senaste
|
|
- `github.com/go-chi/chi/v5` — OK
|
|
- `github.com/prometheus/client_golang` — OK
|
|
|
|
**Åtgärd:** Kör `govulncheck` regelbundet i CI/CD.
|
|
|
|
---
|
|
|
|
## 15. CSRF / SESSION SECURITY
|
|
|
|
### 🔴 CRITICAL — Ingen CSRF-skydd
|
|
|
|
**Problem:** Systemet använder JWT i header (bra för CSRF-resistens) MEN frontend lagrar i localStorage och skickar via fetch.
|
|
|
|
Om systemet byter till cookies (rekommenderat) behövs CSRF-skydd.
|
|
|
|
**Åtgärd:**
|
|
1. Om cookies: implementera Double Submit Cookie eller Synchronizer Token
|
|
2. Om JWT i header: säkerställ att header alltid skickas
|
|
|
|
---
|
|
|
|
## 16. SSRF / SERVER-SIDE REQUESTS
|
|
|
|
### 🟡 MEDIUM — LandvexRealHandler kan vara sårbar för SSRF
|
|
|
|
**Problem:** `backend/handlers/landvex_real.go`:
|
|
```go
|
|
func (h *LandvexRealHandler) fetchFromLandvex(endpoint string) (map[string]interface{}, error) {
|
|
resp, err := h.client.Get(h.baseURL + endpoint)
|
|
```
|
|
|
|
`endpoint` kontrolleras inte. Om detta anropas med användar-kontrollerad input kan det leda till SSRF.
|
|
|
|
**Åtgärd:** Validera endpoint mot allowlist.
|
|
|
|
---
|
|
|
|
## 17. COMMAND / CODE / TEMPLATE INJECTION
|
|
|
|
### 🟢 PASS — Ingen dynamisk kodexekvering hittad
|
|
|
|
---
|
|
|
|
## 18. PATH TRAVERSAL
|
|
|
|
### 🟢 PASS — Ingen filsystemåtkomst med användarkontrollerade paths hittad
|
|
|
|
---
|
|
|
|
## 19. SECURITY HEADERS
|
|
|
|
### 🟡 MEDIUM — Security headers är delvis implementerade
|
|
|
|
**Finns:**
|
|
- X-Content-Type-Options: nosniff
|
|
- X-Frame-Options: DENY
|
|
- X-XSS-Protection: 1; mode=block
|
|
- Referrer-Policy: strict-origin-when-cross-origin
|
|
- CSP: default-src 'self'
|
|
|
|
**Saknas:**
|
|
- Strict-Transport-Security (HSTS)
|
|
- Permissions-Policy
|
|
|
|
**Åtgärd:** Lägg till:
|
|
```go
|
|
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
|
|
w.Header().Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()")
|
|
```
|
|
|
|
---
|
|
|
|
## 20. TRANSPORT SECURITY
|
|
|
|
### 🟡 MEDIUM — Server kör HTTP (inte HTTPS)
|
|
|
|
**Problem:** `main.go`:
|
|
```go
|
|
srv := &http.Server{Addr: ":" + cfg.Port, Handler: r}
|
|
```
|
|
|
|
Ingen TLS-konfiguration.
|
|
|
|
**Åtgärd:**
|
|
1. Terminera TLS vid load balancer (nginx/traefik) ELLER
|
|
2. Konfigurera TLS direkt i Go-servern
|
|
|
|
---
|
|
|
|
## 21. ADMIN SECURITY
|
|
|
|
### 🔴 CRITICAL — Admin-endpoints saknar admin-verifiering
|
|
|
|
**Problem:** `/api/v1/amos/engines/{id}/restart` har ingen roll-kontroll.
|
|
|
|
**Åtgärd:** Lägg till `RequireRole("admin")` middleware.
|
|
|
|
---
|
|
|
|
## 22. MULTI-TENANCY
|
|
|
|
### 🔴 CRITICAL — Tenant isolation är ej implementerat
|
|
|
|
**Problem:**
|
|
1. `tenant_id` finns i modeller men används inte i queries
|
|
2. `crm.go`, `hr.go`, `sales.go` etc. filtrerar inte på tenant
|
|
3. `SwitchTenant` uppdaterar ingen session
|
|
|
|
**Åtgärd:**
|
|
1. Lägg till tenant_id-filter på ALLA databasqueries
|
|
2. Implementera tenant-kontext i middleware
|
|
3. Verifiera att användaren har tillgång till tenant
|
|
|
|
---
|
|
|
|
## 23. API SECURITY
|
|
|
|
### 🔴 CRITICAL — Flera endpoints saknar authentication
|
|
|
|
**Problem:** Alla routes under `r.Group(func(r chi.Router) { r.Use(authMiddleware) ... })` är skyddade, MEN:
|
|
|
|
- `/health` och `/api/v1/health` är publika (OK)
|
|
- `/metrics` är publik — exponerar intern data
|
|
- `/debug/token` är publik (om port matchar)
|
|
|
|
**Åtgärd:**
|
|
1. Skydda `/metrics` med API-nyckel eller IP-restriction
|
|
2. Ta bort `/debug/token`
|
|
|
|
---
|
|
|
|
## 24. SECURITY TESTING
|
|
|
|
### 🔴 CRITICAL — Inga automatiserade security tests
|
|
|
|
**Problem:** Inga tester för:
|
|
- IDOR
|
|
- SQL injection
|
|
- XSS
|
|
- Authentication bypass
|
|
- Rate limiting
|
|
|
|
**Åtgärd:** Skapa security test suite.
|
|
|
|
---
|
|
|
|
## 25. CI/CD SECURITY GATE
|
|
|
|
### 🔴 CRITICAL — Ingen CI/CD security gate
|
|
|
|
**Problem:** Inga automatiska säkerhetskontroller i byggprocessen.
|
|
|
|
**Åtgärd:**
|
|
1. Lägg till secret scanning (gitleaks, truffleHog)
|
|
2. Lägg till dependency scanning (govulncheck, Snyk)
|
|
3. Lägg till static analysis (gosec, semgrep)
|
|
|
|
---
|
|
|
|
## 26. SECURITY AUDIT — SAMMANSTÄLLNING
|
|
|
|
| # | Problem | Severity | Status |
|
|
|---|---------|----------|--------|
|
|
| 1 | Secrets i Git (.env) | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 2 | Hårdkodade credentials i källkod | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 3 | Debug-endpoint genererar admin-tokens | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 4 | Login utan lösenordskontroll | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 5 | JWT HS256 fallback med hårdkodad secret | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 6 | Ingen resource-level authorization (IDOR) | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 7 | RBAC middleware används inte | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 8 | SQL injection i journal.go | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 9 | XSS via dangerouslySetInnerHTML | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 10 | CORS tillåter wildcard med credentials | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 11 | Ingen tenant isolation | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 12 | Admin-endpoints saknar admin-verifiering | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 13 | Token i localStorage | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 14 | Token för lång livstid (30 dagar) | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 15 | Rate limiting för generöst | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 16 | Error messages exponerar intern info | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 17 | Audit log saknar user_id | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 18 | SSRF-risk i LandvexRealHandler | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 19 | Saknar HSTS header | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 20 | Server kör HTTP | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 21 | /metrics är publik | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 22 | Ingen password strength policy | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 23 | Ingen RLS i PostgreSQL | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 24 | Bristfällig input-validering | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 25 | Ingen CSRF-skydd | MEDIUM | 🟡 OÅTGÄRDAT |
|
|
| 26 | Ingen CI/CD security gate | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
| 27 | Inga automatiserade security tests | CRITICAL | 🔴 OÅTGÄRDAT |
|
|
|
|
---
|
|
|
|
## 27. FINAL SECURITY GATE
|
|
|
|
### Adversarial Review
|
|
|
|
**Fråga:** "Om en angripare har internetåtkomst, ett vanligt användarkonto och känner till hela frontendens implementation — hur kan denne få åtkomst till något användaren inte borde kunna komma åt?"
|
|
|
|
**Svar:** Flera vägar:
|
|
|
|
1. **Anropa `/debug/token`** → få admin-token → full åtkomst till allt
|
|
2. **Anropa `/api/v1/auth/login`** med valfri email → få admin-token
|
|
3. **Byta ID i URL** → läsa andra företags kunder, anställda, avtal
|
|
4. **SQL injection via account-filter** → läsa hela databasen
|
|
5. **XSS via contract variables** → stjäla andra användares tokens
|
|
6. **CORS wildcard** → CSRF-attacker från vilken sida som helst
|
|
|
|
---
|
|
|
|
## REKOMMENDERADE ÅTGÄRDER (Prioriterade)
|
|
|
|
### Omedelbart (Blockerar produktion):
|
|
1. ✅ Ta bort `/debug/token`
|
|
2. ✅ Fixa `/api/v1/auth/login` — kräv lösenordsverifiering
|
|
3. ✅ Ta bort HS256-fallback, kräv RS256
|
|
4. ✅ Ta bort secrets från Git, rotera alla secrets
|
|
5. ✅ Fixa SQL injection i journal.go
|
|
6. ✅ Ta bort dangerouslySetInnerHTML eller använd DOMPurify
|
|
7. ✅ Fixa CORS — förbjud wildcard
|
|
|
|
### Inom 1 vecka:
|
|
8. ✅ Implementera tenant isolation på ALLA queries
|
|
9. ✅ Implementera resource-level authorization (IDOR-skydd)
|
|
10. ✅ Applicera RBAC på alla routes
|
|
11. ✅ Skydda admin-endpoints
|
|
12. ✅ Fixa audit log user_id
|
|
|
|
### Inom 1 månad:
|
|
13. ✅ Implementera proper session-hantering (httpOnly cookies)
|
|
14. ✅ Minska token-livstid
|
|
15. ✅ Förbättra rate limiting
|
|
16. ✅ Lägg till input-validering
|
|
17. ✅ Aktivera RLS i PostgreSQL
|
|
18. ✅ Implementera CI/CD security gates
|
|
19. ✅ Skriv security tests
|
|
|
|
---
|
|
|
|
## SLUTSTATUS
|
|
|
|
**SECURITY NOT READY**
|
|
|
|
Systemet får INTE deployas till produktion i nuvarande skick. Flera kritiska säkerhetsbrister möjliggör fullständig kompromettering av systemet.
|
|
|
|
**Blockerande issues:** 12 CRITICAL
|
|
**Medium issues:** 15
|
|
**Totalt:** 27 säkerhetsbrister
|
|
|
|
---
|
|
|
|
*Rapport genererad av Bernt (AI Security Audit)*
|
|
*Datum: 2026-08-10*
|