# CORS

## Vad det är

CORS, utskrivet Cross-Origin Resource Sharing, är de regler som en server
använder för att tala om för webbläsaren att sidor från andra webbplatser får
läsa det den skickar tillbaka. Webbläsare håller isär ursprung. Ett ursprung är
schema, värd och port tillsammans, så `https://app.example.com`,
`https://api.example.com` och `http://app.example.com` är tre olika ursprung,
och ett skript på det ena får inte läsa ett svar från ett annat om inte det
andra går med på det. CORS är sättet att gå med på det: en liten familj
HTTP-headrar, nästan alla med namn som börjar på `Access-Control-`, som servern
sätter på sina svar och som webbläsaren kontrollerar innan något alls lämnas
vidare till skriptet som frågade.

Protokollet beskrivs i Fetch-standarden från WHATWG, den levande
specifikationen av hur webbläsare hämtar resurser. Där står också skälet, och
det är enkelt. En sida ska kunna använda data från ett annat ursprung och
skicka rikare anrop än ett HTML-formulär klarar, men bara när motparten själv
har sagt ja. Utan det jaet skulle vilken sida som helst på nätet kunna läsa
servrar som står bakom ett företags brandvägg, eller läsa en användares privata
data på en annan webbplats med användarens kakor på köpet.

Själva utbytet är kort. När ett skript ber om en resurs från ett annat ursprung
lägger webbläsaren till en `Origin`-header med sidans eget ursprung. Servern
svarar med `Access-Control-Allow-Origin`, och värdet är antingen samma
ursprung, exakt skrivet, eller `*` för alla. Stämmer det får skriptet svaret.
Stämmer det inte får skriptet ett nätverksfel och ingenting av innehållet.

Vissa anrop behöver lov innan de ens skickas. En `GET`, `HEAD` eller `POST`
med några få vanliga headrar och en kropp av typen
`application/x-www-form-urlencoded`, `multipart/form-data` eller `text/plain`
går direkt iväg, eftersom ett HTML-formulär alltid har kunnat skicka sådant
från vilken webbplats som helst. Allt utöver det, till exempel en `PUT`, en
`DELETE`, en JSON-kropp eller en `Authorization`-header, föregås av ett
förhandsanrop, som alla kallar preflight: ett `OPTIONS`-anrop som namnger
metoden i `Access-Control-Request-Method` och de extra headrarna i
`Access-Control-Request-Headers`. Det riktiga anropet följer bara om svaret har
en lyckad status som 200 eller 204 och tillåter båda delarna.

## Varför det spelar roll

Nästan varje webbapplikation med ett separat API stöter på CORS första dagen.
Frontenden serveras från ett ursprung och API:et från ett annat, och det första
`fetch`-anropet från det ena till det andra misslyckas i webbläsarens konsol
tills API:et skickar rätt headrar. Team som lär känna CORS på det sättet minns
det gärna som ett hinder och konfigurerar det därefter, genom att tillåta det
som får felet att försvinna. Det är där risken finns.

Headrarna avgör vem som kan läsa dina användares data genom deras egna
webbläsare. En användare som är inloggad i din applikation bär på en
sessionskaka. Om API:et låter vilket ursprung som helst skicka anrop med kakor
och läsa svaren kan varje sida som användaren besöker anropa API:et i
användarens namn och läsa resultatet. Fetch-standarden säger det rakt ut: att
dela svar som hämtats med användarens inloggningsuppgifter kräver största
försiktighet för att undvika det den kallar confused deputy-problemet.
Webbläsaren, som bär användarens kakor, är ställföreträdaren som blir lurad,
och en slarvig CORS-inställning låter en främmande sida styra den.

Det är lika viktigt att veta vad CORS inte gör. Det skyddar inte servern. Ett
enkelt anrop når fram till servern och körs oavsett om svaret sedan får delas,
så en `POST` som ändrar data har ändrat dem innan webbläsaren har tittat på en
enda CORS-header. Utanför en webbläsare innehåller `Origin`-headern vad
anroparen än skriver, och OWASP:s testguide varnar för att den kan förfalskas.
Vem som får anropa en slutpunkt, och vad de får göra där, avgörs av
autentisering och auktorisering på servern, varje gång. CORS avgör bara vilka
webbsidor som får läsa svaret inne i en webbläsare.

## Så ser det ut i praktiken

Tänk dig en ensidesapplikation på `https://app.example.com` som sparar en
anteckning via ett API på `https://api.example.com` och använder den
sessionskaka som API:et satte vid inloggningen:

```js
await fetch("https://api.example.com/v1/notes/42", {
  method: "PUT",
  credentials: "include",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ text: "Släpp på fredag" }),
});
```

En `PUT` med en JSON-kropp är mer än ett formulär kan skicka, så webbläsaren
frågar först. Förhandsanropet har inga kakor med sig; det namnger bara vad det
riktiga anropet kommer att använda:

```http
OPTIONS /v1/notes/42 HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Accept: */*
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: content-type
```

