Vi bruker informasjonskapsler for analyse og markedsføring. Les mer
Tilbake til Lab
m51 PlattformApril 2026 · 10 min

Sikkerhet i M51 AI OS: Slik beskytter vi kundedataene dine

Row-Level Security, tofaktor, rate-limiting og kontinuerlig revisjon — de konkrete tiltakene som holder multi-tenant-dataene dine trygge.

Markedsføringsdata er blant de mest sensitive dataene en bedrift har. Kampanjebudsjetter, kundelister, organisk ytelse, annonseresultater, og ikke minst OAuth-tilganger til Meta, LinkedIn, Google Ads og analyseverktøy — alt samlet på ett sted.

Når vi bygger en multi-tenant plattform som håndterer dette for hundrevis av kunder samtidig, kan ikke sikkerhet være et salgsargument som legges på i etterkant. Det må være en arkitekturell forutsetning fra første linje kode.

Denne artikkelen beskriver de konkrete tekniske tiltakene som er i drift i M51 AI OS i dag. Ingen løfter, ingen hype — bare hva vi gjør, hvorfor, og hvordan du kan verifisere det.


Dataisolasjon mellom kunder

Alle kjernemodeller i plattformen har en customer_id som identifiserer hvilken kunde dataen tilhører. Denne kolonnen brukes både i applikasjonslaget, der hvert API-kall verifiserer at innlogget bruker tilhører riktig customer_id, og i databaselaget, der Row-Level Security (RLS) er aktivert på alle sensitive tabeller.

RLS er Postgres sin innebygde mekanisme for radbasert tilgangskontroll. Policyene våre krever at spørringer kjører som service_role — en nøkkel som kun brukes av backend-tjenestene våre. Anon-nøkkelen, som er tilgjengelig for klienter, har ikke tilgang til de beskyttede tabellene i det hele tatt.

Hvilke tabeller beskyttes av RLS

Alle tabeller som inneholder kundespesifikke data kjører under service_role-policyer. Et utdrag fra migrasjon 043 som aktiverte RLS på tidligere ubeskyttede tabeller:

TabellInnholdRLS-policy
daily_kpi_historyDaglige KPI-snapshots per kundeservice_role only
pagespeed_snapshotsPageSpeed-målingerservice_role only
agent_activity_logLogg over agent-kjøringerservice_role only
scroll_stopper_simulationsKampanjesimuleringerservice_role only
budget_recommendationsBudsjettanbefalingerservice_role only
agreement_acceptancesAvtaleaksepterservice_role only

Vi kjører også en automatisert sikkerhetsskanning av databaseskjemaet for hver migrasjons-PR. Hvis noen åpner en migrasjon som glemmer å sette RLS på en ny tabell, blokkeres PR-en automatisk før den når produksjon.

Kryptering av sensitive data

RLS beskytter mot uautoriserte spørringer mot databasen. Men hva skjer hvis en angriper på en eller annen måte får tak i selve service_role-nøkkelen? Uten et lag til ville de fått klartekst-OAuth-tokens for alle kunder. Det var uakseptabelt, så vi krypterer alle sensitive felter individuelt før de skrives til databasen.

Hva som krypteres

  • OAuth-tokens for Google, Meta og LinkedIn — access_token, refresh_token og tilknyttet e-postadresse
  • Integrations-secrets for Clickup, HubSpot, Tripletex og Slack — inkludert webhook-URL-er og API-nøkler

Verdiene lagres som Fernet-ciphertext direkte i databasefeltene. Fernet er en autentisert krypteringsalgoritme (AES-128-CBC kombinert med HMAC-SHA256) som både hindrer lesing og oppdager manipulering.

Nøkkelen er separert fra databasetilgangen

Krypteringsnøkkelen avledes via HKDF-SHA256 fra en dedikert miljøvariabel, TOKEN_ENCRYPTION_KEY, som er atskilt fra både DB_SERVICE_ROLE_KEY og JWT_SECRET. Applikasjonen nekter å starte i produksjon hvis nøkkelen mangler eller er kortere enn 32 tegn — samme mønster som JWT_SECRET-validatoren.

Det betyr at en angriper må kompromittere to uavhengige hemmeligheter for å lese tokens: både databasens service_role og applikasjonens krypteringsnøkkel. De har forskjellige rotasjonsrutiner og lagres ikke sammen.

Tamper-resistance via AAD

Hver ciphertext er bundet til en kombinasjon av kunde, provider og felt via Additional Authenticated Data (AAD). Hvis noen forsøker å flytte en ciphertext fra én rad til en annen — for eksempel kopiere kunde A sitt Meta-token inn i kunde B sin rad for å tilegne seg tilgang — vil dekrypteringen feile. HMAC-laget i Fernet oppdager i tillegg enhver endring i selve ciphertexten automatisk.

Versjonering og nøkkelrotasjon

