Sikkerhed
Sikkerhedsoverblik for Ressourcify — fra organisationens beslutningstager til kundens sikkerhedsteam.
Denne side har to lag: et overblik for ledere og indkøbere øverst, og en teknisk dybde længere nede for sikkerhedsteams og auditorer.
Overblik
Ressourcify er bygget med fire principper:
| Princip | Hvad det betyder |
|---|---|
| SSO som standard | Login går altid via din egen Microsoft Entra ID. Vi gemmer aldrig adgangskoder. |
| Defense in depth | Adgang er låst på applikations-, database- og infrastruktur-niveau — ikke kun ét sted. |
| Tenant-isolation | Hver organisations data er isoleret. En medarbejder kan aldrig se en anden organisations indhold. |
| Sporbarhed | Hver kritisk handling logges. Vi kan altid svare på "hvem ændrede hvad hvornår?" |
Hvad du som beslutningstager skal vide
- Identitet: Vi har ingen egen brugerdatabase med adgangskoder. Brugere oprettes ved første login via Microsoft Entra. Hvis du fjerner en bruger fra Entra, mister vedkommende straks adgang ved næste session-fornyelse (max 12 timer).
- Adgangskontrol: Fem roller (ORG_ADMIN, DEPT_ADMIN, COORDINATOR, RESOURCE, VIEWER) styret centralt via Entra-sikkerhedsgrupper. Du behøver ikke vedligeholde adgang to steder.
- Data: Hostet hos Supabase i EU (Frankfurt). Krypteret in transit (TLS 1.3) og at rest (AES-256).
- Audit: Hver mutation logges med før/efter-tilstand. Default retention 365 dage, konfigurerbar op til 5 år.
- GDPR: Databehandler-aftale er tilgængelig på anmodning. Data slettes ved opsigelse efter aftalt udfasningsperiode.
Hvad du som bruger ser
- Du kan ikke se data fra andre organisationer.
- Du kan kun ændre data du har eksplicit ret til (din egen — eller dem du administrerer som koordinator/admin).
- Audit-loggen ser kun ORG_ADMIN.
Teknisk dybde
For kundens sikkerhedsteam og auditorer. Forudsætter generel kendskab til web-sikkerhed.
Identitets-arkitektur
- Provider: Microsoft Entra ID (Azure AD) via OIDC, scope
openid profile email - NextAuth v5 håndterer OIDC-flow, nonce-validering, state, PKCE
- Sessioner: JWT i
HttpOnly; Secure; SameSite=Laxcookie, signeret med HS256 +AUTH_SECRET - Session-levetid: 12 timer. Re-auth efter 12t går silent via Entra hvis brugeren stadig er logget ind på Microsoft
- Ingen lokale credentials:
passwordHash-kolonnen findes påUser-modellen men benyttes ikke i produktion
Se Auth-flow for komplet sekvensdiagram.
Adgangskontrol (RBAC)
Tre lag — alle skal være tilfredse før en handling tillades:
- Session-tjek:
auth()validerer JWT-signaturen og henter brugerens roller fra DB can()-tjek:lib/authz.tsevaluerer rolle × ressource × handling × scope- Postgres Row-Level Security: Forsvarslag #3 hvis app-koden skulle have en bug
Roller, scopes og handlinger: RBAC-matrix.
Tenant-isolation
Alle queries filtrerer på organizationId fra sessionen. Modeller uden
direkte organizationId (fx Assignment) arver isolation fra parent.
Row-Level Security: Alle 34 public-tabeller har RLS-policies som
afviser læsning hvis current_setting('app.organization_id') ikke matcher
række-organisationen. Prisma's tjenestekonto ejer tabellerne og bypasser
RLS — så app-koden virker, men anden adgangsvej blokeres:
- Direkte JDBC/psql-forespørgsler fra ikke-tjenestekonto: blokeret
- Supabase PostgREST med anon-rolle: blokeret
- En lækket bruger-JWT brugt mod databasen: blokeret
Hemmeligheder
| Hemmelighed | Hvor |
|---|---|
AUTH_SECRET (JWT-signering) | Vercel env, krypteret at rest |
AUTH_MICROSOFT_ENTRA_ID_SECRET (Entra client secret) | Krypteret med ENCRYPTION_KEY i EntraConfig-tabel — alternativt env-fallback |
DATABASE_URL, DIRECT_URL | Vercel env, kun læselige af Vercel-runtime |
ENCRYPTION_KEY (AES-256-GCM til client secret) | Vercel env, separat fra database |
Krypteringen er AES-256-GCM med 96-bit nonce per ciphertext. Klartekst ses aldrig i logs.
Audit-log
Hver CREATE/UPDATE/DELETE på kritiske entiteter giver en
AuditLog-række med før- og efter-snapshot. Følsomme felter
(passwordHash, krypterede secrets) redactes til "<redacted>".
- Tabel:
AuditLogi samme database som operationelle data - Retention: Konfigurabel per organisation (90/180/365/730/1825 dage). Default 365.
- Autoprune: Daglig cron-lignende job sletter rækker ældre end retention. Idempotent.
- Eksport: ORG_ADMIN kan eksportere som CSV. Eksport-handlingen logger selv et
EXPORT-event. - Tilgang: Kun
ORG_ADMIN. Ingen anden rolle har adgang.
Se Audit-log for fuld dækningsliste og felt-skema.
Input-validering
Hvert API-endpoint har fire valideringslag:
| Lag | Værktøj | Effekt |
|---|---|---|
| Session | auth() | 401 hvis ikke logget ind |
| Authz | can() | 403 hvis utilstrækkelig rolle |
| Body | Zod safeParse | 422 ved skema-fejl |
| Query | Zod safeParse | 422 ved skema-fejl |
UUID-validering er separat (samme uuidSchema.safeParse). Vi bruger
aldrig Zod's parse — kun safeParse, så uventede inputs ikke kaster
uncaught exceptions.
Fejl-håndtering
API'et returnerer RFC 7807 Problem Details. Fejl-svar indeholder aldrig:
- Stack traces
- Database-query'er
- Andre brugeres data
- Filsystem-stier
Cross-organisation requests svarer med 404 (ikke 403) for ikke at lække ressource-eksistens.
Hosting og netværk
- Application: Vercel (EU-region)
- Database: Supabase (Frankfurt, EU)
- Transport: TLS 1.3, HSTS, sikre cookies, Content-Security-Policy
- Web Application Firewall: Vercel's indbyggede WAF
Threat model — kort
| Trussel | Modforanstaltning |
|---|---|
| Stjålet bruger-JWT | 12t-levetid + HttpOnly cookie + signatur-validering |
| Privilege escalation via gruppe-manipulation | Kun Entra-administratorer kan ændre grupper; ændringer kræver re-login |
| Cross-tenant data-lækage | App-filter + RLS som dobbelt-sikring |
| SQL injection | Prisma med parameteriserede queries |
| XSS | React's default-escaping + CSP-headers |
| CSRF | SameSite=Lax cookie + NextAuth's indbyggede CSRF-token |
| Supply chain | Lock-file committed; pnpm verify-store-integrity; dependabot |
| Insider threat | Audit-log, separation of duties (kun ORG_ADMIN kan ændre roller eller retention) |
Compliance
- GDPR: Databehandler-aftale på anmodning. Soft-delete på
Userbevarer audit-trail uden persondata. - Datafortrolighed: TLS 1.3 i transit, AES-256 at rest (Supabase + krypteret client secret)
- Subprocessorer: Microsoft (Entra), Supabase, Vercel. Liste opdateres ved ændringer.
Ansvarlig fremgangsmåde ved fund
Skriv til security@ressourcify.dk (eller den kontakt der er angivet i din kontrakt). Vi svarer inden 48 timer og koordinerer offentliggørelse i samarbejde med dig.
Vi har ikke et formelt bug bounty endnu — men anerkender alle indrapporter offentligt med samtykke.