Pro zkušeného vývojáře nemusí být tento text vůbec překvapivý, ale myslím, že spousta článků, které jsem četl o nastavení CORS v Rails, říkala něco jako: použijte rack-cors, povolte libovolnému hostiteli přístup k API a (případně): v produkci byste měli zvážit něco jiného (než povolení libovolnému hostiteli).
Navrhované kód byla vždy blízko té, která se nacházela pod ním:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ''
resource '', headers: :any, methods: :any
end
end
a bohužel, tyto texty byly sotva vysvětlující pro nás co vlastně dělat ve výrobě.
Kopírování a vkládání mi docela jde (Občas si dělám legraci, že by si firmy mohly najmout copy-pastera ze Stack Overflow.), pokud jde o okamžik "přemýšlení a přizpůsobení" mezi "kopírovat" a "vložit". Rád bych tedy trochu rozvedl, co zde děláme a jak to funguje v reálném životě.
Doufám, že vám nevadí, že začnu krátkým úvodem do teorie cti a pak přejdu k příkladům Rails.
Úvod
Začněme od začátku. Pro lepší vysvětlení jsem úvod rozdělil na tři části. V první části nastíním, co je to původ - klíčový pojem pro to, o čem se zde bavíme. Druhá část je o SOP, jen stručný popis. A poslední část hovoří o CORS sama o sobě.
Co je to původ?
Podle webových dokumentů MDN:
- Původ webového obsahu je definován schématem (protokolem), hostitelem (doménou) a portem adresy URL použité k přístupu k němu. Dva objekty mají stejný původ pouze tehdy, pokud se shodují schéma, hostitel a port (zdroj)
To je snad jasné, ne? Pro jistotu si rozebereme dva příklady z MDN.
http://example.com/app1/index.html, http://example.com/app2/index.html
2 výše uvedené mají stejný původ, protože:
- jejich schémata (http) jsou stejná,
- jejich domény (example.com) jsou stejné,
- jejich porty (implicitní) jsou stejné.
http://www.example.com, http://myapp.example.com
Tyto 2 domény mají odlišný původ, protože domény (www.example.com, myapp.example.com) se liší.
Doufám, že je to dostatečně jasné. Pokud ne, přejděte na webové dokumenty MDN, kde najdete další příklady.
Co je to SOP?
Webové dokumenty MDN říkají (zdroj):
- Zásada stejného původu je důležitý bezpečnostní mechanismus, který omezuje způsob, jakým může dokument nebo skript načtený z jednoho zdroje interagovat se zdrojem z jiného zdroje. Pomáhá izolovat potenciálně škodlivé dokumenty a omezuje možné vektory útoku.
- Zápisy napříč původem jsou obvykle povoleny. Příkladem jsou odkazy, přesměrování a odesílání formulářů.
- Vkládání napříč původem je obvykle povoleno.
- Křížové čtení je obvykle zakázáno, ale přístup ke čtení často uniká vložením.
Použijte CORS umožnit křížový přístup
Jak vidíte, v definicích SOP se hodně hovoří o chování napříč původem. To je v pořádku. Nyní bychom měli vědět jen to, že stejný původ má více oprávnění a že můžeme pravidla pro křížové původy uvolnit pomocí CORS. A zde přichází na řadu další část.
Co je CORS?
Na základě slov MDN:
- Cross-Origin Resource Sharing (CORS) je mechanismus založený na hlavičce protokolu HTTP, který umožňuje serveru uvést jakýkoli jiný původ (doménu, schéma nebo port), než je jeho vlastní, ze kterého by měl prohlížeč povolit načítání zdrojů. CORS se také opírá o mechanismus, pomocí kterého prohlížeče zasílají serveru, na kterém je umístěn prostředek s křížovým původem, požadavek "preflight", aby ověřily, zda server povolí skutečný požadavek. V rámci tohoto preflightu odesílá prohlížeč hlavičky, které udávají metodu HTTP a hlavičky, které budou použity ve skutečném požadavku. (zdroj).
To stále nestačí. Nebylo tam výslovně řečeno, že nejdůležitější hlavička při použití CORS je Access-Control-Allow-Origin:
- Na stránkách
Access-Control-Allow-Origin hlavička odpovědi udává, zda může být odpověď sdílena s kódem žádajícím o odpověď z daného původu (zdroj).
To by mělo být vše. V reálném životě při konfiguraci CORS, obvykle konfigurujeme ACAO nejprve záhlaví.
Skutečný život
To je vše, pokud jde o definice. Vraťme se zpět k Rails a příkladům z reálného života.
Jak nakonfigurovat CORS v systému Rails?
Určitě budeme používat stojanové korby (jak nám bylo řečeno). Připomeňme si první úryvek, který je nejčastěji uváděn v jiných článcích:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ''
resource '', headers: :any, methods: :any
end
end
Možností je nepřeberné množství, ale podívejme se na tyto dvě:
- vytváříme rozhraní API, které mohou používat klienti prohlížečů třetích stran,
- jsme typicky oddělili frontend od backendu a chceme našim důvěryhodným klientům umožnit přístup k rozhraní API.
Budování rozhraní API s přístupem klientů třetích stran
Pokud máte před sebou první možnost, pravděpodobně byste mohli zvolit následující. původ '*' - chcete, aby ostatní vytvořili klienta na základě vašeho rozhraní API, a nevíte, kdo to je, že?
Typické oddělení frontend/backend
Pokud vyvíjíte druhý typ rozhraní, pravděpodobně nechcete, aby všichni zadávali požadavky na vaše rozhraní API s různým původem. Spíše chcete:
- umožnit produkčním klientům přístup k produkčnímu rozhraní API,
- totéž platí pro inscenaci,
- totéž platí pro localhost,
- možná budete chtít povolit aplikacím FE review přístup ke staging.
Stále budeme používat stojanové korby (jak nám bylo řečeno) - ale po našem.
Použijme 2 proměnné ENV: ALLOWED_ORIGINS pro doslovné definice původu (hvězdička nebo skutečná adresa URL) a ALLOWED_ORIGIN_REGEXPS pro vzory.
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
Co se tu děje?
- Jak vidíte, rozdělujeme hodnoty definované v proměnných ENV pomocí různých oddělovačů. Je to proto, že středník se ve vzoru definujícím adresu URL vyskytuje s menší pravděpodobností.
- Literální hodnoty jsou připraveny k použití, ale musíme namapovat vzory na skutečné instance Regexp.
- Pak vše spojíme dohromady a umožníme těmto hostitelům přístup k jakémukoli prostředku s metodami, které naše rozhraní API používá na bílém seznamu.
To by vám mělo poskytnout dostatečnou flexibilitu pro definování správných hodnot ve vývojovém, stagingovém a produkčním prostředí.
Závěry
Shrňme si vše výše uvedené do klíčových bodů:
- použít ENV proměnné pro konfiguraci
CORS,
- použít regulární výrazy pro povolení přístupu k rozhraní API pro staging různého původu (např. pro aplikace pro recenze),
- mezi "kopírovat" a "vložit" vždy vložte "přemýšlet a upravit".
To je vše. Hezký den! 🙂
Přečtěte si více:
Proč byste (pravděpodobně) měli používat Typescript?
10 newyorských startupů, které stojí za zmínku v roce 2021