API:et jämför ursprunget med sin egen lista och svarar:

```http
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: content-type
Access-Control-Max-Age: 600
Vary: Origin
```

Nu skickar webbläsaren själva `PUT`-anropet med kakan och en `Origin`-header,
och API:ets svar upprepar `Access-Control-Allow-Origin` och
`Access-Control-Allow-Credentials: true`. Saknas någon av dem är anteckningen
sparad på servern, men skriptet får aldrig se svaret. `Access-Control-Max-Age`
låter webbläsaren återanvända svaret på förhandsanropet i upp till 600 sekunder
i stället för att fråga före varje anrop; utan headern behåller standarden det
i fem sekunder. `Vary: Origin` är med eftersom svaret beror på vem som frågar,
och en cache som bortsåg från det skulle kunna lämna ett ursprungs svar vidare
till ett annat.

På en plattform med professionella och organisatoriska data har API:et
[hastighetsbegränsning, CORS, storleksgränser för anrop och validering av indata](/sv/portfolio/hardened-the-application-with-nonce-based-csp/)
innan något når databasen.
Samma sida förklarar varför CORS är en punkt på den listan och inte hela
försvaret: plattformen utgår från att klienten är fientlig, och servern
kontrollerar behörigheterna på nytt vid varje anrop, oavsett vad webbläsaren
har tillåtit.

## Var det går fel

**Servern kopierar vilken `Origin` som helst tillbaka till
`Access-Control-Allow-Origin`.** Det är den snabbaste vägen till att få felet i
konsolen att försvinna, och tillsammans med
`Access-Control-Allow-Credentials: true` ger det varenda webbplats på internet
användarens vy av API:et. Ha en uttrycklig lista över de ursprung som ska ha
åtkomst, jämför inkommande `Origin` exakt med den och skicka headrarna bara vid
träff.

**Listan kontrolleras med ett prefix eller en delsträng.** En regel som godtar
varje ursprung som börjar med `https://app.example.com` godtar också
`https://app.example.com.attacker.example`, och en delsträngsmatchning är ännu
sämre. Jämför hela serialiserade ursprung. Ett serialiserat ursprung har varken
sökväg eller avslutande snedstreck, så `https://app.example.com/` på listan
kommer aldrig att matcha någonting.

**`*` förväntas fungera tillsammans med kakor.** När ett anrop har
inloggningsuppgifter med sig avvisar webbläsaren `*` i
`Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`,
`Access-Control-Allow-Headers` och `Access-Control-Expose-Headers`, och
`Access-Control-Allow-Credentials` måste vara exakt `true`, med små bokstäver.
Ett publikt API som bara läses och inte använder kakor kan tryggt svara `*`;
ett API som vilar på en session måste namnge ursprunget.

**`null` står på listan över tillåtna.** Sidor som laddats från en
`data:`-URL får ett ogenomskinligt ursprung, och ett anrop som har
omdirigerats mellan ursprung rapporterar också `Origin: null`, så många helt
olika dokument visar samma värde. Att tillåta `null` är att tillåta alla. Låt
det vara ute.

**`Vary: Origin` saknas.** Skickar API:et `Access-Control-Allow-Origin` bara
när ett anrop har en `Origin` som stämmer kan en cache spara ett svar utan
headern och servera det för ett senare CORS-anrop, eller spara ett ursprungs
svar och ge det till ett annat. Fetch-standarden ber om `Vary: Origin` när
svaret beror på ursprunget. Är värdet `*` eller ett fast ursprung, skicka det i
varje svar och låt bli `Vary`.

**`Access-Control-Allow-Headers: *` antas täcka allt.** Jokertecknet täcker
aldrig `Authorization`, som måste namnges, och det täcker ingenting alls för
anrop med inloggningsuppgifter. Namnge de headrar som klienten faktiskt
skickar.

**CORS används som åtkomstkontroll.** En server som avvisar anrop för att
`Origin` ser fel ut, och annars släpper in dem, har skrivit en kontroll som
varje skript utanför en webbläsare går rakt förbi. Autentisera anroparen,
auktorisera handlingen på servern och låt CORS sköta sitt enda jobb: att avgöra
vad en webbläsare visar för en sida.

## Läs mer

- [Fetch Standard: the CORS protocol](https://fetch.spec.whatwg.org/#http-cors-protocol)
  — definitionen: anrops- och svarsheadrarna, förhandsanropet, tabellen över
  inloggningsuppgifter och rådet om `Vary`.
- [RFC 6454: The Web Origin Concept](https://www.rfc-editor.org/rfc/rfc6454) —
  vad ett ursprung är, hur schema, värd och port avgör det och hur det skrivs i
  `Origin`-headern.
- [OWASP Web Security Testing Guide: Testing Cross Origin Resource Sharing](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/11-Client-side_Testing/07-Testing_Cross_Origin_Resource_Sharing/)
  — hur testare letar efter ett jokerteckensursprung eller ett ursprung som
  skickas tillbaka utan kontroll.

<https://engineer.company/sv/glossary/cors/>
