För en erfaren utvecklare kanske den här texten inte alls är överraskande, men jag tror att många av de artiklar jag har läst om CORS-installationen i Rails sa något i stil med: använd rack-cors, låt alla värdar komma åt API:et och (eventuellt): du bör överväga något annat (än att tillåta alla värdar) i produktionen.
Den föreslagna kod var alltid nära den under:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
ursprung ''
resource '', headers: :any, methods: :any
slut
slut
och tyvärr förklarade dessa texter knappast för oss vad vi faktiskt skulle göra i produktionen.
Jag är ganska bra på att kopiera och klistra in (Jag skämtar ibland om att företag skulle kunna anställa en Stack Overflow copy-paster), så länge det finns ett "tänk och justera"-moment mellan "kopiera" och "klistra in". Så jag skulle vilja utveckla lite vad vi gör här och hur det fungerar i verkliga livet.
Jag hoppas att du inte har något emot att jag börjar med en kort introduktion till hedersteori och sedan går vidare till Rails-exemplen.
Inledning
Låt oss börja från början. För att förklara saker och ting bättre har jag delat upp introduktionen i tre delar. I den första delen beskrivs vad ett ursprung är - nyckelbegreppet för det vi diskuterar här. Den andra handlar om SOP, bara en kort beskrivning. Och den sista delen handlar om CORS
själv.
Vad är ett ursprung?
Enligt MDN Web Docs:
- Webbinnehållets ursprung definieras av schemat (protokollet), värden (domänen) och porten för den URL som används för att komma åt det. Två objekt har samma ursprung endast när schema, host och port matchar varandra (källa)
Det verkar ganska tydligt, eller hur? Låt oss analysera två exempel från MDN, bara i fall att.
http://example.com/app1/index.html
, http://example.com/app2/index.html
De 2 ovanstående har samma ursprung eftersom:
- deras system (http) är desamma,
- deras domäner (example.com) är desamma,
- deras portar (implicita) är desamma.
http://www.example.com
, http://myapp.example.com
Dessa 2 har olika ursprung eftersom domänerna (www.example.com
, myapp.exempel.com
) är olika.
Jag hoppas att det är tillräckligt tydligt. Om inte, kan du gå till MDN Web Docs för fler exempel.
Vad är SOP?
MDN Web Docs säger (källa):
- Policyn för samma ursprung är en viktig säkerhetsmekanism som begränsar hur ett dokument eller skript som laddas från ett ursprung kan interagera med en resurs från ett annat ursprung. Det hjälper till att isolera potentiellt skadliga dokument och minska möjliga attackvektorer.
- Skrivningar mellan olika ursprung är vanligtvis tillåtna. Exempel är länkar, omdirigeringar och formulärinlämningar.
- Cross-origin embedding är normalt tillåtet.
- Cross-origin reads är vanligtvis inte tillåtna, men läsåtkomst läcker ofta ut genom inbäddning.
Användning CORS
för att tillåta åtkomst över ursprungsland
Som du kan se handlar definitionerna av SOP mycket om beteende mellan olika ursprung. Men det gör inget. Allt vi borde veta nu är att samma ursprung har fler privilegier och att vi kan lossa på reglerna för cross-origins genom att använda CORS. Och här kommer nästa avsnitt in.
Vad är CORS?
Baserat på MDN:s ord:
- Cross-Origin Resource Sharing (CORS) är en HTTP-headerbaserad mekanism som gör det möjligt för en server att ange andra ursprung (domän, schema eller port) än det egna från vilket en webbläsare ska tillåta inläsning av resurser. CORS bygger också på en mekanism genom vilken webbläsare gör en "preflight"-förfrågan till den server som är värd för cross-origin-resursen, för att kontrollera att servern kommer att tillåta den faktiska förfrågan. I denna preflight skickar webbläsaren rubriker som anger den HTTP-metod och de rubriker som kommer att användas i den faktiska begäran (källa).
Det är fortfarande inte tillräckligt. Vad som inte uttryckligen sades där är att den viktigaste rubriken när du använder CORS
är Access-kontroll-Allow-Origin
:
- Den
Access-kontroll-Allow-Origin
svarshuvudet anger om svaret kan delas med begärande kod från det angivna ursprunget (källa).
Ja, det borde vara allt. I verkliga livet, när du konfigurerar CORS
konfigurerar vi vanligtvis ACAO
rubriken först.
Verkliga livet
Det var allt när det gäller definitioner. Låt oss återgå till Rails och exempel från verkligheten.
Hur konfigurerar jag CORS i Rails?
Vi kommer definitivt att använda rack-cors (som vi blev tillsagda att göra). Låt oss komma ihåg det första utdraget, det som oftast tillhandahålls i andra artiklar:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
ursprung ''
resource '', headers: :any, methods: :any
slut
slut
Antalet alternativ är stort eller till och med oändligt, men låt oss överväga dessa två:
- bygger vi det API som får användas av webbläsarklienter från tredje part,
- vi har en typisk frontend/backend-separation och vill tillåta våra betrodda kunder att komma åt API:et.
API för byggnader som nås av tredjepartskunder
Om du står inför det första alternativet kan du förmodligen välja ursprung
"*" - du vill att andra ska bygga en klient på toppen av ditt API och inte veta vem de är, eller hur?
Typisk frontend/backend-separation
Om du utvecklar det senare vill du förmodligen inte att alla ska göra cross-origin-förfrågningar till ditt API. Det vill du snarare:
- tillåta produktionsklienter att få tillgång till produktions-API,
- samma sak för staging,
- samma sak för localhost,
- du kanske vill tillåta FE-granskningsappar att komma åt staging.
Vi kommer fortfarande att använda rack-cors (som vi blev tillsagda att göra) - men på vårt eget sätt.
Låt oss använda 2 ENV-variabler: TILLÅTNA_URSPRUNG
för bokstavliga ursprungsdefinitioner (en asterisk eller en faktisk URL) och TILLÅTET_URSPRUNG_REGEXPS
för mönstren.
config/initializers/cors.rb
frozenstringliteral: true
toregexp = ->(sträng) { Regexp.new(sträng) }
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)
resurs '*',
methods: %i[get post put patch delete options head],
rubriker: :vilken som helst
slut
slut
Vad är det som händer här?
- Som du kan se delar vi upp värdena som definieras i ENV-variablerna med olika separatorer. Det beror på att det är mindre sannolikt att ett semikolon förekommer i URL-definieringsmönstret.
- Bokstavliga värden är klara att användas, men vi måste mappa mönstren så att de blir faktiska Regexp-instanser.
- Sedan sammanfogar vi allt och låter dessa värdar komma åt alla resurser med vitlistade metoder som vårt API använder.
Detta bör ge dig tillräcklig flexibilitet för att definiera korrekta värden i dina utvecklings-, staging- och produktionsmiljöer.
Slutsatser
Låt oss sammanfatta allt ovanstående i viktiga punkter:
- använda ENV-variabler för att konfigurera
CORS
,
- använda reguljära uttryck för att tillåta olika ursprung att få åtkomst till staging API (t.ex. för granskningsappar),
- alltid sätta "tänka och justera" mellan "kopiera" och "klistra in".
Så var det med det. Ha en trevlig dag! 🙂 🙂
Läs mer om detta:
Varför bör du (förmodligen) använda Typescript?
10 NYC Startups värda att nämna 2021