CORS w Symfony 4 / 5: Kompleksowe Wprowadzenie

0
430
Rate this post

Cross-Origin Resource Sharing (CORS) to mechanizm, który pozwala na udostępnienie zasobów pochodzących z jednej domeny (origin) dla innych domen. Jest to kluczowe dla współczesnych aplikacji webowych, w których frontend i backend mogą działać na różnych domenach. Symfony, będące jednym z najpopularniejszych frameworków PHP, oferuje różne sposoby zarządzania CORS, i w tym artykule omówimy te metody w kontekście wersji Symfony 4 i 5.

Jak działa CORS?

Gdy przeglądarka wysyła zapytanie do serwera, domyślnie blokuje wszelkie zapytania krzyżowe (cross-origin), chyba że serwer zwróci odpowiednie nagłówki. CORS wprowadza kilka nagłówków HTTP, które serwer może użyć do określenia, z jakimi domenami chce współpracować.

Ręczna konfiguracja w Symfony

W Symfony można ręcznie skonfigurować CORS, dodając odpowiednie nagłówki HTTP w pliku konfiguracyjnym.

Przykład w services.yaml

yaml
services:
CorsResponseListener:
tags:
- { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }

Klasa CorsResponseListener

php
namespace App\EventListener;

use Symfony\Component\HttpKernel\Event\ResponseEvent;

class CorsResponseListener
{
public function onKernelResponse(ResponseEvent $event)
{
$responseHeaders = $event->getResponse()->headers;

$responseHeaders->set('Access-Control-Allow-Origin', '*');
$responseHeaders->set('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, PATCH, OPTIONS');
}
}

Pakiet NelmioCorsBundle

Jeżeli preferujesz gotowe rozwiązanie, możesz skorzystać z pakietu NelmioCorsBundle.

Instalacja

Instalację rozpoczynamy od dodania pakietu do naszego projektu.

bash
composer require nelmio/cors-bundle

Konfiguracja w nelmio_cors.yaml

yaml
nelmio_cors:
defaults:
allow_origin: ['*']
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
allow_headers: ['Content-Type', 'Authorization']
paths:
'^/api/':
allow_origin: ['http://localhost', 'http://127.0.0.1']
allow_headers: ['*']
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
max_age: 3600

Konfiguracja zaawansowana

Jeżeli potrzebujesz bardziej zaawansowanej konfiguracji, możesz również rozważyć użycie zdarzeń (events) w Symfony.

Przykład użycia EventSubscriber

php
namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class CorsSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => 'onKernelResponse',
];
}

public function onKernelResponse(ResponseEvent $event)
{
$response = $event->getResponse();
$response->headers->set('Access-Control-Allow-Origin', '*');
// ... (inne nagłówki)
}
}

Testowanie i Debugowanie

Kiedy skonfigurujesz CORS w Symfony, ważnym krokiem jest przetestowanie całego mechanizmu. Możesz to zrobić za pomocą narzędzi takich jak Postman, curl lub bezpośrednio z poziomu przeglądarki, sprawdzając konsolę deweloperską.

Jak to działa w praktyce?

Praktyczne użycie CORS w Symfony może być różne w zależności od architektury i wymagań aplikacji. Na przykład, w mikroserwisach, mechanizm CORS może zostać skonfigurowany na poziomie API Gateway. W bardziej złożonych systemach możesz potrzebować użycia różnych reguł CORS dla różnych środowisk, jak deweloperskie, testowe i produkcyjne.

Middleware i Proxy jako Alternatywa

Innym podejściem do zarządzania CORS jest użycie middleware lub proxy, który siedzi między twoim backendem a klientem. Popularne serwery proxy, takie jak Nginx czy Apache, oferują opcje konfiguracyjne, które pozwalają na zarządzanie CORS na poziomie serwera, zamiast aplikacji. Może to być użyteczne, jeśli masz wiele różnych aplikacji backendowych i chcesz centralnie zarządzać zasadami CORS.

Konfiguracja w Nginx

W przypadku serwera Nginx, możesz dodać następujące dyrektywy do konfiguracji:

nginx
location / {
# ... (inne konfiguracje)

add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
# ... (inne nagłówki)
}

Konfiguracja w Apache

Jeśli korzystasz z Apache, możesz użyć modułu mod_headers do dodania odpowiednich nagłówków CORS:

apache
<Directory /var/www/my-site/>
# ... (inne konfiguracje)

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
# ... (inne nagłówki)
</Directory>

Uwzględnianie uwierzytelniania w CORS

Czasem możesz potrzebować umożliwić CORS tylko dla zautoryzowanych użytkowników. W tym celu można użyć nagłówka Access-Control-Allow-Credentials. Warto pamiętać, że jeżeli ten nagłówek jest ustawiony na true, Access-Control-Allow-Origin nie może być ustawiony na wildcard * i musi zawierać konkretną domenę.