Alle ciphertext har et versjonsprefiks (v1:...), og oauth_tokens- og integrations-tabellene har en encryption_version-kolonne. Det gjør det mulig å rotere nøkler uten nedetid: vi introduserer v2, re-krypterer gradvis i bakgrunnen, og fjerner v1 først når alle rader er oppdatert.

KMS-klar arkitektur

Koden er bygget rundt et FieldEncryptor-grensesnitt. Dagens implementasjon bruker applikasjonslag-kryptering fordi produksjonsmiljøet vårt ikke har native KMS-integrasjon, men hele verktøykjeden — kryptering, dekryptering, rotasjon — kan byttes til AWS KMS, GCP KMS eller HashiCorp Vault uten å røre koden som skriver eller leser tokens. Vi bytter når første enterprise-kunde stiller eksplisitt krav.

Tilstand i produksjon

Alle eksisterende rader er migrert fra klartekst til v1-kryptering via et idempotent backfill-script. Produksjonsdatabasen har null rader med klartekst-tokens. Nye tokens krypteres automatisk ved innskriving gjennom samme kodesti.

Autentisering og tilgang

Pålogging går via et administrert Auth-lag med JWT som sesjonsformat. Passord lagres hashet med bcrypt. I produksjon kreves det at JWT_SECRET er minst 32 tegn langt — applikasjonen nekter å starte hvis denne ikke er riktig konfigurert.

Rollestruktur er bevisst enkel: en binær admin/bruker-rolle kombineres med et numerisk access_level som styrer hvilke sider og funksjoner brukeren ser. Tilgang verifiseres på hvert API-kall — ikke bare i UI-laget.

Tofaktorautentisering (MFA)

MFA er innebygd, ikke en tilleggstjeneste fra tredjepart. Vi støtter tre metoder side om side:

  • TOTP — standard autentiseringsapper (Google Authenticator, 1Password, Authy, osv.)
  • SMS — engangskode til verifisert telefonnummer
  • Gjenopprettingskoder — engangskoder du kan skrive ned hvis du mister tilgang til enheten din

Rate-limiting på MFA-endepunktene hindrer brute-force: send-OTP, verify-OTP og recovery-code-bruk har alle strammere grenser enn det generelle API-et.

Beskyttelse av AI-laget

En AI-plattform har et angrepsflate som tradisjonelle webapper ikke har. Prompt-injeksjon, SSRF via AI-initierte HTTP-kall, og eksfiltrering gjennom hallusinerte lenker er reelle trusler. Vi behandler alle tre som førsteklasses sikkerhetsproblemer.

  • Prompt-injeksjon: flere titalls kjente mønstre blokkeres før input når modellen — alt fra "ignore previous instructions" på flere språk til delimiter-injeksjon, rolleovertakelse, system-prompt-ekstraksjon og jailbreak-forsøk
  • SSRF-vern: AI-verktøy som henter eksterne URL-er kjører gjennom en validator som avviser localhost, cloud-metadata-endepunkter (169.254.169.254), private IP-områder og DNS-rebinding
  • HTML-sanitering: alle sanitiserte felter fjerner script-tagger, iframes, event-handlers (onclick og lignende) og javascript:-URI-er før de lagres eller rendres
  • Unicode-normalisering: fullwidth-tegn og combining marks brukes aktivt for å omgå mønstermatching — input normaliseres før validering
  • Tilgangskontroll på agent-nivå: verifiserer at innlogget bruker faktisk eier customer_id-en som forespørselen refererer til, ikke bare at brukeren er autentisert

Alle fem punktene har dedikerte testklasser i test_security.py som kjøres på hver eneste PR.

Infrastruktur og motstandsdyktighet

Sikkerhet er ikke bare angrepsforsvar — det handler også om å holde plattformen tilgjengelig og forutsigbar under press, uten å blottlegge systeminformasjon når noe går galt.

Rate-limiting

API-et har en generell grense, og strammere grenser på endepunkter som er sensitive for misbruk:

EndepunktGrense
Generell API-trafikk60 forespørsler per minutt per IP
Innlogging5 forsøk per minutt
Registrering3 forsøk per minutt
Passordendring3 forsøk per minutt
MFA-verifiseringStrammere per-brukergrense

Rate-limit-tellerne deles på tvers av alle backend-workere via en sentral datastore, slik at grensene holder selv under høy last med mange parallelle prosesser.

Circuit breaker og graceful degradation

En circuit breaker sitter foran databaselaget. Etter fem påfølgende feil bytter den til OPEN-tilstand, returnerer HTTP 503 i 30 sekunder, og prøver deretter en forsiktig HALF_OPEN-probe før den eventuelt går tilbake til normal drift. Dette forhindrer at en midlertidig databaseforstyrrelse kaskaderer til full nedetid, og det gir databasen tid til å komme seg uten å drukne i retry-storm fra plattformen.

Databaseklienten over circuit breakeren har egen retry med eksponentiell backoff (0,5 → 1 → 2 sekunder) for forbigående feil som 408, 429, 500, 502, 503 og 504.

