For en erfaren utvikler er denne teksten kanskje ikke overraskende i det hele tatt, men jeg tror at mange av artiklene jeg har lest om CORS-oppsettet i Rails sa noe sånt som: bruk rack-cors, tillat alle verter å få tilgang til API-et, og (eventuelt): du bør vurdere noe annet (enn å tillate alle verter) i produksjon.
Den foreslåtte kode var alltid i nærheten av den under:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ''
resource '', headers: :any, methods: :any
end
end
og dessverre forklarte disse tekstene oss i liten grad hva vi faktisk skulle gjøre i produksjonen.
Jeg er ganske OK med copy-pasting (Jeg spøker av og til med at bedrifter kunne ansette en Stack Overflow copy-paster), for så vidt som det er et "tenk og juster"-øyeblikk mellom "kopier" og "lim inn". Så jeg vil gjerne utdype litt om hva vi gjør her, og hvordan det fungerer i det virkelige liv.
Jeg håper det er greit at jeg begynner med en kort introduksjon til æresteori og deretter går videre til Rails-eksemplene.
Innledning
La oss begynne fra begynnelsen. For å forklare ting bedre har jeg delt innledningen i tre deler. Den første delen vil skissere hva et opphav er - nøkkelbegrepet for det vi diskuterer her. Den andre delen handler om SOP, bare en kort beskrivelse. Og den siste delen handler om CORS
seg selv.
Hva er en opprinnelse?
I følge MDN Web Docs:
- Webinnholdets opprinnelse defineres av skjemaet (protokollen), verten (domenet) og porten til URL-adressen som brukes for å få tilgang til det. To objekter har samme opprinnelse bare når skjema, vert og port stemmer overens (kilde)
Det virker ganske klart, ikke sant? La oss analysere to eksempler fra MDN, bare for sikkerhets skyld.
http://example.com/app1/index.html
, http://example.com/app2/index.html
De 2 ovenfor har samme opprinnelse fordi:
- deres ordninger (http) er de samme,
- domenene deres (eksempel.com) er de samme,
- portene deres (implisitt) er de samme.
http://www.example.com
, http://myapp.example.com
Disse 2 har forskjellig opprinnelse fordi domenene (www.example.com
, myapp.example.com
) er forskjellige.
Jeg håper det er tydelig nok. Hvis ikke, kan du gå til MDN Web Docs for flere eksempler.
Hva er SOP?
MDN Web Docs sier (kilde):
- Policyen for samme opprinnelse er en viktig sikkerhetsmekanisme som begrenser hvordan et dokument eller skript som er lastet inn fra én opprinnelse, kan samhandle med en ressurs fra en annen opprinnelse. Den bidrar til å isolere potensielt skadelige dokumenter og redusere mulige angrepsvektorer.
- Skriving på tvers av opphav er vanligvis tillatt. Eksempler på dette er lenker, viderekoblinger og skjemainnleveringer.
- Innbygging på tvers av opphav er vanligvis tillatt.
- Lesing på tvers av opphav er vanligvis ikke tillatt, men lesetilgang lekkes ofte ved hjelp av embedding.
Bruk CORS
for å tillate tilgang på tvers av opprinnelse
Som du ser, handler definisjonene av SOP mye om atferd på tvers av opprinnelsesland. Det gjør ingenting. Alt vi bør vite nå er at samme opprinnelse har flere privilegier, og vi kan løsne på reglene for kryssopprinnelser ved å bruke CORS. Og her kommer neste avsnitt inn.
Hva er CORS?
Basert på MDNs ord:
- CORS (Cross-Origin Resource Sharing) er en HTTP-header-basert mekanisme som gjør det mulig for en server å angi hvilken som helst annen opprinnelse (domene, skjema eller port) enn sin egen som en nettleser skal tillate innlasting av ressurser fra. CORS er også avhengig av en mekanisme der nettlesere foretar en "preflight"-forespørsel til serveren som er vert for cross-origin-ressursen, for å kontrollere at serveren vil tillate den faktiske forespørselen. I denne preflighten sender nettleseren overskrifter som angir HTTP-metoden og overskriftene som skal brukes i den faktiske forespørselen (kilde).
Det er fortsatt ikke nok. Det som ikke ble sagt eksplisitt der, er at den viktigste overskriften når du bruker CORS
er Tilgangskontroll-Allow-Origin
:
- Den
Tilgangskontroll-Allow-Origin
svaroverskriften angir om svaret kan deles med kode fra den gitte opprinnelsen (kilde).
Vel, det burde være alt. I det virkelige liv, når du konfigurerer CORS
konfigurerer vi vanligvis ACAO
overskriften først.
Det virkelige liv
Det var alt når det gjelder definisjoner. La oss gå tilbake til Rails og eksempler fra det virkelige liv.
Hvordan konfigurerer jeg CORS i Rails?
Vi vil definitivt bruke rack-cors (som vi ble bedt om). La oss huske det første utdraget, det som oftest er gitt i andre artikler:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ''
resource '', headers: :any, methods: :any
end
end
Det finnes uendelig mange alternativer, men la oss se på disse to:
- bygger vi API-et som kan brukes av tredjeparts nettleserklienter,
- vi har typisk frontend/backend-separasjon og ønsker å gi våre betrodde kunder tilgang til API-et.
Bygger API som tredjeparts klienter har tilgang til
Hvis du står overfor det første alternativet, kan du sannsynligvis velge opprinnelse
"*" - du vil at andre skal bygge en klient på toppen av API-et ditt, men du vet ikke hvem de er, ikke sant?
Typisk frontend/backend-separasjon
Hvis du utvikler sistnevnte, vil du sannsynligvis ikke at alle skal kunne sende forespørsler til API-et ditt på tvers av opprinnelse. Det vil du heller gjøre:
- tillate produksjonsklienter å få tilgang til produksjons-API-et,
- det samme for iscenesettelse,
- det samme for localhost,
- kan det være lurt å gi FE-gjennomgangsapper tilgang til staging.
Vi kommer fortsatt til å bruke rack-cors (slik vi ble bedt om) - men på vår måte.
La oss bruke to ENV-variabler: TILLATT_OPPRINNELSE
for bokstavelige opprinnelsesdefinisjoner (en stjerne eller en faktisk URL) og TILLATT_OPPRINNELSE_REGEKS
for mønstrene.
config/initializers/cors.rb
frozenstringliteral: true
toregexp = ->(string) { Regexp.new(string) }
hosts = [
*ENV.fetch('ALLOWEDORIGINS').split(','),
*ENV.fetch('ALLOWEDORIGINREGEXPS').split(';').map(&to_regexp)
]
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins(*hosts)
resource '*',
methods: %i[get post put patch delete options head],
headers: :any
end
end
Hva er det som foregår her?
- Som du ser, deler vi opp verdiene som er definert i ENV-variablene, med ulike separatorer. Det er fordi det er mindre sannsynlig at semikolon dukker opp i URL-definisjonsmønsteret.
- Bokstavverdiene er klare til bruk, men vi må tilordne mønstrene til å være faktiske Regexp-instanser.
- Deretter kobler vi alt sammen og lar disse vertene få tilgang til alle ressurser med hvitelistede metoder som API-et vårt bruker.
Dette bør gi deg nok fleksibilitet til å definere riktige verdier i utviklings-, staging- og produksjonsmiljøene dine.
Konklusjoner
La oss oppsummere alt det ovennevnte i hovedpunkter:
- bruke ENV-variabler til å konfigurere
CORS
,
- bruke regulære uttrykk for å gi ulike opprinnelser tilgang til staging-API-et (f.eks. for vurderingsapper),
- Sett alltid "tenk og juster" mellom "kopier" og "lim inn".
Det var det hele. Ha en fin dag 🙂 🙂 .
Les mer om dette:
Hvorfor bør du (sannsynligvis) bruke Typescript?
10 NYC-oppstartsbedrifter verdt å nevne i 2021