Przykład z NelmioCorsBundle:

yaml
nelmio_cors:
paths:
'^/api/':
allow_origin: ['http://localhost:8080']
allow_credentials: true

Przykład z EventSubscriber:

php
public function onKernelResponse(ResponseEvent $event)
{
$response = $event->getResponse();
$response->headers->set('Access-Control-Allow-Origin', 'http://localhost:8080');
$response->headers->set('Access-Control-Allow-Credentials', 'true');
// ... (inne nagłówki)
}

Warianty i niuanse

CORS to temat o wiele bardziej złożony, niż może się wydawać na pierwszy rzut oka. Możesz potrzebować zdefiniować niestandardowe nagłówki, zarządzać preflight requests poprzez metody OPTIONS, lub zastosować logikę warunkową dla różnych ścieżek w twojej aplikacji. Symfony oferuje dużą elastyczność, pozwalającą na dopasowanie CORS do indywidualnych potrzeb projektu.

Wyzwania i pułapki

O ile CORS jest niezbędnym mechanizmem dla współczesnych aplikacji webowych, to nie jest wolny od wyzwań i pułapek. Błędna konfiguracja CORS może prowadzić do problemów z bezpieczeństwem, takich jak podatności na ataki CSRF czy data leakage. Dlatego też, bardzo ważne jest, aby dokładnie zrozumieć mechanizm CORS i świadomie go konfigurować.

Monitorowanie i Audyt

Ostatnim, ale nie mniej ważnym elementem jest monitorowanie i audyt konfiguracji CORS. Narzędzia takie jak OWASP ZAP czy różnego rodzaju skanery mogą pomóc w identyfikacji potencjalnych problemów. Jeżeli wprowadzasz jakiekolwiek zmiany w konfiguracji CORS, upewnij się, że przetestujesz je w różnych scenariuszach, aby uniknąć niespodziewanych problemów.

Automatyzacja i CI/CD

Jeśli jesteś częścią zespołu deweloperskiego pracującego nad dużą aplikacją, warto również włączyć sprawdzanie konfiguracji CORS jako część procesu CI/CD (Continuous Integration/Continuous Deployment). Automatyczne testy mogą pomóc wychwycić potencjalne problemy jeszcze przed wdrożeniem na środowisko produkcyjne.

Przykładowe narzędzia

  • CORS-Anywhere: Jest to prosty proxy napisany w Node.js, który dodaje nagłówki CORS do każdego zapytania. Może być używany w środowisku testowym.
  • Mocha + Chai: Biblioteki te są często używane do pisania testów jednostkowych i integracyjnych w JavaScript. Możesz stworzyć testy sprawdzające, czy odpowiednie nagłówki CORS są zwracane.

Przykładowy test w Mocha

javascript
const expect = require('chai').expect;
const axios = require('axios');

describe('CORS headers', () => {
it('should include CORS headers', async () => {
const response = await axios.get('http://localhost/api/some-endpoint');
expect(response.headers).to.have.property('access-control-allow-origin');
});
});

Dokumentacja i Best Practices

Ostatecznie, bardzo ważne jest, aby utrzymać dokumentację konfiguracji CORS aktualną. To nie tylko ułatwi nowym członkom zespołu zrozumienie, jak jest skonfigurowany CORS, ale również będzie pomocne w audytach bezpieczeństwa i przeglądach kodu.

Best Practices

  • Unikać wildcardów: Nie ustawiaj Access-Control-Allow-Origin na * dla wrażliwych zasobów.
  • Zasada najmniejszych uprawnień: Zawsze ograniczaj dostęp do zasobów do minimum, które jest potrzebne.
  • Logowanie i monitorowanie: Utrzymuj logi odnoszące się do CORS, które mogą pomóc w debugowaniu i audytach.

Wpływ na wydajność

Dobrze jest również być świadomym, że mechanizm CORS może wpłynąć na wydajność aplikacji. Preflight requests (zapytania typu OPTIONS) są dodatkowymi zapytaniami wysyłanymi przed właściwym zapytaniem, i mogą wprowadzić opóźnienia. Optymalizacja tych zapytań, na przykład przez ustawienie odpowiedniego czasu życia cache (Access-Control-Max-Age), może pomóc w mitigacji tego problemu.

Podsumowując

CORS to nie tylko zestaw nagłówków czy reguł, to mechanizm, który wpływa na różne aspekty projektu: od bezpieczeństwa, przez wydajność, aż do jakości kodu i doświadczenia użytkownika. Symfony oferuje rozbudowane narzędzia do zarządzania CORS, które można dostosować do konkretnych potrzeb aplikacji.