Sikkerhetsheaders

Alle HTTP-svar fra backend setter:

  • Strict-Transport-Security — tvinger HTTPS i nettlesere som har besøkt oss før
  • Content-Security-Policy — begrenser hvor skript og ressurser kan lastes fra
  • X-Frame-Options: SAMEORIGIN — forhindrer at plattformen embeddes i iframes på fremmede domener
  • X-Content-Type-Options: nosniff — stopper nettlesere fra å gjette MIME-typer

CSP-policyen for dashboardet skiller seg fra CSP-en for markedsføringssidene: dashboardet har ikke connect-src til tredjeparts-analyseverktøy, mens de offentlige sidene gjør det for å kunne måle konvertering.

Hemmeligheter og konfigurasjon

Null hemmeligheter er hardkodet i kildekoden. Alle API-nøkler, OAuth-credentials, database-URL-er og interne secrets lastes fra miljøvariabler i produksjonsmiljøet og er bare tilgjengelige for kjøretidsprosessene. Pydantic-basert validering sjekker at kritiske verdier er satt før applikasjonen starter — hvis noe mangler i produksjon, nekter prosessen å komme opp i det hele tatt.

Tre kritiske hemmeligheter er bevisst separerte med uavhengige rotasjonsrutiner:

  • JWT_SECRET — signerer brukersesjoner; må være minst 32 tegn
  • DB_SERVICE_ROLE_KEY — gir backend tilgang til databasen bak RLS
  • TOKEN_ENCRYPTION_KEY — dekrypterer OAuth-tokens og integrations-secrets; må være minst 32 tegn

En angriper må kompromittere alle tre for å eksfiltrere klartekst-tokens fra produksjonsdatabasen. Å lekke én nøkkel reduserer ikke sikkerheten til de to andre.

Service-account-JSON for Google Workspace base64-kodes i en egen miljøvariabel og dekodes i minne ved oppstart. Selve JSON-filen ligger aldri på disk.

Sikkerhet som kontinuerlig disiplin

Kontrollene over kjører ikke bare i produksjon. De valideres på hver eneste pull request av GitHub Actions før kode kan merges til main.

  • ruff — Python lint-regler som fanger utrygge mønstre og dårlig stil
  • ESLint + TypeScript-kompilering i strict-modus — frontend-typechecks hele kodebasen
  • pytest med coverage — 787 backend-tester, inkludert hele sikkerhetssuiten i tests/test_security.py, tests/test_field_encryption.py og tests/test_oauth_token_encryption.py
  • vitest — 162 frontend-tester som dekker tilgangskontroll, API-klient og sensitive hooks
  • npm audit — stopper PR-er med høy- eller kritisk-alvorlige frontend-avhengigheter
  • pip-audit — scanner alle Python-avhengigheter mot kjente sårbarheter i PyPA-databasen
  • Database-sikkerhetsskanning — blokkerer migrasjons-PR-er med feil som manglende RLS eller usikre funksjoner

Med andre ord: en utvikler kan ikke ved et uhell slå av RLS på en tabell, legge inn et prompt-injeksjons-sårbart endepunkt, eller introdusere en avhengighet med kjent CVE. CI fanger det før koden når kunder.

Hva artikkelen ikke dekker

Vi har med vilje holdt oss til tekniske kontroller som er verifiserbare i kildekoden. To ting ligger utenfor omfanget:

  • Juridisk behandling av personopplysninger — rettslig grunnlag, databehandleravtaler, dine rettigheter under GDPR — dekkes separat i personvernerklæringen
  • Formelle sertifiseringer som SOC 2 eller ISO 27001 — vi har ikke disse per april 2026; vi er åpne om hvor vi står og diskuterer gjerne konkrete krav i salgssamtaler

Les personvernerklæringen for juridiske detaljer

Personvern

Ansvarlig rapportering

Hvis du oppdager en sårbarhet i M51 AI OS, vil vi høre om det. Send e-post til [email protected] med en beskrivelse av funnet, gjerne med stegene som reproduserer problemet. Vi svarer innen to virkedager, og vi forfølger ikke sikkerhetsforskere som rapporterer i god tro.

Ikke test sårbarheter mot produksjonsdata som ikke tilhører deg. Hvis du trenger et testmiljø for verifisering, ta kontakt først — vi hjelper deg med å sette opp en isolert konto.

Om M51 AI OS

M51 AI OS er plattformen som gir markedsføringsteam og byråer tilgang til spesialiserte AI-agenter for innholdsproduksjon, SEO, annonsering og kampanjeoptimalisering. Sikkerhetstiltakene beskrevet her er på plass fordi kundene våre stoler oss med arbeid som er kjernen av forretningen deres.

Vil du se plattformen i aksjon? Book en demo.

Book demo
NorskutvikletGDPR-compliantClaude Opus 4.6
Personvern