javascript - CSRF-beskyttelse med CORS Origin-header vs. CSRF-token


Dette spørsmålet handler kun om beskyttelse mot Cross Site Request Forgery-angrep.

Det handler spesifikt om: Er beskyttelse via Origin-headeren (CORS) like god som beskyttelse via et CSRF-token?

Eksempel :

  • Alice er logget inn (ved å bruke en informasjonskapsel) med nettleseren sin på https://example.com. Jeg antar at hun bruker en moderne nettleser.
  • Alice besøker https://evil.example, og client-side-koden til evil.example utfører en eller annen form for forespørsel til https://example.com (klassisk CSRF-scenario).

Så :

  • Hvis vi ikke sjekker Origin-headeren (på serversiden), og ingen CSRF-token, har vi et CSRF-sikkerhetshull.
  • Hvis vi sjekker en CSRF-token, er vi trygge (men det er litt kjedelig).
  • Hvis vi sjekker Origin-headeren, bør forespørselen fra klientkoden til evil.example bli blokkert like godt som den ville gjort ved bruk av en CSRF-token - bortsett fra, hvis det på en eller annen måte er mulig for koden til evil.example å sette Origin-headeren.

Jeg vet at dette ikke burde være mulig med XHR (se f.eks. Sikkerhet for ressursdeling på tvers av opprinnelse), i det minste ikke hvis vi stoler på at W3C-spesifikasjonen blir implementert korrekt i alle moderne nettlesere (kan vi?)

Men hva med andre typer forespørsler - f.eks. innsending av skjema? Laste et script/img/... tag? Eller noen annen måte en side kan bruke for å (lovlig) lage en forespørsel? Eller kanskje noen kjent JS-hack?

Merk: Jeg snakker ikke om

  • native applikasjoner,
  • manipulerte nettlesere,
  • tverrside-skriptfeil på example.coms side,
  • ...

Svar #1

vet, at dette ikke burde være mulig med XHR (se f.eks. Sikkerhet for deling av ressurser på tvers av opprinnelse), i det minste ikke hvis vi stoler på at W3C-spesifikasjonen blir implementert korrekt i alle moderne nettlesere (kan vi?)

På slutten av dagen må du "stole på" klientens nettleser for å lagre brukerens data på en sikker måte og beskytte klient-siden av økten. Hvis du ikke stoler på klientens nettleser, bør du slutte å bruke nettet helt for noe annet enn statisk innhold. Selv ved bruk av CSRF-tokener stoler du på at klientens nettleser korrekt overholder Same Origin Policy.

Selv om det har vært tidligere nettlesersårbarheter, som for eksempel i IE 5.5/6.0, der det har vært mulig for angripere å omgå Same Origin Policy og utføre angrep, kan man vanligvis forvente at disse blir reparert så snart de oppdages, og med at de fleste nettlesere oppdateres automatisk, vil denne risikoen i stor grad bli redusert.

Men hva med andre typer forespørsler - f.eks. innsending av skjema? Laste et script/img/... tag? Eller noen annen måte en side kan bruke for å (lovlig) lage en forespørsel? Eller kanskje noen kjent JS-hack?

Origin-headeren sendes normalt bare for XHR-forespørsler på tvers av domener. Bildeforespørsler inneholder ikke headeren.

Merk: Jeg snakker ikke om

  • native applikasjoner,

  • manipulerte nettlesere,

  • tverrside-skriptfeil på example.coms side,

Jeg er ikke sikker på om dette faller under manipulerte nettlesere eller ikke, men gamle versjoner av Flash tillot at vilkårlige overskrifter kunne settes, noe som ville gjøre det mulig for en angriper å sende en forespørsel med en forfalsket referer-overskrift fra offerets maskin for å utføre et angrep.

Svar #2

Forklaring av begrepene

CORS (Cross-Origin Resource Sharing) er en protokoll som gjør det mulig for to forskjellige domener å kommunisere med hverandre.

