
SaaS-sikkerhedstjekliste før lancering: 40 tjek, vi kører først (2026)
En SaaS-sikkerhedstjekliste før lancering er mere værd end en stak compliance-badges, du endnu ikke har. Her er den ubehagelige sandhed: De fleste lancerings-tjeklister fortæller dig, hvad du skal sikre, men viser aldrig hvordan. Denne leverer koden. Vi bygger på Next.js og Supabase, vi har set en enkelt manglende tenant_id-filter lade en testkonto læse en anden kundes data, og IBMs rapport om omkostninger ved databrud fra 2024 sætter det globale gennemsnit til 4,88 millioner USD. Du behøver ikke SOC 2 for at gå live. Du har brug for app-lags-basislinjen nedenfor, grupperet, kørbart og kortlagt til OWASP og NIST.
Vigtigste pointer
- Du behøver ikke SOC 2 eller en pentest for at lancere. Du har brug for app-lags-basislinjen nedenfor.
- Den farligste launch-bug er cross-tenant datalækage fra et manglende
tenant_id-tjek. - Lav aldrig din egen auth. Brug Auth.js, Clerk eller Supabase Auth.
- Auditér
NEXT_PUBLIC_-præfikser, før du deployer. Det er den hurtigste måde at lække en hemmelighed på.
Denne basislinje kortlægges til OWASP ASVS 5.0 og NIST Secure Software Development Framework (SSDF), de to referencer, Google stoler mest på for dette emne, og bemærkelsesværdigt nok de to, som ingen af de topplacerede guider bother at citere.
Din pre-launch sikkerhedstjekliste (hurtig version)
Dette er minimumskravene til sikkerhed, før du shipper, grupperet i seks kategorier. Fyrre punkter. Kør dem fra top til bund, giv kodesektionerne til din udvikler, og behandl alt markeret som P0 i prioritets-tabellen nedenfor som en launch-blocker.
Hemmeligheder & Konfiguration
.enver i.gitignorefra commit ét og blev aldrig committet.- Hvert
NEXT_PUBLIC_- ogVITE_-præfiks er auditeret; intet hemmeligt sendes til browseren. - Server-hemmeligheder ligger i en manager (platform env vars, AWS Secrets Manager, Vault), ikke i repoet.
- Enhver nøgle, der nogensinde har rørt git-historikken, roteres før lancering.
- Ingen hemmeligheder vises i logs, error payloads eller client-bundlet.
- Du har greppet det byggede bundle for live-nøgler (
grep -r "sk_live" .next/).
Auth & Adgang
- Auth er bygget på et bibliotek (Auth.js, Clerk eller Supabase Auth), ikke håndlavet.
- MFA er tilgængelig på konti.
- Session-cookies sætter
Secure,HttpOnlyogSameSite. - Ingen JWT'er eller session-tokens gemmes i
localStorage. - RBAC og least-privilege roller håndhæves server-side, ikke bare skjult i UI'en.
- Adgangskoder hashes med Argon2 eller bcrypt (kun hvis du selv styrer auth).
- Password-reset og email-verifikations flows testes mod misbrug.
Data & Tenancy
- Hver query bærer et
tenant_id-filter. - Tenant-scoping håndhæves på ORM- eller repository-laget, ikke husket per query.
- Row-level security er aktiveret, og dens fejlmodes er forstået.
- Hver object-ID endpoint kører et ejerskabstjek (dette dræber IDOR).
tenant_idinkluderes i cache-nøgler og object-storage stier.- Data er krypteret ved hvile og under transmission.
- Betalings- og webhook-signaturer (Stripe osv.) verificeres server-side.
Dependencies & Supply Chain
npm auditellerpnpm auditer ren for høje og kritiske (eller eksplicit triagerede).- Dependabot eller Renovate er aktiveret.
- Snyk eller Socket kører dybere SCA samt malware- og licens-tjek.
- Lockfilen er committet.
- Ingen forladte eller uvedligeholdte pakker sidder i den kritiske sti.
- Container-images scannes, hvis du shipper Docker.
Netværk & Transport
- HTTPS håndhæves overalt, med HSTS preload.
- En Content-Security-Policy er sat (report-only først, derefter enforce).
X-Content-Type-Options: nosniffogX-Frame-Options/frame-ancestorser sat.Referrer-PolicyogPermissions-Policyer sat.- CORS bruger en allow-list, aldrig
*med credentials. - Rate limiting beskytter auth og dyre endpoints.
- Hver endpoint validerer input med et schema (Zod eller lignende).
Overvågning & Respons
- Centraliserede audit-logs registrerer, hvem der adgang til hvad, og hvornår.
- Error handling lækker aldrig stack traces til brugere.
- Alarmer affyres ved auth-anomalier (spikes i mislykkede logins, umulig rejse).
- Automatiserede backups kører, og du har testet en restore.
- En incident-response kontakt og en one-page runbook eksisterer.
- Uptime og error monitoring (Sentry eller tilsvarende) er live.
- Du kender triggeren for at hente en pentest ind.
Fix-først prioritet
Ikke alle punkter blokerer lanceringen. Denne triage-tabel sorterer basislinjen efter skade-hvis-skippe, så en founder ved, hvad der er ikke-forhandlingsbart. P0 = fix før lancering, P1 = fix uge én, P2 = fix inden for kvartalet.
| Tjek | Kategori | Hvis du springer det over | Fix indsats | Ship-blocker? |
|---|---|---|---|---|
| Cross-tenant isolering på hver query | Data & Tenancy | En kunde læser en andens data | Med | P0: blokér lancering |
| Hemmeligheder ud af client-bundlet | Hemmeligheder & Konfig | Offentlige API-nøgler, konto-overtagelse | Lav | P0: blokér lancering |
| Ejerskabstjek på object-ID endpoints | Data & Tenancy | IDOR: inkrementering af id lækker records | Lav | P0: blokér lancering |
| Auth på et bibliotek, ikke håndlavet | Auth & Adgang | Shippede auth bugs, ødelagte sessions | Med | P0: blokér lancering |
| HTTPS og HSTS overalt | Netværk & Transport | Token-tyveri over ledningen | Lav | P0: blokér lancering |
| npm audit ren for høj/kritisk | Dependencies | Kendt CVE i en transitiv dep | Lav | P1: uge én |
| Rate limiting på auth endpoints | Netværk & Transport | Credential stuffing, brute force | Lav | P1: uge én |
| Sikkerhedsheaders (CSP, HSTS, nosniff) | Netværk & Transport | XSS, clickjacking, MIME-angreb | Lav | P1: uge én |
| Centraliserede audit-logs | Overvågning | Du kan ikke se eller bevise et brud | Med | P1: uge én |
| Testet backup restore | Overvågning | En backup, der ikke restorerer, er ingenting | Med | P1: uge én |
| MFA tilgængelig på konti | Auth & Adgang | Nemmere konto-over tagelse | Lav | P2: dette kvartal |
| Fuld CSP håndhævet udover report-only | Netværk & Transport | Residual XSS overflade | Med | P2: dette kvartal |
Hemmeligheder & Konfiguration: Lækker nogen nøgler ind i dit client bundle?
Hemmeligheds-hygiejne ved lancering betyder, at ingen credential nogensinde når browseren. Præfikset NEXT_PUBLIC_ i Next.js (og VITE_ i Vite) sender en værdi til hver besøgende, så ét forkert præfiks lækker en nøgle. Hold .env ude af git, put server-hemmeligheder i en manager, og grep dit build-output, før du deployer.
Her er den fælde, vi ser oftest: NEXT_PUBLIC_ betyder ikke "offentlig info." Det betyder "Jeg sender bogstaveligt talt dette til hver besøgendes browser." Præfiks en Stripe-hemmelighed eller en service-role nøgle på den måde, og den er live i bundlet for enhver, der åbner DevTools.
# .env.local (the mistake)
NEXT_PUBLIC_STRIPE_SECRET=sk_live_51H... # BAD: ships to every browser
STRIPE_SECRET_KEY=sk_live_51H... # OK: server-only
# Public (safe to expose) vs server-only
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ... # anon key is meant to be public
SUPABASE_SERVICE_ROLE_KEY=eyJ... # never NEXT_PUBLIC_ this
# Catch a leaked key before you deploy
grep -r "sk_live" .next/ # any hit means a secret is in your client bundleResten af hemmeligheds-basislinjen er kedelig og ikke-forhandlingsbar: .env i .gitignore fra første commit, server-hemmeligheder i en manager frem for repoet, og rotation af enhver nøgle, der nogensinde har rørt git-historikken (at slette en commit fjerner ikke lækagen). Et lækket deploy-token er præcis sådan, brud som Vercel-hacket starter, så behandl hvert token, som om det allerede er på nogens watchlist.
Auth & Adgang: Bør du bygge auth eller bruge et bibliotek?
Bør du bygge auth eller bruge et bibliotek? Brug næsten altid et bibliotek. Auth.js, Clerk og Supabase Auth har absorberet års edge cases, du ellers ville genopdage i produktion: session fixation, token revokation, misbrug af reset-flow. At rulle din egen er kun forsvarligt med en security engineer og en grund til, at ingen provider passer, hvilket er sjældent.
At rulle sin egen auth er den dyreste måde at spare $25 om måneden på. Her er hvordan de ærlige muligheder sammenlignes.
| Mulighed | Bedst når | MFA indbygget | Standard session | Fælde |
|---|---|---|---|---|
| Auth.js (NextAuth) | Du vil have gratis, self-hosted, fuld kontrol | Via providers/add-ons | JWT eller database | Du ejer hver security edge case |
| Clerk | Du vil have MFA, UI og orgs out of the box | Ja | Managed | Betalte tiers skalerer med aktive brugere |
| Supabase Auth | Du kører allerede Supabase og Postgres RLS | Ja | JWT | RLS policy-kvalitet er op til dig |
| Roll your own | Du har en security engineer og ingen provider passer | Du bygger det | Du bygger det | De fleste auth bugs starter lige her |
To fælder sænker teams, der vælger et bibliotek, men skipper konfigurationen. For det første er JWT-revokation virkelig svær, så et stjålet token forbliver gyldigt, indtil det udløber; hold token-levetider korte og foretræk server-side sessions til noget følsomt. For det andet er tokens i localStorage stjælbare af ethvert XSS-payload, så gem sessions i httpOnly cookies med Secure og SameSite. Håndhæv RBAC på serveren, ikke ved at skjule knapper i UI'en.
Hvis din SaaS shipper en AI- eller LLM-funktion, behandl model-input som en utroværdig auth-grænse også. Se vores guide til forebyggelse af prompt injection, fordi en jailbroken assistent med tool-access er et access-control problem iført et chat-vindue.
Data & Tenancy: Hvordan stopper du én tenant fra at læse en andens data?
Tenant-isolering betyder, at hver query, cache-nøgle og storage-sti er scopet til den aktuelle tenant. Et manglende tenant_id-filter lader én kunde læse en andens data, den farligste launch-bug der findes. Row-level security hjælper, men det er et sikkerhedssele, ikke et kraftfelt, så tilføj også ejerskabstjek på hver object-ID endpoint.
Dette er afsnittet, ingen konkurrent dækker som kode, og det er grunden til, at cross-tenant lækager sniger sig ind i produktion. Fixet starter med aldrig at stole på et ID alene. Scop hver læsning til callerens tenant, og håndhæv det på data-laget, så ingen behøver at huske det per query.
// BAD: no tenant scope. Any valid id returns any tenant's row.
const order = await db.order.findFirst({ where: { id } });
// GOOD: scoped to the caller's tenant on every read.
const order = await db.order.findFirst({
where: { id, tenantId: session.tenantId },
});Den relaterede fejl er IDOR (insecure direct object reference), som OWASP API Security Top 10 filer under API1: Broken Object Level Authorization. En testkonto inkrementerer et ID i URL'en og læser en record, den aldrig bør se. PortSwiggers Web Security Academy har en fuld walkthrough af, hvordan angribere finder disse. Fixet er ét ejerskabstjek.
// GET /api/invoices/1234, a test account increments the id
// and reads another tenant's invoice. Classic BOLA.
const invoice = await db.invoice.findUnique({ where: { id } });
// Fix: verify ownership before you return anything.
if (invoice.tenantId !== session.tenantId) {
return res.status(403).json({ error: "Forbidden" });
}Her er framingen, der holder dig ærlig: hver IDOR er en tenant-isoleringsfejl, men ikke hver tenant-isoleringsfejl er en IDOR. Postgres row-level security fanger mange af dem på databasen, men den har silent-failure modes, det er værd at kende, før du stoler på den.
-- Postgres RLS: a seatbelt, not a force field.
alter table orders enable row level security;
create policy tenant_isolation on orders
using (tenant_id = current_setting('app.tenant_id')::uuid);
-- Silent-failure trap: forget to SET app.tenant_id on a pooled
-- connection and the policy reads the PREVIOUS request's tenant.Connection-pool forurening, async-context lækager og shared-cache forgiftning besejrer alle RLS stille, hvilket er hvorfor OWASP Multi-Tenant Security Cheat Sheet fortæller dig at præfixe cache-nøgler og storage-stier med tenanten også. Access-control kapitlet i OWASP ASVS 5.0 og Supabase RLS docs er de to referencer, det er værd at læse fuldt ud her.
Dependencies & Supply Chain: Hvad gemmer sig i dine node_modules?
Din app er kun så sikker som sin svageste transitive dependency. Kør npm audit eller pnpm audit i CI og fail buildet på høje eller kritiske fund, før du overhovedet lancerer. Tilføj Dependabot eller Renovate for automatiske opdateringer og Snyk eller Socket for dybere malware- og licens-tjek.
Fælden er at køre audit'en én gang i hånden, se grønt og aldrig køre den igen. Wire det ind i CI, så en ny CVE i en pakke, du ikke har rørt, stadig blokerer mergen.
# .github/workflows/ci.yml: block the merge on high/critical
- name: Audit dependencies
run: npm audit --audit-level=high # non-zero exit fails the jobnpm audit docs dækker severity levels og --production flagget, hvis du vil ignorere dev-only fund. Automatiseret scanning er bordtennis, dog; for dybere statisk analyse, der fanger code smells og injection paths, en dependency scanner miss'er, se vores SonarQube review. Commit din lockfile, drop pakker, der ikke har shipped en release i år, og scan dit container image, hvis du deployer Docker.
Netværk & Transport: Hvilke security headers har en SaaS faktisk brug for?
Hvilke security headers har en SaaS brug for? HTTPS plus HSTS og et kort header-set lukker de lettest-udnyttelige gab. Tilføj en Content-Security-Policy, en CORS allow-list i stedet for et wildcard, og rate limits på auth og dyre endpoints. Valider hvert input med et schema som Zod, så dårlige payloads aldrig når din logik.
Du behøver ikke hver header, der nogensinde er opfundet. Du har brug for denne korte liste, og MDN security-headers reference forklarer hver i dybden.
| Header | Anbefalet værdi | Hvad det stopper |
|---|---|---|
| Strict-Transport-Security | max-age=63072000; includeSubDomains; preload | Protocol downgrade, SSL-strip angreb |
| Content-Security-Policy | default-src 'self'; start report-only | XSS, injectede scripts, data exfiltration |
| X-Content-Type-Options | nosniff | MIME-sniffing, der gør en upload til et script |
| X-Frame-Options / frame-ancestors | DENY (eller frame-ancestors 'none') | Clickjacking via skjulte iframes |
| Referrer-Policy | strict-origin-when-cross-origin | Lækage af fulde URLs (og tokens inde i dem) |
| Permissions-Policy | camera=(), microphone=(), geolocation=() | Rogue scripts, der rører device APIs |
Sæt headers én gang, ved kanten, og tilføj en rate limiter, så et script ikke kan brute-force din login-rute hele natten.
// next.config.js: security headers on every response
const securityHeaders = [
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-Frame-Options", value: "DENY" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
];
// Rate-limit auth routes (Upstash example)
const { success } = await ratelimit.limit(ip);
if (!success) return new Response("Too many requests", { status: 429 });Start din CSP i report-only, så du ikke breaker din egen app, se violation reports i et par dage, og flip den derefter til enforce. Hold CORS til en navngiven allow-list, og par aldrig * med credentials.
Overvågning & Respons: Hvordan vil du vide, hvis du er blevet brudt ind i?
Du kan ikke respondere til noget, du ikke kan se. Før lancering, wire op centraliserede audit-logs, alarmer på auth-anomalier som spikes i mislykkede logins, automatiserede backups med en testet restore og en one-page incident runbook. En utestet backup er et håb, ikke en backup, og tiden til at skrive runbooken er nu, ikke midt i en incident.
IBMs data sætter den gennemsnitlige tid til at identificere og indeholde et brud til 258 dage, og du kan ikke skrumpe det tal, hvis dine logs ikke registrerer, hvem der rørte hvad. Centraliser dem, alarmér på de anomalier, der betyder noget (spikes i mislykkede logins, umulig-rejse logins, pludselig export volumen), og sørg for, at din error handler returnerer en clean message i stedet for en stack trace, der mapper dine internals.
Moderne brud-detektion læner sig på anomaly monitoring frem for statiske regler; der er mere om, hvordan det faktisk virker i vores stykke om hvordan AI forhindrer databrud. For framework backing, lægger NIST SSDF (SP 800-218) respond-and-monitor practices ud i klart sprog. Test en restore før lancering, ikke efter din database forsvinder.
Hvad vi faktisk finder, når vi reviewer vores egne lanceringer
Når vores team kører en pre-launch security pass på et build, vores eller en klients, viser to misses sig mere end noget andet. Først: en hemmelighed, der rider ind i browseren på et NEXT_PUBLIC_ præfiks, normalt en third-party API-nøgle, nogen har præfikset for at få et client-side call til at virke. Andet: mindst én endpoint, der mangler sit tenant_id scope eller et ejerskabstjek.
Tenant-scope miss'et er det skræmmende, fordi appen ser fin ud. Hver side loader. Bugen viser sig kun, når nogen ændrer et ID i URL'en. Ved én review returnerede GET /api/orders/:id enhver ordre til enhver logged-in bruger; en testkonto læste en anden tenants ordrer ved at inkrementere nummeret. Fixet var to linjer: sammenlign order.tenantId med session.tenantId før returnering.
Vi vil ikke citere dig en fake catch-rate her. Hvad der er ærligt og gentageligt er dette: NEXT_PUBLIC_ lækagen og det manglende tenant scope er de to ting, vi finder i næsten hver first-pass review, og begge er billige at fixe, når du først ved, du skal lede efter dem. Det er præcis derfor, tjeklisten front-loader dem som P0.
Hvis du hellere vil have et team til at køre denne pass for dig før launch day, er det det arbejde, vi laver. Få en pre-launch security review →
Om forfatteren
Mert Batur Gurbuz er Co-Founder af Techsy.io, hvor teamet shipper AI-agenter, automationssystemer og voice/SDR pipelines for B2B-klienter. Han studerer på University of Birmingham og skriver om LLM tooling stacken, som Techsy-teamet faktisk bruger i produktion. Connect på LinkedIn.
Ofte stillede spørgsmål
Hvad bør være på en SaaS-sikkerhedstjekliste før lancering?
Seks kategorier: hemmeligheder og konfig (hold nøgler ude af client-bundlet), auth og adgang (brug et bibliotek, tilføj MFA), data og tenancy (tenant_id scoping plus ejerskabstjek), dependencies (npm audit i CI), netværk og transport (HTTPS, HSTS, CSP, rate limits) og overvågning og respons (audit-logs, testede backups, en runbook).
Er min SaaS sikker nok til at lancere?
Du er klar, når P0-basislinjen er done: hemmeligheder ud af client-bundlet, tenant-isolering på hver query, auth på et bibliotek, HTTPS med security headers og en ren dependency scan. Perfektion er ikke barren. En shipped, overvåget app med basislinjen dækket slår en "perfekt" én, der aldrig lancerer.
Har jeg brug for en penetrationstest før lancering af en SaaS?
Ikke for at lancere lovligt. Prioritér én, hvis du håndterer betalinger eller PII, målretter enterprise-købere, eller en auditor eller investor spørger. På MVP-stadiet, brug den indsats på app-lags-basislinjen og OWASP Top 10 først. En pentest finder mere, når de åbenlyse IDOR- og header-gab allerede er lukket.
Har jeg brug for SOC 2 for at lancere en SaaS?
Nej. Ingen kunde forventer SOC 2 fra en startup, der lancerede sidste uge. Det er en enterprise-sales unlock, ikke en launch gate, og det tager måneder. Ship med app-lags-basislinjen, og start derefter SOC 2-processen, når en rigtig enterprise-deal har brug for det, ikke før.
Bør jeg bygge min egen authentication eller bruge et bibliotek som Auth.js, Clerk eller Supabase Auth?
Brug næsten altid et bibliotek. Auth.js, Clerk og Supabase Auth har håndteret de session-, token- og reset-flow edge cases, der forårsager de fleste self-built auth bugs. At rulle din egen er kun forsvarligt, hvis du har en security engineer og et hard requirement, ingen provider møder, hvilket er genuint sjældent.
Hvordan holder jeg hemmeligheder ude af mit client bundle?
Auditér hvert NEXT_PUBLIC_- og VITE_-præfiks, fordi alt med det præfiks shipper til browseren. Hold .env ude af git fra første commit, gem server-hemmeligheder i en manager, og grep dit byggede bundle (grep -r "sk_live" .next/) før du deployer for at fange en lækket nøgle.
Hvordan isolerer jeg tenant-data i en multi-tenant SaaS?
Put et tenant_id-filter på hver query og håndhæv det på ORM- eller repository-laget, så det er automatisk. Aktiver row-level security og lær dens failure modes (pool forurening, async lækager). Tilføj et ejerskabstjek til hver object-ID endpoint for at lukke IDOR, og scop cache-nøgler og storage-stier efter tenant.
Hvilke security headers har en SaaS brug for før lancering?
Som minimum: Strict-Transport-Security (HSTS), en Content-Security-Policy, X-Content-Type-Options: nosniff, X-Frame-Options eller frame-ancestors, Referrer-Policy og Permissions-Policy. Start din CSP i report-only, review violations, og enforce den derefter. MDN security-headers docs lister anbefalede værdier for hver, og headers-tabellen ovenfor summerer, hvad hver én stopper.
Er automatiseret scanning som npm audit eller Snyk nok?
Nødvendig, men ikke tilstrækkelig. Værktøjer som npm audit, Snyk og Socket fanger kendte CVE'er og maliciøse pakker, men de kan ikke finde business-logic og access-control flaws som IDOR eller et manglende tenant scope. Dem har brug for et menneske, en testkonto og et eksplicit ejerskabstjek. Kør begge: scanneren og en manuel pass.
Bundlinjen: En SaaS-sikkerhedstjekliste, du faktisk kan shippe
Du behøver ikke at være perfekt for at lancere. Du har brug for basislinjen. Luk P0-punkterne først: hemmeligheder ud af bundlet, tenant-isolering på hver query, et ejerskabstjek på hver object-endpoint, auth på et bibliotek og HTTPS med headers. Hvis du fixer én ting før fredagens lancering, lad det være tenant-isolering, fordi det er bugen, der lækker en kundes data med nul advarsel.
Alt her er kørbart i dag, og intet af det kræver et compliance-budget. Arbejd dig gennem de 40 tjek, giv kodesektionerne til din dev, og ship. Vil du have et andet sæt øjne, før du går live? Få en gratis konsultation, og vi gennemgår listen med dig.