CORS sdílení zdrojů

V Cognitu často vytváříme webová rozhraní k různým API. Ať už pro prezentaci výsledků datových kolegů z Gauss Algorithmic, nebo aktuálně pro administrační rozhraní Colpirio. Ukážeme si, na co musíme myslet při vytváření API služeb, které zpracovávají požadavky webových aplikací běžících na různých doménách.

Cross-Origin Resource Sharing

Zmíněné administrační rozhraní píšeme v klientském javascriptu a používáme framework Angular. Dotazy na API služeb jsou klasické AJAX dotazy. A to je kámen úrazu. Standardně nelze AJAX požadavek poslat např. na jinou doménu. Díky bezpečnostnímu mechanismu Same Origin Policy prohlížeč takový dotaz zakáže.

Aby bylo možné takový dotaz provést a přitom zachovat bezpečnost aplikace, musí se prohlížeč dohodnout se serverem, zda je ochotný takový požadavek přijmout. Pravidla této komunikace definuje specifikace pro Cross-Origin Resource Sharing (CORS).

Ke klasickému HTTP požadavku přidává CORS nové hlavičky, ve kterých si klient se serverem předávají instrukce. Všechny moderní webové prohlížeče tyto hlavičky podporují a řídí se jimi. Pokud XHR požadavek na serveru může změnit data (není poslán HTTP metotou GET, ale POST, DELETE, PATCH atd.), musí prohlížeč podle CORS specifikace provést přípravný požadavek pomocí HTTP metody OPTIONS, tzv. preflight request. Tímto dotazem se ověří, zda je hlavní požadavek povolen. Teprve po potvrzení tohoto dotazu může být hlavní požadavek poslán na server.

CORS hlavičky

Prohlížeč posílá na server následující CORS hlavičky:

  • Origin: název domény, ze které se vytváří požadavek.

V přípravném požadavku (preflight request) jsou přítomny ještě hlavičky:

  • Access-Control-Request-Method: HTTP metoda chystaného požadavku,
  • Access-Control-Request-Headers: čárkou oddělený seznam hlaviček, které budou v chystaném požadavku použity.

Odpověď serveru obsahuje následující CORS hlavičky:

  • Access-Control-Allow-Origin: mezerou oddělený seznam povolených domén, znak * udává, že jsou povoleny všechny domény a hodnota null naopak zakazuje všechny domény,
  • Access-Control-Allow-Methods: čárkou oddělený seznam povolených HTTP metod požadavku,
  • Access-Control-Allow-Headers: seznam povolených hlaviček v požadavku,
  • Access-Control-Allow-Credentials: oznamuje, jestli součástí požadavku mohou být cookies, HTTP autentizace a cliet-side certifikáty,
  • Access-Control-Max-Age: v sekundách uvedená doba, po kterou je preflight request uchován v cache,
  • Access-Control-Expose-Headers: seznam hlaviček, které je bezpečné odeslat na server.

Kompletní specifikace je dostupná na stránkách W3C konsorcia Cross-Origin Resource Sharing.

Implementace na serveru

Pokud nechceme řešit složité nastavování, můžeme CORS hlavičky automaticky přidat do HTTP odpovědi třeba pomocí webového serveru Apache nebo Nginx. Návody pro běžné servery jsou uvedeny na webu enable-cors.org. Tento přístup však není příliš vhodný. Lepší je specifikovat CORS hlavičky pro jednotlivé přístupové body API služby.

V Cognitu píšeme serverové služby v programovacích jazycích PHP a Python. Přičemž PHP aplikace pohání Symfony framework a Python aplikace framework Flask. Díky těmto frameworkům je implementace obsluhy CORS hlaviček velmi snadná.

CORS pro Symfony

Do Symfony nainstalujeme balíček NelmioCorsBundle:

Balíček zapneme v aplikaci:

A nakonec nastavíme pravidla pro jednotlivé URL adresy:

CORS pro Flask

Do Flasku nainstalujeme rozšíření Flask-Cors:

Rozšíření naimportujeme do aplikace a nakonfigurujeme:

A máme hotovo. API služby jsou připravené obsluhovat požadavky z webových aplikací běžících na libovolných doménách.