En CORS (pre-flight) forespørsel må inkludere Origin-attributtet i headeren. Et svar på en CORS pre-flight forespørsel inneholder Access-Control-Allow-Origin-attributtet i headeren for å tillate/begrense tilgang fra et annet domene. Fra MDN web-dokumentasjon:

Attributtet Origin finnes i alle forespørselsoverskrifter bortsett fra GET og HEAD-forespørsler. En CORS (forhåndsforespørsel) må inneholde Origin-attributtet, men ikke alle forespørsler med Origin-attributt er en CORS (forhåndsforespørsel).

Same-origin policy er en mekanisme for å begrense domeners mulighet til å kommunisere med hverandre.

CSRF-tokenet (Cross-Site-Request-Forgery) lagres i brukerens sesjon og må sendes med en POST/DELETE/PUT-forespørsel. På serversiden vil CSRF-tokenet bli sammenlignet med verdien i sesjonen og tillate å fortsette bare hvis de samsvarer.

Same origin-policy forhindrer kommunikasjon på tvers av domener

Alle nettlesere implementerer same-origin-policyen. Denne policyen hindrer generelt at en webapplikasjon på domene A kan sende en HTTP-forespørsel til en applikasjon på domene B. Den begrenser imidlertid ikke alle forespørsler. For eksempel begrenser ikke same-origin-policyen embed-tagger som dette:

<img src="https://dashboard.example.com/post-message/hello">

Det er irrelevant om svaret er et gyldig bilde — forespørselen blir fortsatt utført. Dette er grunnen til at det er viktig at endepunkter som endrer tilstand på webapplikasjonen din ikke kan kalles med GET-metoden.

CORS-protokoll: Pre-flight-sjekk

Du kan bruke CORS for å omgå same-origin policy og la domene A sende en forespørsel til domene B som ellers ville vært forbudt. Før den faktiske forespørselen sendes, vil en preflight-forespørsel bli sendt for å sjekke om serveren tillater at domene A sender denne typen forespørsel. Hvis det gjør det, vil domene B ha Access-Control-Allow-Origin feltet i responsheaderen, og domene A vil sende den faktiske forespørselen.

For eksempel, hvis ingen Access-Control-Allow-Origin er satt, vil en Javascript XMLHttpRequest bli begrenset for domene A av en preflight, uten å utføre forespørselen på domene B.

Hvorfor du trenger CSRF-token til tross for same-origin policy

Hvis same-origin-policyen skulle fungere for alle typer forespørsler, ville det ikke vært behov for å bruke CSRF-token, fordi man ville hatt full beskyttelse gjennom same-origin-policyen og kunne brukt CORS for å eksplisitt angi hvilket domene som kan kommunisere. Dette er imidlertid ikke tilfelle. Det finnes et par HTTP-forespørsler som ikke sender en CORS preflight-forespørsel!

GET-, HEAD- og POST-forespørsler med spesifikke headere og spesifikk innholdstype sender ikke en preflight-forespørsel. Slike forespørsler kalles enkle forespørsler. Dette betyr at forespørselen vil bli utført.

Siden skjemaer eksisterte før CORS og allerede hadde tillatelse til å sende data til hvilken som helst opprinnelse, ønsket man ikke å ødelegge det eksisterende internett med forflytningstester for POST-forespørsler. Se også her:

Motivasjonen er at

-elementet fra HTML 4.0 (som går forut for cross-site XMLHttpRequest og fetch) kan sende enkle forespørsler til hvilken som helst opprinnelse, så alle som skriver en server må allerede beskytte mot cross-site request forgery (CSRF). Under denne forutsetningen trenger ikke serveren å aktivt velge å delta (ved å svare på en preflight-forespørsel) for å motta enhver forespørsel som ligner på en skjema-innsending, siden trusselen fra CSRF ikke er verre enn den ved skjema-innsending. Serveren må imidlertid fortsatt aktivt velge å delta ved å bruke Access-Control-Allow-Origin for å dele svaret med skriptet.

