{"licence":{"name":"CC BY-SA 4.0","spdx":"CC-BY-SA-4.0","url":"https://creativecommons.org/licenses/by-sa/4.0/","attribution":"Atlas, a bilingual technical dictionary (https://atlas.maintz.dev/)"},"id":"security/csrf-token","url":{"en":"https://atlas.maintz.dev/en/terms/security/csrf-token/","da":"https://atlas.maintz.dev/da/terms/security/csrf-token/"},"term":{"en":"CSRF token","da":"CSRF-token"},"aka":{"en":["anti-CSRF token","synchronizer token"],"da":["anti-CSRF-token"]},"domain":["security"],"cluster":"application-security","layer":"application","status":"current","summary":{"en":"A secret, hard-to-guess value a site puts in its own forms and checks on every change, so requests started by other sites fail.","da":"En hemmelig værdi, som et site lægger i sine egne formularer og tjekker ved hver ændring, så forespørgsler fra andre sites afvises."},"body":{"formal":{"en":"A random value the server ties to the user's session and places in each page or form it sends; any request that changes data must return the value, and the server refuses requests where it is missing or wrong.","da":"En tilfældig værdi, som serveren knytter til brugerens session og lægger i hver side eller formular, den sender; enhver forespørgsel, der ændrer data, skal sende værdien tilbage, og serveren afviser forespørgsler, hvor den mangler eller er forkert."},"plain":{"en":"Like a shop that stamps a one-off number on every order form it hands out, and only accepts forms that come back with the number it gave you.","da":"Som en butik, der stempler et engangsnummer på hver bestillingsseddel, den udleverer, og kun godtager sedler, der kommer tilbage med det nummer, den gav dig."},"inPractice":{"en":"A pension fund's member portal puts a hidden field with a fresh value in its form for changing the payout account; a forged change sent from another site carries the login cookie but not the value, so the portal rejects it.","da":"En pensionskasses medlemsportal lægger et skjult felt med en ny værdi i formularen til at ændre udbetalingskonto; en forfalsket ændring fra et andet site har cookien fra login med, men ikke værdien, så portalen afviser den."},"whyItMatters":{"en":"The browser sends cookies by itself, so a cookie alone cannot prove the user meant to act; without a second proof, any site the user visits can act in their name.","da":"Browseren sender selv cookies med, så en cookie alene beviser ikke, at brugeren ville handle; uden et ekstra bevis kan ethvert site, brugeren besøger, handle i vedkommendes navn."}},"deepDive":{"en":"The synchronizer token pattern is the stateful form: the server generates a large unpredictable value with a cryptographically secure random number generator, stores it in the server-side session and embeds it in every form as a hidden field or exposes it to JavaScript through a meta tag. On every unsafe request (POST, PUT, PATCH, DELETE) the server compares the submitted value with the stored one using a constant-time comparison and rejects the request on mismatch or absence. Per-request tokens shorten the window in which a leaked token is useful, and OWASP rates them as more secure, but they break the back button and multiple open tabs, so per-session tokens are the common default.\n\nStateless applications use the double-submit cookie pattern: the token is sent both as a cookie and as a request parameter or header, and the server checks that the two match. The naive version is weak, because an attacker who can write cookies for the domain, for example from a compromised or vulnerable subdomain or through a man-in-the-middle on plain HTTP, can plant a matching pair. OWASP therefore recommends the signed double-submit variant, where the token is an HMAC, keyed with a server secret, over a session-bound value that changes at each login and a random value, so a planted cookie fails verification. Cookie name prefixes such as __Host- further prevent subdomains from overwriting the cookie.\n\nWhere the token travels matters. It must never appear in a URL, since URLs leak through Referer headers, logs and browser history. Single-page applications typically read it from a cookie or an endpoint and send it in a custom header such as X-CSRF-Token or X-XSRF-TOKEN (the convention Angular's HttpClient uses). Frameworks mask the token on each render, XOR-ing it with a fresh random pad as Django and Rails do, so that the value in an HTTPS-compressed response cannot be recovered with a BREACH-style length side channel.\n\nA CSRF token is not an authentication secret and offers no protection against XSS: any script running in the same origin can read it from the DOM. It also does nothing for requests authenticated with a bearer token in an Authorization header, which browsers do not attach automatically, so APIs that do not use cookies usually do not need one. Regenerating the token when the user logs in avoids session fixation-style reuse of a token obtained before authentication.","da":"Synchronizer token-mønstret er den tilstandsfulde form: serveren genererer en stor, uforudsigelig værdi med en kryptografisk sikker tilfældighedsgenerator, gemmer den i sessionen på serversiden og indlejrer den i hver formular som skjult felt eller gør den tilgængelig for JavaScript via et meta-tag. Ved hver usikker forespørgsel (POST, PUT, PATCH, DELETE) sammenligner serveren den indsendte værdi med den gemte med en sammenligning i konstant tid og afviser forespørgslen, hvis værdien mangler eller ikke passer. Tokens pr. forespørgsel forkorter det tidsrum, hvor et lækket token kan bruges, og OWASP vurderer dem som mere sikre, men de ødelægger tilbageknappen og flere åbne faner, så tokens pr. session er det almindelige valg.\n\nTilstandsløse applikationer bruger double-submit-cookie-mønstret: tokenet sendes både som cookie og som parameter eller header, og serveren tjekker, at de to er ens. Den naive udgave er svag, fordi en angriber, der kan skrive cookies til domænet, fx fra et kompromitteret eller sårbart subdomæne eller via man-in-the-middle på ukrypteret HTTP, kan plante et matchende par. OWASP anbefaler derfor den signerede variant, hvor tokenet er en HMAC med en hemmelig servernøgle over en sessionsbundet værdi, der skifter ved hvert login, og en tilfældig værdi, så en plantet cookie ikke består verifikationen. Cookie-præfikser som __Host- forhindrer desuden subdomæner i at overskrive cookien.\n\nHvor tokenet sendes, har betydning. Det må aldrig stå i en URL, da URL'er lækker via Referer-headers, logs og browserhistorik. Single page-applikationer læser det typisk fra en cookie eller et endpoint og sender det i en brugerdefineret header som X-CSRF-Token eller X-XSRF-TOKEN (den konvention, Angulars HttpClient bruger). Frameworks maskerer tokenet ved hver rendering, hvor det XOR'es med en ny tilfældig værdi, som Django og Rails gør, så værdien i et komprimeret HTTPS-svar ikke kan genskabes via en længdebaseret sidekanal af BREACH-typen.\n\nEt CSRF-token er ikke en autentificeringshemmelighed og beskytter ikke mod XSS: ethvert script, der kører i samme origin, kan læse det fra DOM'en. Det gør heller intet for forespørgsler, der autentificeres med et bearer token i Authorization-headeren, fordi browsere ikke sender den med automatisk, så API'er uden cookies har som regel ikke brug for et. At generere et nyt token, når brugeren logger ind, forhindrer, at et token hentet før autentificering genbruges på samme måde som ved session fixation."},"howTo":{"steps":{"en":["List every request that changes state (POST, PUT, PATCH, DELETE), and make sure no GET request changes data.","Turn on the framework's built-in CSRF protection, such as the anti-forgery tokens in ASP.NET Core, Django or Spring Security, instead of writing your own.","Generate the token with a cryptographically secure random generator, tie it to the user's session and issue a new one when the user logs in.","Put the token in a hidden form field or, in a single-page app, send it in a custom header such as X-CSRF-Token, and never put it in a URL.","Check the token on the server for every state-changing request with a constant-time comparison, and reject the request when it is missing or wrong.","If the application is stateless, use the signed double-submit cookie pattern with an HMAC bound to the session, and give the cookie the __Host- prefix.","Add defence in depth with SameSite=Lax or Strict on session cookies and a check of the Fetch Metadata or Origin headers on unsafe requests.","Test that a forged request from another origin is rejected, and keep that test in the automated suite so a new endpoint cannot skip the check."],"da":["Lav en liste over alle forespørgsler, der ændrer tilstand (POST, PUT, PATCH, DELETE), og sørg for, at ingen GET-forespørgsel ændrer data.","Slå frameworkets indbyggede CSRF-beskyttelse til, fx anti-forgery tokens i ASP.NET Core, Django eller Spring Security, i stedet for at skrive jeres egen.","Generér tokenet med en kryptografisk sikker tilfældighedsgenerator, knyt det til brugerens session, og udsted et nyt, når brugeren logger ind.","Læg tokenet i et skjult formularfelt eller, i en single page-app, send det i en brugerdefineret header som X-CSRF-Token, og læg det aldrig i en URL.","Tjek tokenet på serveren ved hver forespørgsel, der ændrer tilstand, med en sammenligning i konstant tid, og afvis forespørgslen, hvis det mangler eller er forkert.","Er applikationen tilstandsløs, så brug den signerede double-submit-cookie med en HMAC bundet til sessionen, og giv cookien præfikset __Host-.","Tilføj et ekstra forsvarslag med SameSite=Lax eller Strict på sessionscookies og et tjek af Fetch Metadata- eller Origin-headers på usikre forespørgsler.","Test, at en forfalsket forespørgsel fra en anden origin bliver afvist, og hold testen i den automatiske testsuite, så et nyt endpoint ikke kan springe tjekket over."]},"pitfalls":{"en":["Exempting single endpoints, such as JSON or file-upload endpoints, from the CSRF check to make a client work.","Using the naive double-submit cookie without a signature, which an attacker who controls a subdomain can defeat.","Relying on SameSite cookies alone, although requests from sibling subdomains count as same-site and still get through.","Believing the token helps against XSS, when any script running on the page can read it."],"da":["At undtage enkelte endpoints, fx JSON- eller upload-endpoints, fra CSRF-tjekket for at få en klient til at virke.","At bruge den naive double-submit-cookie uden signatur, som en angriber med kontrol over et subdomæne kan omgå.","At stole på SameSite-cookies alene, selv om forespørgsler fra søster-subdomæner tæller som same-site og stadig slipper igennem.","At tro, at tokenet hjælper mod XSS, når ethvert script, der kører på siden, kan læse det."]},"guides":[{"title":"OWASP Cheat Sheet Series - Cross-Site Request Forgery Prevention","url":"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html","publisher":"OWASP","tier":"reference"},{"title":"Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core","url":"https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery","publisher":"Microsoft","tier":"official-doc"}]},"edges":[{"type":"requires","to":"cs/session","confidence":"high","strength":"normal"},{"type":"requires","to":"cs/web-application","confidence":"high","strength":"normal"},{"type":"mitigates","to":"security/cross-site-request-forgery","why":{"en":"A forged request from another site cannot include the value, because that site cannot read the target site's pages.","da":"En forfalsket forespørgsel fra et andet site kan ikke have værdien med, fordi det site ikke kan læse målsitets sider."},"confidence":"high","strength":"primary"},{"type":"used-with","to":"cs/same-origin-policy","confidence":"high","strength":"normal"}],"depth":6,"sources":[{"title":"OWASP Cheat Sheet Series - Cross-Site Request Forgery Prevention","url":"https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html","tier":"reference","publisher":"OWASP"}],"draft":true}