Den 15. september 2026 publiserte CISA og NIST en felles veiledning kalt Interagency Report 8587, med klare krav til hvordan identitetstoken og API-nøkler skal sikres mot forfalskning, tyveri og misbruk. Bakgrunnen var en rekke hendelser gjennom sommeren og høsten der falske tokens, gjenbrukte sesjoner og lekkede API-nøkler sto for de fleste store sikkerhetsbruddene. I ett tilfelle klarte angripere å forfalske SAML-forespørsler i et kompromittert føderasjonsmiljø og omgå tofaktorautentisering hos tusenvis av organisasjoner. I et annet tilfelle ga en eksponert signeringsnøkkel angripere mulighet til å forfalske tokens og hente ut over 60 000 e-poster fra et enkelt byrå.
Dette er en praktisk guide for utviklere og driftsteam som bygger eller drifter API-er med JWT (JSON Web Tokens). Du får 14 konkrete steg som dekker signering, validering, rotasjon, tilbakekalling, rate limiting, skylagring av nøkler og overvåking, samt kartlegging mot kravene i IR 8587. Vi bygger et komplett eksempelprosjekt i Node.js og Express, men prinsippene gjelder uavhengig av språk og rammeverk. Sett av 60-75 minutter for å følge hele guiden fra start til et fungerende, testet oppsett.
Slik er et JWT bygget opp: header, payload og signatur
Før vi går i gang med stegene, kort om hva et JWT faktisk er. Et JWT består av tre deler skilt med punktum: en header som beskriver algoritmen, en payload med selve dataene (kalt claims), og en signatur som bekrefter at innholdet ikke er endret. Alle tre delene er base64url-kodet, ikke kryptert, så alle som fanger opp tokenet kan lese innholdet rett ut. Det er en vanlig misforståelse at JWT er hemmelig. Det er signaturen som er beskyttet, ikke innholdet.
// Dekodet (ikke kryptert) innhold i et typisk access-token
{
"alg": "RS256",
"typ": "JWT"
}
.
{
"sub": "bruker-4471",
"iss": "api.dittfirma.no",
"aud": "dittfirma-api",
"scope": "konto:les konto:skriv",
"iat": 1760000000,
"exp": 1760000600,
"jti": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
Legg merke til feltene iss (utsteder), aud (mottaker), exp (utløpstid), iat (utstedt tidspunkt) og jti (unik token-ID). Disse fem feltene er grunnlaget for alt vi skal validere i resten av guiden, og det er nøyaktig disse NIST peker på i IR 8587 når de krever at “all tokens and assertions MUST include explicit audience fields”. Siden innholdet er lesbart for alle, skal du aldri legge passord, personnummer eller andre sensitive data rett i payloaden. Scope-feltet bør også være så smalt som mulig, slik at et lekket token gir minst mulig handlingsrom for en angriper.
Hvorfor JWT- og API-token-sikkerhet er kritisk akkurat nå
CISA og NIST skriver i sin felles kunngjøring at føderale byråer, skyleverandører og skykunder bør gjennomgå og implementere IR 8587 for å styrke sikkerheten i skysystemene sine. Veiledningen er rettet mot alt som bruker signerte tokens for autentisering: SSO, føderasjon, API-tilgang og i økende grad også AI-agenter som trenger tokens for å nå systemer, data og API-er på egne vegne. Det gjør JWT-sikkerhet relevant langt utenfor amerikansk offentlig sektor, også for norske og nordiske team som bygger API-er mot AWS, Azure eller GCP.
Tallene bak veiledningen er ubehagelig konkrete. En rapport fra Stingrai sin skysikkerhetsindeks for 2026 fant svake IAM-kontroller i 87 til 97 prosent av kontoer på AWS, Azure og Google Cloud. Samme rapport viste at 21,0 prosent av all førstegangs-tilgang til Google Cloud-arbeidsbelastninger i andre halvår 2025 skjedde via feilkonfigurasjon, og at 78 prosent av undersøkte IT-fagfolk hadde opplevd skyfeilkonfigurasjon det siste året. Det er nøyaktig den typen svakheter en gjennomgang av JWT-validering og API-nøkkelstyring er laget for å avdekke.
NIST formulerer kravet til token-validering konkret: “The source and integrity of identity assertions and access tokens are verified before granting access to system and information resources.” Med andre ord: ingen API skal godta et token uten å bekrefte både hvor det kom fra og at det ikke er endret på veien. CISA følger opp med en klar oppfordring: “CISA urges federal agencies, CSPs and cloud consumers to review and implement IR 8587 to improve the security of their cloud systems.” Det er dette vi bygger praktisk støtte for i resten av artikkelen.
De vanligste angrepstypene mot JWT og hvordan de fungerer
Før du begynner å bygge, er det nyttig å vite hva du faktisk forsvarer deg mot. De fleste angrep mot JWT-baserte API-er faller i noen få kategorier, og alle stegene senere i denne guiden er direkte svar på én eller flere av dem.
Algoritme-forveksling er det klassiske angrepet der et bibliotek lar en angriper bytte signeringsalgoritmen fra RS256 til HS256 og deretter signere et falskt token med den offentlige nøkkelen som delt hemmelighet. Dette fungerer kun hvis verifiseringskoden ikke eksplisitt låser hvilken algoritme som godtas, noe steg 7 i denne guiden retter opp i. En nær beslektet variant kalles “alg: none”-angrepet, der tokenet markeres som usignert, og noen biblioteker historisk godtok det uten videre sjekk.
Token-replay er det andre store angrepsmønsteret, og det var ifølge gjennomgangen av sikkerhetshendelser i september 2026 blant de mest utbredte i perioden. Her fanger angriperen opp en gyldig, uendret forespørsel og sender den på nytt for å utløse samme handling to ganger, for eksempel en betaling. Steg 12 i guiden bygger forsvar mot nettopp dette ved å spore unike token-ID-er (jti).
Signeringsnøkkel-eksponering er det tredje mønsteret, og det var dette som gjorde skade i flere av de omtalte hendelsene fra 2026, der en lekket nøkkel ga angripere mulighet til å forfalske tokens helt fritt. Her hjelper verken god claim-validering eller rate limiting, siden angriperen i praksis kan utstede egne, gyldig signerte tokens. Dette er grunnen til at steg 3 og steg 10 i guiden handler spesifikt om trygg lagring og skanning etter lekkede nøkler, ikke bare om selve valideringslogikken.
Forutsetninger: verktøy, versjoner og kunnskap du trenger
Du trenger ikke være sikkerhetsekspert for å følge denne guiden, men du bør kjenne til grunnleggende REST-API-utvikling. Her er det du må ha installert før du starter:
- Node.js 22 LTS eller nyere (sjekk med
node -v) - npm, som følger med Node.js-installasjonen
- OpenSSL 3, som følger med de fleste Linux- og macOS-installasjoner
- Et terminalvindu og en kodeeditor (VS Code eller lignende)
- Grunnleggende kjennskap til Express.js og REST-API-er
- curl eller Postman for å teste API-kall
- Git for versjonskontroll (valgfritt, men anbefalt)
- En git-hemmelighetsskanner som gitleaks eller trufflehog, nyeste versjon, til steg 10
Pakkene vi bruker er jsonwebtoken for å signere og verifisere tokens, express og express-rate-limit for API-rammeverket og rammeverk for hastighetsbegrensning. Installer alltid nyeste versjon via npm i stedet for å låse til et bestemt versjonsnummer, siden sikkerhetsrettelser i disse bibliotekene kommer ofte. Sjekk npm sin side for hver pakke rett før du starter, slik at du ikke bygger på en kjent sårbar versjon.
Noen team foretrekker biblioteket jose i stedet for jsonwebtoken, siden jose har bredere støtte for moderne nøkkelformater og kjører like godt i nettleser, Node.js og edge-kjøretider som Cloudflare Workers eller Deno Deploy. Begge bibliotekene løser samme kjerneproblem, signering og verifisering av JWT, og prinsippene i denne guiden gjelder uavhengig av hvilket du velger. Vi bruker jsonwebtoken her fordi API-et er enklere å lese for de som ikke har jobbet med JWT før, men bytt gjerne til jose hvis prosjektet ditt allerede kjører i et edge-miljø.
Steg 1-3: Sett opp prosjektet og generer nøkkelpar
Start med et rent prosjekt. Steg 1 er å initialisere mappen og installere avhengighetene. Steg 2 er å velge mappestruktur som skiller nøkler, middleware og ruter. Steg 3 er å generere et asymmetrisk nøkkelpar, som er selve grunnlaget for sikker token-signering.
mkdir jwt-api-sikkerhet && cd jwt-api-sikkerhet
npm init -y
npm install express jsonwebtoken express-rate-limit dotenv
mkdir keys middleware routes logs
Deretter genererer du et RSA-nøkkelpar med OpenSSL. Bruk minst 2048 bits, men 4096 bits gir mer sikkerhetsmargin for langlevde signeringsnøkler. Den private nøkkelen skal aldri ligge i samme repo som koden din, og den skal aldri sjekkes inn i Git.
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/private.pem
openssl rsa -pubout -in keys/private.pem -out keys/public.pem
chmod 600 keys/private.pem
echo "keys/private.pem" >> .gitignore
I produksjon bør den private nøkkelen ligge i en hemmelighetstjeneste som AWS Secrets Manager, Azure Key Vault eller GCP Secret Manager, ikke på disk i det hele tatt. For dette eksempelprosjektet leser vi filen lokalt, men koden er skrevet slik at du enkelt kan bytte til en hemmelighetstjeneste senere uten å endre resten av applikasjonen.
Steg 4-5: Velg signeringsalgoritme og utsted korte access-tokens
Valg av algoritme er kanskje den viktigste beslutningen i hele oppsettet. IR 8587 peker konkret på digitalt signerte og asymmetrisk krypterte tokens, og ber implementere avvisning av svake algoritmer og tvungen bruk av forventet signeringsalgoritme. Tabellen under sammenligner de tre vanligste valgene for JWT-signering.
| Algoritme | Nøkkeltype | Typisk bruk | Styrke | Svakhet |
|---|---|---|---|---|
| HS256 | Delt nøkkel (symmetrisk) | Interne tjenester med full tillit | Raskt, enkelt | Samme nøkkel kan både signere og verifisere, høy risiko ved lekkasje |
| RS256 | RSA-nøkkelpar (asymmetrisk) | API-er med flere verifiserende klienter | Privat nøkkel deles aldri med verifiserere | Krever nøkkelrotasjon og god nøkkellagring |
| ES256 | Elliptisk kurve (asymmetrisk) | Mobil- og IoT-API-er med begrenset CPU | Mindre signaturer, lavere CPU-bruk enn RSA | Noe mer kompleks implementasjon |
For de fleste API-er med eksterne klienter er RS256 eller ES256 riktig valg, fordi verifisering kan skje med den offentlige nøkkelen uten at noen klient får tilgang til signeringsnøkkelen. Bruk aldri HS256 for API-er der mer enn ett team eller én tjeneste verifiserer tokenet, siden det tvinger deg til å dele en hemmelighet bredere enn nødvendig. Under ser du hvordan du utsteder et kortlevd access-token med RS256.
Scope-feltet i eksempelet under er med vilje smalt. En vanlig feil er å designe scopes som “admin” eller “full-access”, som gir alt-eller-ingenting-tilgang og tvinger deg til å stole blindt på hele tokenet. Design heller scopes per handling, som konto:les og konto:skriv, slik at et token kun kan brukes til nøyaktig det klienten faktisk trenger. Dette prinsippet kalles minste privilegium, og det er samme logikk som IAM-revisjonen i steg 11 bygger på for skyressurser.
const jwt = require('jsonwebtoken');
const fs = require('fs');
const privateKey = fs.readFileSync('keys/private.pem');
function issueAccessToken(userId, scopes) {
return jwt.sign(
{ sub: userId, scope: scopes.join(' ') },
privateKey,
{
algorithm: 'RS256',
issuer: 'api.dittfirma.no',
audience: 'dittfirma-api',
expiresIn: '10m',
jwtid: crypto.randomUUID()
}
);
}
Legg merke til at access-tokenet lever i bare 10 minutter. Korte levetider begrenser skaden hvis et token likevel lekker, siden angriperen får et smalt tidsvindu å utnytte det i. De fleste API-er klarer seg fint med 5 til 15 minutters levetid på access-tokens, mens langsiktig tilgang håndteres med et separat refresh-token, som vi bygger i neste steg.
Steg 6-7: Valider pålagte claims og bygg refresh-token-rotasjon
NIST er direkte på dette punktet: “As such, all tokens and assertions MUST include explicit audience fields, and all access control mechanisms MUST reject tokens with incorrect or missing audience restrictions.” En vanlig svakhet i egenbygde API-er er at man verifiserer signaturen, men hopper over å sjekke issuer, audience, utløpstid og algoritme. Middleware under avviser et token hvis noe av dette mangler eller ikke stemmer.
const jwt = require('jsonwebtoken');
const publicKey = fs.readFileSync('keys/public.pem');
function validateToken(req, res, next) {
const header = req.headers.authorization || '';
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
if (!token) return res.status(401).json({ error: 'mangler token' });
try {
const payload = jwt.verify(token, publicKey, {
algorithms: ['RS256'],
issuer: 'api.dittfirma.no',
audience: 'dittfirma-api',
clockTolerance: 5
});
req.user = payload;
next();
} catch (err) {
return res.status(401).json({ error: 'ugyldig token', detail: err.name });
}
}
Merk at algorithms: ['RS256'] er satt eksplisitt. Uten denne linjen kan et bibliotek i verste fall godta et token signert med en annen algoritme enn forventet, et klassisk angrep kalt algoritme-forveksling. Nå bygger vi refresh-token-rotasjon, som gir brukeren lang tilgang uten at et langlevd token flyter rundt i nettleseren eller appen.
const refreshStore = new Map(); // bytt til Redis i produksjon
function issueRefreshToken(userId) {
const token = crypto.randomUUID();
refreshStore.set(token, { userId, used: false, createdAt: Date.now() });
return token;
}
function rotateRefreshToken(oldToken) {
const record = refreshStore.get(oldToken);
if (!record || record.used) {
throw new Error('refresh-token gjenbrukt eller ugyldig');
}
record.used = true; // gammelt token kan aldri brukes igjen
const newToken = issueRefreshToken(record.userId);
return { userId: record.userId, newToken };
}
Prinsippet heter refresh-token-rotasjon: hver gang et refresh-token brukes, blir det ugyldiggjort og byttet ut med et nytt. Hvis du ser at et allerede brukt refresh-token dukker opp igjen, er det et sikkert tegn på at tokenet er stjålet og brukt av to parter samtidig. Da bør du tilbakekalle hele token-familien til brukeren umiddelbart, ikke bare det ene tokenet.
Steg 8: Tilbakekall tokens uavhengig av passord
En av de mest overraskende detaljene fra sikkerhetshendelsene i 2026 er at lekkede API-nøkler fortsatte å fungere selv etter at brukeren byttet passord. Det skjer fordi mange systemer kun kobler tilgang til passordhash, ikke til tokenets egen levetid eller en separat tilbakekallingsliste. Løsningen er en tilbakekallingsmekanisme som virker helt uavhengig av passordlogikken.
const revokedJtis = new Set(); // bytt til Redis med TTL i produksjon
function revokeToken(jti) {
revokedJtis.add(jti);
}
function checkRevocation(req, res, next) {
if (revokedJtis.has(req.user.jti)) {
return res.status(401).json({ error: 'token er tilbakekalt' });
}
next();
}
Legg checkRevocation etter validateToken i middleware-kjeden, slik at du først bekrefter at signaturen er gyldig, og deretter sjekker om nettopp dette tokenet er trukket tilbake. Bruk en TTL i Redis som matcher den lengste mulige levetiden på et access-token, slik at tilbakekallingslisten ikke vokser i det uendelige. Tilbakekall alltid hele settet av tokens knyttet til en bruker ved mistanke om kompromittering, ikke bare det ene tokenet du så bli misbrukt. Gi også brukeren selv mulighet til å se og trekke tilbake egne aktive sesjoner fra en konto-side, på samme måte som de fleste store nettjenester lar deg logge ut andre enheter manuelt.
Steg 9: Sett opp rate limiting per bruker, klient og endepunkt
Uten hastighetsbegrensning kan et stjålet token brukes til å trekke ut enorme mengder data før du merker noe som helst. express-rate-limit gir deg en enkel, men effektiv barriere. Sett ulike grenser for ulike endepunkter, siden et påloggingsendepunkt tåler færre forsøk per minutt enn en vanlig datalesing. Et endepunkt som eksporterer store datamengder bør ha en enda strengere grense enn et vanlig lese-endepunkt, siden det er nøyaktig den typen funksjon angripere leter etter når de først har fått tak i et gyldig token.
const rateLimit = require('express-rate-limit');
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
keyGenerator: (req) => req.ip,
message: { error: 'for mange innloggingsforsøk' }
});
const apiLimiter = rateLimit({
windowMs: 60 * 1000,
max: 100,
keyGenerator: (req) => req.user?.sub || req.ip
});
app.post('/login', loginLimiter, loginHandler);
app.use('/api', validateToken, checkRevocation, apiLimiter);
Merk at keyGenerator for det autentiserte endepunktet bruker bruker-ID fra tokenet, ikke bare IP-adressen. Det stopper en angriper som roterer mellom flere IP-adresser, men fortsatt bruker samme stjålne token. Hvis du drifter API-et bak flere instanser, må raten telles i en delt butikk som Redis, ikke i minnet på én enkelt server.
Steg 10: Fjern hardkodede nøkler og skann for lekkasjer
Rapporten fra Passwork sin sikkerhetsoppsummering for juli 2026 beskrev et tilfelle der en kontraktør ved et uhell la ut 844 MB med data åpent på GitHub, inkludert administrative passord for AWS GovCloud og klartekstfiler med innloggingsdetaljer til interne systemer. Den typen lekkasje skjer stort sett fordi nøkler ligger hardkodet i kildekode, miljøfiler eller CI-logger i stedet for i en egen hemmelighetstjeneste.
# Skann hele git-historikken etter lekkede nøkler og tokens
gitleaks detect --source . --log-opts="--all" --report-path gitleaks-report.json
# Skann container-images for hemmeligheter før push til registry
trufflehog docker --image=dittfirma-api:latest
Kjør skanningen som en del av CI-pipelinen, ikke bare manuelt før release. Hvis skanneren finner en treff, roter den aktuelle nøkkelen umiddelbart, selv om du tror lekkasjen var kortvarig eller aldri nådd produksjon. Anta alltid at en nøkkel som har vært synlig i en logg eller et offentlig repo er kompromittert, og behandle den som det inntil den er trukket tilbake.
Steg 11: Revider IAM-tillatelser i skyen
JWT-validering hjelper lite hvis tjenestekontoen som utsteder tokenet selv har for mange tillatelser i skyen. Den samme statistikken fra Stingrai sin skysikkerhetsindeks viser at svake IAM-kontroller rammer de store skyleverandørene nesten likt. Rapporten fra CISA og NIST peker også på to konkrete sårbarheter som understreker hvor stor skade feilkonfigurert identitetshåndtering kan gjøre.
| CVE | Produkt | CVSS | Problem |
|---|---|---|---|
| CVE-2026-20316 | Cisco Secure Firewall Management Center | Ikke angitt i rapportering | Statiske, hardkodede innloggingsdetaljer, lagt til CISAs KEV-katalog i 2026 |
| CVE-2026-20079 | Cisco Secure Firewall Management Center | 10,0 (kritisk) | Rapportert sammen med CVE-2026-20316 i samme KEV-oppføring |
| CVE-2025-29827 | Azure Automation | 9,9 (kritisk) | Offentlig-som-standard endepunkt gir tilgang til administrerte identiteter tvers av leietakere |
Bruk denne sjekklisten når du reviderer IAM i egen skyomgivelse: fjern alle administratorkontoer uten tidsbegrenset tilgang, aktiver phishing-resistent MFA for alle brukere og tjenestekontoer, fjern nøkler som ikke har vært brukt de siste 90 dagene, og kontroller at ingen tjenestekonto har bredere tilgang enn det API-et faktisk bruker. Et godt sted å starte er å liste alle IAM-roller knyttet til token-utstedelsen og fjerne alt som ikke er strengt nødvendig for den funksjonen alene.
Mange team gjør denne revisjonen én gang ved oppsett og glemmer den helt etterpå, selv om tilgangsbehovet endrer seg hver gang en ny funksjon legges til. Sett derfor en fast kalenderpåminnelse, for eksempel hvert kvartal, for å gå gjennom IAM-rollene på nytt. sikkerhetsselskapet Ox Security peker på at feilkonfigurert IAM, ikke utnyttede programvaresårbarheter, er den vanligste veien inn ved skykompromittering, noe som gjør denne typen periodisk revisjon til en av de mest kostnadseffektive sikkerhetstiltakene du kan gjøre for et API som bruker JWT mot skyressurser.
Steg 12: Legg til replay-beskyttelse med jti og nonce
Et gyldig, usignert-endret token kan likevel misbrukes hvis en angriper fanger opp og gjenbruker samme forespørsel. Dette kalles et replay-angrep, og det var blant de mest rapporterte angrepsformene i september 2026 ifølge gjennomgangen av perioden. Løsningen er å gi hvert token en unik identifikator (jti) og spore hvilke identifikatorer som allerede er brukt til sensitive operasjoner.
const usedJtis = new Set(); // bytt til Redis med kort TTL
function preventReplay(req, res, next) {
const jti = req.user.jti;
if (usedJtis.has(jti) && req.method !== 'GET') {
return res.status(409).json({ error: 'forespørsel allerede behandlet' });
}
usedJtis.add(jti);
next();
}
Denne beskyttelsen er spesielt viktig for endepunkter som endrer data, som betalinger, overføringer eller kontoendringer. For lesing (GET) er replay normalt ufarlig, men for skriving bør du alltid kombinere jti-sporing med kort levetid på tokenet, slik at tidsvinduet for et vellykket replay-angrep blir så lite som mulig.
Steg 13: Overvåk og varsle om token-misbruk
Du kan ikke stoppe det du ikke ser. Logg alle mislykkede signaturvalideringer, uvanlig token-gjenbruk, geografisk umulige reiser mellom påloggingssteder og plutselige endringer i tilganger (scopes). Følgende middleware logger mislykkede valideringer strukturert, slik at de kan sendes videre til et SIEM-system eller en enkel varslingstjeneste. Selv et lite team uten eget sikkerhetsovervåkingssystem bør minst ha disse loggene tilgjengelig i et søkbart format, slik at de kan gås gjennom manuelt dagen etter en hendelse er varslet av noe annet, for eksempel en bruker som rapporterer unormal aktivitet på egen konto.
function logFailedAuth(err, req) {
const entry = {
timestamp: new Date().toISOString(),
ip: req.ip,
path: req.path,
reason: err.name,
userAgent: req.headers['user-agent']
};
fs.appendFileSync('logs/auth-failures.log', JSON.stringify(entry) + '\n');
if (countRecentFailures(req.ip) > 10) {
triggerAlert('mulig brute-force mot token-validering fra ' + req.ip);
}
}
Eksempel på en typisk logglinje når overvåkingen fanger et forsøk på å bruke et token med feil algoritme, som vist under. Dette er nøyaktig den typen hendelse IR 8587 ber organisasjoner fange opp og reagere på.
{"timestamp":"2026-10-15T09:12:44.001Z","ip":"203.0.113.42","path":"/api/konto","reason":"JsonWebTokenError","userAgent":"curl/8.9"}
Steg 14: Test hele oppsettet mot en reell angrepssimulering
Siste steg er å verifisere at alt faktisk virker som planlagt, ikke bare i teorien. Test både det positive sporet (gyldig token gir tilgang) og de negative sporene (ugyldig signatur, feil audience, utløpt token og tilbakekalt token skal alle avvises). Start API-et og kjør disse kommandoene.
# Gyldig token, skal gi 200
curl -H "Authorization: Bearer $GYLDIG_TOKEN" http://localhost:3000/api/konto
# Manipulert signatur, skal gi 401
curl -H "Authorization: Bearer $GYLDIG_TOKEN-tuklet" http://localhost:3000/api/konto
# Tilbakekalt token, skal gi 401 selv om signaturen er gyldig
curl -H "Authorization: Bearer $TILBAKEKALT_TOKEN" http://localhost:3000/api/konto
Forventet utskrift for det manipulerte tokenet ser slik ut: {"error":"ugyldig token","detail":"JsonWebTokenError"} med HTTP-status 401. Hvis du i stedet får 200 på et manipulert eller utløpt token, har validateToken-middlewaren en feil, og du må gå tilbake til steg 6 før du går videre. Gjør denne testen til en del av CI-pipelinen, slik at en fremtidig kodeendring ikke svekker valideringen uten at noen merker det.
Bygg valideringen inn i CI/CD-pipelinen
Et oppsett som bare er testet manuelt på din egen maskin, holder sjeldent lenge. Hver gang en kollega endrer middleware-rekkefølgen eller bytter ut en pakke, bør testene fra steg 14 og hemmelighetsskanningen fra steg 10 kjøre automatisk før koden når produksjon. Under ser du et minimalt eksempel på hvordan dette kan se ut i en GitHub Actions-workflow.
name: token-sikkerhet
on: [pull_request]
jobs:
sjekk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm test
- name: skann etter lekkede nøkler
run: gitleaks detect --source . --log-opts="--all"
Merk fetch-depth: 0, som sørger for at gitleaks får hele git-historikken å skanne, ikke bare den siste commiten i pull requesten. Sett pipelinen til å feile (ikke bare varsle) hvis gitleaks finner en treff eller hvis testene fra steg 14 mislykkes. En pipeline som bare logger en advarsel uten å stoppe merge, blir fort ignorert over tid, og da har du i praksis ingen beskyttelse igjen.
Vanlige fallgruver ved JWT- og API-token-implementasjon
- Ingen eksplisitt algoritme-liste ved verifisering. Uten
algorithms: ['RS256']kan biblioteket i teorien godta andre algoritmer enn du forventer. - For lange levetider på access-tokens. Et token som lever i timer eller dager gir angriperen et stort tidsvindu etter en lekkasje.
- Tilbakekalling koblet kun til passordbytte. Som vist i hendelsene fra 2026 fortsetter lekkede nøkler å virke hvis tilbakekalling ikke er en egen mekanisme.
- Hemmeligheter i miljøvariabler uten rotasjonsplan. En .env-fil uten rutine for rotasjon blir en varig svakhet, ikke en midlertidig løsning.
- Manglende audience-sjekk. Et token utstedt for én tjeneste kan da misbrukes mot en helt annen tjeneste som deler samme signeringsnøkkel.
- Rate limiting kun på IP-adresse. Det stopper ikke en angriper som roterer IP, men fortsatt bruker samme stjålne token.
- Ingen logging av mislykkede valideringer. Uten logger ser du aldri at noe er under angrep før skaden er skjedd.
- For brede scopes på hvert token. Et token som gir tilgang til alt brukeren kan gjøre, gjør en enkelt lekkasje langt mer skadelig enn nødvendig.
- Samme signeringsnøkkel delt mellom test og produksjon. En lekkasje i et testmiljø med lavere sikkerhet kan da ramme produksjonssystemet direkte.
Feilsøking: de vanligste problemene og løsningene
Her er de problemene utviklere oftest møter når de setter opp JWT-validering i praksis, med konkret årsak og løsning for hvert av dem.
| Problem | Sannsynlig årsak | Løsning |
|---|---|---|
| jwt.verify kaster “invalid signature” | Feil nøkkelfil lest inn, eller nøkkelpar byttet ut uten at API-et ble restartet | Bekreft at public.pem matcher private.pem brukt til signering, restart prosessen |
| Token godtas selv etter utløp | clockTolerance satt for høyt, eller serverklokken er feil | Synkroniser serverklokken med NTP, reduser clockTolerance til få sekunder |
| “jwt audience invalid” på gyldige klienter | Flere API-versjoner bruker ulik audience-verdi | Standardiser audience-verdien på tvers av alle tjenester som utsteder og verifiserer |
| Refresh-token fungerer bare én gang per bruker | Flere faner eller enheter bruker samme refresh-token samtidig | Utsted separate refresh-tokens per enhet eller sesjon, ikke ett per bruker |
| Rate limiting treffer legitime brukere | Delt IP-adresse bak NAT eller bedriftsproxy | Bruk bruker-ID fra tokenet som nøkkel i rate limiter, ikke bare IP |
| Tilbakekalte tokens godtas likevel | checkRevocation kjører ikke i riktig rekkefølge i middleware-kjeden | Sett checkRevocation direkte etter validateToken, før ruten selv |
| Hemmelighetsskanner finner ingenting, men lekkasje skjedde | Skanneren kjørte ikke på full git-historikk, bare siste commit | Kjør med –log-opts=”–all” eller skann hele historikken manuelt |
| Høy CPU-bruk på token-verifisering ved høy trafikk | RSA-verifisering er tyngre enn nødvendig for mobile eller IoT-klienter | Bytt til ES256 for klienter med begrenset CPU-kapasitet |
| Ulike team bruker ulik signeringsnøkkel for samme tjeneste | Mangler sentral nøkkelhåndtering mellom mikrotjenester | Flytt nøkler til en delt hemmelighetstjeneste med versjonert tilgang |
Avanserte tips for produksjonsmiljø og skalering
Når oppsettet først virker lokalt, er neste utfordring å gjøre det robust i produksjon med flere instanser og høy trafikk. Her er noen tips som sparer deg for feilsøking senere. Bruk JWKS (JSON Web Key Set) i stedet for å distribuere en statisk offentlig nøkkelfil til hver tjeneste. Det gjør nøkkelrotasjon mulig uten nedetid, siden verifiserende tjenester kan hente ny nøkkel automatisk fra et endepunkt.
Sett opp automatisk nøkkelrotasjon med overlapp, der den gamle nøkkelen fortsatt godtas i en kort periode etter at en ny tas i bruk. Det unngår at alle utstedte tokens blir ugyldige samtidig som du roterer. Et JWKS-endepunkt (JSON Web Key Set) eksponerer de offentlige nøklene dine på en standardisert URL, slik at verifiserende tjenester kan hente ny nøkkel automatisk i stedet for at noen må kopiere en fil manuelt mellom miljøer.
app.get('/.well-known/jwks.json', (req, res) => {
res.json({
keys: [
{ kid: 'nokkel-2026-10', kty: 'RSA', use: 'sig', alg: 'RS256', n: '...', e: 'AQAB' },
{ kid: 'nokkel-2026-07', kty: 'RSA', use: 'sig', alg: 'RS256', n: '...', e: 'AQAB' }
]
});
});
Hver nøkkel får et kid (key ID), og hvert utstedt token inneholder samme kid i headeren. Da kan den verifiserende tjenesten slå opp riktig offentlig nøkkel automatisk, selv om du har flere aktive nøkler samtidig under en rotasjon. For tjenester som håndterer AI-agenter, som NIST eksplisitt nevner i IR 8587, bør du vurdere kortere levetider og strengere scope-begrensning enn for vanlige menneskelige brukere, siden agenter ofte opererer automatisert og uten en person som merker unormal aktivitet. Sett gjerne agent-tokens til 2-5 minutters levetid og et enda smalere scope enn det du ville gitt et menneske som logger inn manuelt.
Til slutt: sett opp automatisk varsling når en signeringsnøkkel endres uventet, når en tjenestekonto plutselig får nye tillatelser, eller når antall mislykkede valideringer fra en enkelt klient stiger kraftig. Disse tre signalene fanger opp de aller fleste reelle angrepsmønstrene beskrevet i sikkerhetshendelsene gjennom 2026.
Det komplette prosjektet og oppsummering
Når du har fulgt alle 14 stegene, har du en mappe med følgende struktur: keys/ med privat og offentlig nøkkel (aldri sjekket inn i Git), middleware/ med validateToken, checkRevocation og preventReplay, routes/ med login- og API-endepunktene dine, og logs/ med strukturerte logger for mislykkede valideringer. Hovedfilen binder alt sammen ved å kjede middleware i riktig rekkefølge: først rate limiting på login, deretter token-validering, så tilbakekallingssjekk, så replay-beskyttelse, og til sist den faktiske API-logikken.
Legg merke til at ingen av de 14 stegene krever dyre verktøy eller kommersielle lisenser. Alt som er brukt her, jsonwebtoken, express-rate-limit, OpenSSL og gitleaks, er fritt tilgjengelig og allerede i vanlig bruk i de fleste Node.js-prosjekter. Det som gjør oppsettet sikkert, er ikke verktøyene i seg selv, men at de kobles sammen i riktig rekkefølge og faktisk testes etter at de er på plass. Et team som allerede bruker JWT, men mangler ett eller to av disse stegene, bør prioritere tilbakekalling og rate limiting først, siden det er de to kontrollene som demper skaden raskest hvis noe uansett går gjennom.
Dette er ikke en engangsøvelse. Signeringsnøkler bør roteres regelmessig, tilbakekallingslisten bør overvåkes for uvanlig vekst, og hele testsuiten fra steg 14 bør kjøres i hver pipeline. Målet er ikke bare å overleve neste revisjon, men å ha et oppsett som faktisk motstår de angrepsmønstrene CISA og NIST beskriver i IR 8587: forfalskede tokens, gjenbrukte sesjoner og lekkede nøkler som fortsetter å virke lenge etter at de skulle vært døde.
Ofte stilte spørsmål om JWT- og API-token-sikkerhet
Er JWT usikkert i seg selv?
Nei. JWT er bare et format for signerte tokens. Sikkerheten avhenger helt av hvordan du signerer, validerer og tilbakekaller dem, slik vi har gått gjennom i denne guiden.
Bør jeg bruke HS256 eller RS256?
Bruk RS256 eller ES256 så snart mer enn én tjeneste eller ett team skal verifisere tokenet. HS256 passer kun når signering og verifisering skjer i samme, fullstendig tiltrodde tjeneste.
Hvor lenge bør et access-token leve?
Mellom 5 og 15 minutter er vanlig for de fleste API-er. Langsiktig tilgang skal håndteres med et separat, roterende refresh-token, ikke ved å forlenge access-tokenets levetid.
Hva gjør jeg hvis et refresh-token blir stjålet?
Hvis du har implementert rotasjon riktig, oppdager du det fordi det stjålne tokenet før eller senere brukes etter at det allerede er byttet ut. Da må du tilbakekalle hele token-familien til brukeren, ikke bare det ene tokenet.
Må jeg implementere alt i IR 8587 selv om jeg ikke er et amerikansk føderalt byrå?
Nei, veiledningen er formelt rettet mot amerikanske føderale systemer og deres skyleverandører. Prinsippene (audience-validering, kort levetid, uavhengig tilbakekalling, overvåking) er likevel god praksis for alle som driver API-er, uavhengig av jurisdiksjon.
Hvordan vet jeg om en API-nøkkel allerede er lekket?
Kjør en hemmelighetsskanner som gitleaks over hele git-historikken, ikke bare siste commit, og sjekk CI-logger og container-images. Anta at en nøkkel som har vært synlig offentlig er kompromittert, selv om du ikke finner bevis på misbruk.
Er JWKS nødvendig for et lite API med én tjeneste?
Ikke strengt nødvendig, men det gjør fremtidig nøkkelrotasjon langt enklere. For API-er som forventes å vokse eller få flere verifiserende klienter, lønner det seg å sette opp JWKS fra start.
Hvor finner jeg selve IR 8587-dokumentet?
Veiledningen er publisert av CISA og NIST, og kunngjøringen med lenke til fullstendig dokument ligger på CISAs offisielle nyhetsside. Infosecurity Magazine har også en god oppsummering av innholdet, og NIST sin presentasjon av rapporten går gjennom kravene i mer teknisk detalj.
Hva er forskjellen mellom autentisering og autorisasjon i et JWT?
Autentisering bekrefter hvem brukeren er, og skjer typisk ved innlogging før tokenet i det hele tatt utstedes. Autorisasjon handler om hva den innloggede brukeren får gjøre, og styres av scope- og rolle-feltene inni selve tokenet. Et gyldig signert token beviser bare at utstederen en gang bekreftet identiteten, ikke at brukeren fortsatt skal ha akkurat den tilgangen i dag, derfor er kort levetid og tilbakekalling så viktig.
Bør jeg lagre access-token i localStorage eller en cookie?
En httpOnly-cookie med SameSite satt til Strict eller Lax er normalt tryggere enn localStorage, siden httpOnly-cookies ikke er tilgjengelige fra JavaScript og dermed tåler et cross-site scripting-angrep bedre. Hvis du må bruke localStorage, for eksempel i en mobilapp uten cookie-støtte, blir kort levetid og streng Content Security Policy enda viktigere som kompenserende kontroller. Uansett hvilket lagringssted du velger, gjelder de samme 14 stegene fra resten av guiden, siden valideringen og tilbakekallingen skjer på serveren, ikke i nettleseren.