Dessverre lager en enkel <form action="POST"> en enkel forespørsel!

Og på grunn av disse enkle forespørslene må vi beskytte POST-rutene med CSRF-tokener (GET-ruter trenger ikke CSRF fordi de uansett kan leses av innebygde tagger som vist ovenfor. Bare sørg for at du ikke har en tilstandsendrende GET-metode).

Er det nok å sjekke opprinnelsen til forespørselen?

Siden POST-forespørselen sender opprinnelsen med i headeren og nettleseren ikke tillater å endre den, kan man lure på om det er nok å sjekke opprinnelsen alene, uten CSRF-token. Imidlertid kan skript som kjøres i CLI sette opprinnelsen til en vilkårlig verdi. I tillegg kan det fortsatt være mulig å endre opprinnelsen på grunn av en kombinasjon av plugins som brukeren kan ha tilgjengelig. En annen ting er at nettlesere kan feile i å støtte det (Edge ble utgitt i april 2015, men støttet opprinnelses-header i juni 2015, i Firefox var det en feil som ble fikset i 2008 https://bugzilla.mozilla.org/show_bug.cgi?id=446344).

Fra Wikipedia:

Ulike andre teknikker har blitt brukt eller foreslått for CSRF-forebygging historisk. Å verifisere at forespørselens headere inneholder [...] HTTP Origin-headeren. Dette er imidlertid usikkert - en kombinasjon av nettleserutvidelser og omdirigeringer kan tillate en angriper å sende tilpassede HTTP-headere i en forespørsel til hvilken som helst nettside, og dermed tillate en forfalsket forespørsel

Derfor anbefales det å bruke en CSRF-token i stedet for å stole på origin-header. Men det finnes tilfeller hvor det kan være bedre å ikke ha en CSRF-token, på grunn av utløp av økt.

Problem med CSRF-tokens

Jeg ville unngå å bruke CSRF-tokener for kontaktskjemaer. Det kan føre til en situasjon der en bruker har skrevet lenge, og når han sender inn, er økten utdatert, CSRF-token er ugyldig, og hele meldingen fra brukeren blir fjernet. Det ville være en forferdelig brukeropplevelse. For et kontaktskjema vil du unngå å få meldinger fra SPAM, så recaptcha3 kan være et rimelig valg, da det ikke krever brukerinteraksjon.

Du kan også forlenge levetiden til økter i nettleseren din, men dette kan ende opp med en stor lagringsstørrelse. Alternativt kan du bruke økter med cookie-hash som lagres hos en klient. Disse øktene bør heller ikke utløpe, selv om klienten må ta en 12-timers pause før skjemaet sendes inn.

Min oppsummering

  • Bruk CSRF-token for kritiske endepunkter, som innlogging eller kasse i butikk
  • Av praktiske årsaker vil jeg foreslå å ikke bruke CSRF-token for søknadsskjemaer eller kontaktskjemaer.
  • Aldri ha en GET-endepunkt som endrer tilstand

Svar #3

Nettinnhold kan ikke tukle med Origin-headeren. Videre, under samme opprinnelsespolicy, kan ikke én opprinnelse engang sende egendefinerte headere til andre opprinnelser. [1]

Dermed er det å sjekke Origin-headeren like godt til å blokker angrep som å bruke et CSRF-token.

Hovedbekymringen med å stole på dette er om det tillater alle legitime forespørsler å fungere. Den som spør vet om dette problemet, og har satt opp spørsmålet for å utelukke de største tilfellene (ingen gamle nettlesere, kun HTTPS).

Nettleserleverandører følger disse reglene, men hva med tillegg? De gjør det kanskje ikke, men spørsmålet ser bort fra "manipulerte nettlesere." Hva med feil i nettleseren som lar en angriper forfalske Origin-headeren? Det kan også være feil som gjør at CSRF-tokenet lekker på tvers av opprinnelser, så det ville kreve mer arbeid å argumentere for at den ene er bedre enn den andre.

Relaterte artikler