CORS w AMP
Wiele składników i rozszerzeń AMP wykorzystuje zdalne punkty końcowe, stosując żądania Cross-Origin Resource Sharing (CORS). Niniejszy dokument wyjaśnia kluczowe aspekty korzystania z CORS w AMP. Aby dowiedzieć się więcej o samym CORS, zobacz Specyfikacje CORS W3.
- Po co mi CORS do własnego źródła?
- Stosowanie plików cookie do żądań CORS
-
Po co mi CORS do własnego źródła?
Konieczność stosowania mechanizmu CORS do żądań własnego źródła może być dezorientująca, zagłębmy się w to.
Składniki AMP pobierające dane dynamiczne (np. amp-form, amp-list itd.) wysyłają żądania CORS do zdalnych punktów końcowych w celu pobrania danych. Jeśli strona AMP zawiera takie składniki, trzeba obsługiwać CORS, aby te żądania nie kończyły się niepowodzeniem.
Zilustrujmy to na przykładzie:
Powiedzmy, że masz stronę AMP, która zawiera listę produktów z cenami. Aby zaktualizować ceny na stronie, użytkownik klika przycisk, który pobiera najnowsze ceny z punktu końcowego JSON (za pomocą składnika amp-list). JSON jest w Twojej domenie.
Dobrze, strona jest zatem w mojej domenie i JSON też jest w mojej domenie. Nie widzę żadnego problemu!
Ach, ale jak Twój użytkownik dostał się na stronę AMP? Czy dostęp jest uzyskiwany do buforowanej strony? Jest dość prawdopodobne, że użytkownik nie uzyskał bezpośredniego dostępu do strony AMP, ale zamiast tego odnalazł ją za pośrednictwem innej platformy. Na przykład wyszukiwarka Google do szybkiego renderowania stron AMP używa usługi Google AMP Cache; są to strony buforowane i serwowane na serwerach usługi Google AMP Cache, w innej domenie. Gdy użytkownik kliknie przycisk, aby zaktualizować ceny na swojej stronie, buforowana strona AMP wysyła żądanie do domeny Twojego źródła, aby uzyskać ceny, co skutkuje niezgodnością źródeł (serwer buforujący -> domena źródła). Aby zezwolić na takie żądania danych z różnych źródeł, musisz obsługiwać mechanizm CORS, w przeciwnym razie żądanie nie powiedzie się.
Co mam zatem zrobić?
- W przypadku stron AMP pobierających dane dynamiczne testuj wersję buforowaną tych stron; nie testuj tylko we własnej domenie. (Patrz Testowanie CORS w AMP poniżej)
- Wykonaj zawarte w niniejszym dokumencie instrukcje dotyczące obsługi żądań i odpowiedzi CORS.
Stosowanie plików cookie do żądań CORS
Większość składników AMP wykorzystującch żądania CORS albo automatycznie ustawia tryb credentials mode, albo pozwala autorowi na opcjonalne włączenie go. Na przykład składnik amp-list
pobiera dynamiczną zawartość z pliku JSON punktu końcowego CORS i pozwala autorowi na ustawienie trybu uwierzytelniania za pomocą atrybutu credentials
.
Przykład: dodawanie spersonalizowanej zawartości do składnika amp-list za pomocą plików cookie
<amp-list credentials="include" src="<%host%>/json/product.json?clientId=CLIENT_ID(myCookieId)" > <template type="amp-mustache"> Your personal offer: ${{price}} </template> </amp-list>
Dzięki określeniu trybu credentials źródło może uwzględniać pliki cookie w żądaniu CORS, a także ustawiać pliki cookie w odpowiedzi (z zastrzeżeniem ograniczeń dotyczących plików cookie stron trzecich).
Ograniczenia dotyczące plików cookie stron trzecich
Ograniczenia dotyczące plików cookie stron trzecich określone w przeglądarce mają zastosowanie również do uwierzytelnionych żądań CORS w AMP. Ograniczenia te zależą od przeglądarki i platformy, ale w przypadku niektórych przeglądarek źródło można ustawić tylko wtedy, gdy użytkownik wcześniej odwiedzi źródło w oknie strony pierwszej (najwyższym). Innymi słowy, tylko wtedy, gdy użytkownik bezpośrednio odwiedził witrynę źródła. W związku z tym, usługa dostępna za pośrednictwem CORS nie może zakładać, że będzie w stanie domyślnie ustawiać pliki cookie.
Zabezpieczenia CORS w AMP
Aby zapewnić prawidłowość i bezpieczeństwo żądań i odpowiedzi na stronach AMP, musisz:
Jeśli używasz Node na swoim zapleczu, możesz użyć oprogramowania pośredniczącego AMP CORS, które jest częścią przybornika AMP.
Weryfikowanie żądań CORS
Gdy Twój punkt końcowy otrzyma żądanie CORS:
- Sprawdź, czy nagłówek CORS
Origin
jest dozwolonym źródłem (źródło wydawcy + serwery buforujące AMP). - Jeśli nie ma nagłówka Origin sprawdź, czy żądanie pochodzi z tego samego źródła. (za pomocą nagłówka
AMP-Same-Origin
).
1) Zezwalanie na żądania określonych źródeł CORS
Punkty końcowe CORS otrzymują żądane źródło za pomocą nagłówka HTTP Origin
. Punkty końcowe powinny zezwalać na żądania tylko z: (1) własnego źródła wydawcy; oraz (2) każdego źródła cacheDomain
wymienionego na liście pod adresem https://cdn.ampproject.org/caches.json.
Na przykład, punkty końcowe powinny zezwalać na żądania z:
- Subdomeny Google AMP Cache:
https://<publisher's domain>.cdn.ampproject.org
(na przykładhttps://nytimes-com.cdn.ampproject.org
)
2) Zezwalanie na żądania tych samych źródeł
W przypadku żądań tego samego źródła, w których brakuje nagłówka Origin
AMP ustawia następujący nagłówek niestandardowy:
AMP-Same-Origin: true
Ten nagłówek niestandardowy jest wysyłany przez środowisko uruchomieniowe AMP po wygenerowaniu żądania XHR tego samego źródła (tj. dokumentu serwowanego z niebuforowanego adresu URL). Zezwól na żądania zawierające nagłówek AMP-Same-Origin:true
.
Wysyłanie nagłówków odpowiedzi CORS
Po zweryfikowaniu żądania CORS wynikowa odpowiedź HTTP musi zawierać następujące nagłówki:
Access-Control-Allow-Origin: <origin>
Nagłówek ten jest wymogiem specyfikacji CORS W3, w których origin
odnosi się do źródła generującego żądanie, które zostało dozwolone za pomocą nagłówka żądania CORS Origin
(na przykład "https://<domena wydawcy>.cdn.ampproject.org"
).
Chociaż specyfikacja CORS W3 zezwala na zwrócenie w odpowiedzi wartości *
, dla większego bezpieczeństwa należy:
- Jeśli nagłówek
Origin
jest obecny, zatwierdzić i powtórzyć wartość nagłówkaOrigin
.
Przetwarzanie żądań zmiany stanu
Przed przetworzeniem żądań, które mogłyby zmienić stan systemu (np. użytkownik zapisuje się na listę mailingową lub z niej wypisuje), sprawdź co następuje:
Jeśli nagłówek Origin
jest ustawiony:
- Jeśli źródło nie odpowiada jednej z poniższych wartości, należy zatrzymać przetwarzanie i zwrócić odpowiedź z błędem:
<domena wydawcy>.cdn.ampproject.org
- źródło wydawcy (Twoje)
gdzie *
to symbol wieloznaczny, a nie znak gwiazdki ( * ).
- W przeciwnym razie przetwórz żądanie.
Jeśli nagłówek Origin
nie jest ustawiony:
- Sprawdź, czy żądanie zawiera nagłówek
AMP-Same-Origin: true
. Jeśli żądanie nie zawiera tego nagłówka, zatrzymaj przetwarzanie i zwróć odpowiedź z błędem. - W przeciwnym razie przetwórz żądanie.
Przykładowy sposób postępowania: obsługa żądań i odpowiedzi CORS
W żądaniach CORS do Twojego punktu końcowego należy uwzględnić dwa scenariusze:
- Żądanie z tego samego źródła.
- Żądanie ze źródła buforowanego (z serwera buforującego AMP).
Przejdźmy te scenariusze z przykładem. W naszym przykładzie, zarządzamy witryną example.com
, w której znajduje się strona AMP o nazwie article-amp.html.
. Strona AMP zawiera składnik amp-list
, służący do pobierania danych dynamicznych z pliku data.json
, który również znajduje się w witrynie example.com
. Chcemy przetwarzać żądania do naszego pliku data.json
, pochodzące z naszej strony AMP. Te żądania mogą pochodzić ze strony AMP o tym samym źródle (nie buforowanym) lub ze strony AMP z innego źródła (buforowanego).
Dozwolone źródła
Na podstawie tego, co wiemy o CORS i AMP (z powyższej sekcji Weryfikowanie żądań CORS ), do celów naszego przykładu będziemy zezwalać na żądania z następujących domen:
example.com
— domena wydawcyexample-com.cdn.ampproject.org
— subdomena Google AMP Cache
Nagłówki odpowiedzi na dozwolone żądania
W przypadku żądań z dozwolonych źródeł nasza odpowiedź będzie zawierać następujące nagłówki:
Access-Control-Allow-Origin: <origin>
Poniżej widnieją dodatkowe nagłówki odpowiedzi, które możemy zawrzeć w naszej odpowiedzi CORS:
Access-Control-Allow-Credentials: true Content-Type: application/json Access-Control-Max-Age: <delta-seconds> Cache-Control: private, no-cache
Pseudokod CORS
Naszą logikę obsługi żądań i odpowiedzi CORS można uprościć do następującego pseudokodu:
IF CORS header present IF origin IN allowed-origins allow request & send response ELSE deny request ELSE IF "AMP-Same-Origin: true" allow request & send response ELSE deny request
Przykładowy kod CORS
Oto przykładowa funkcja JavaScript, której możemy użyć do obsługi żądań i odpowiedzi CORS:
function assertCors(req, res, opt_validMethods, opt_exposeHeaders) { var unauthorized = 'Unauthorized Request'; var origin; var allowedOrigins = [ 'https://example.com', 'https://example-com.cdn.ampproject.org', 'https://cdn.ampproject.org', ]; var allowedSourceOrigin = 'https://example.com'; //publisher's origin // If same origin if (req.headers['amp-same-origin'] == 'true') { origin = sourceOrigin; // If allowed CORS origin & allowed source origin } else if ( allowedOrigins.indexOf(req.headers.origin) != -1 && sourceOrigin == allowedSourceOrigin ) { origin = req.headers.origin; } else { res.statusCode = 403; res.end(JSON.stringify({message: unauthorized})); throw unauthorized; } res.setHeader('Access-Control-Allow-Credentials', 'true'); res.setHeader('Access-Control-Allow-Origin', origin); }
Uwaga: próbka działającego kodu znajduje się w pliku amp-cors.js.
Scenariusz 1: żądanie otrzymane od strony AMP o tym samym źródle
W poniższym scenariuszu strona article-amp.html
żąda pliku data.json
; źródła są te same.
Jeśli zbadamy żądanie, znajdziemy:
Request URL: https://example.com/data.json Request Method: GET AMP-Same-Origin: true
Jako że żądanie to pochodzi z tego samego źródła, nie ma nagłówka Origin
, ale ma niestandardowy nagłówek żądania AMP AMP-Same-Origin: true
. Możemy zezwolić na to żądanie, ponieważ pochodzi ono z tego samego źródła (https://example.com
).
Nasza odpowiedź ma wówczas następujące nagłówki:
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://example.com
Scenariusz 2: żądanie otrzymane od buforowanej strony AMP
W poniższym scenariuszu strona article-amp.html
buforowana na serwerze Google AMP Cache żąda pliku data.json
; źródła są różne.
Jeśli zbadamy żądanie, znajdziemy:
Request URL: https://example.com/data.json Request Method: GET Origin: https://example-com.cdn.ampproject.org
Jako że żądanie zawiera nagłówek Origin
sprawdzimy, czy pochodzi on z dozwolonego źródła. Możemy zezwolić na to żądanie, ponieważ pochodzi ono z dozwolonego źródła.
Nasza odpowiedź ma wówczas następujące nagłówki:
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: https://example-com.cdn.ampproject.org
Praca z buforowanymi czcionkami
Usługa Google AMP Cache buforuje dokumenty AMP HTML, obrazy i czcionki, aby optymalizować szybkość działania strony AMP. Czyniąc stronę AMP szybką, chcemy również być ostrożni, zabezpieczając buforowane zasoby. Zmienimy sposób, w jaki serwer buforujący AMP odpowiada na buforowane na nim zasoby, zazwyczaj czcionki, poprzez respektowanie wartości źródła Access-Control-Allow-Origin
.
Wcześniejszy sposób działania (przed październikiem 2019 r.)
Gdy strona AMP ładowała plik https://example.com/some/font.ttf
z atrybutu @font-face src
, serwer buforujący AMP buforował plik z czcionkami i serwował zasób jak poniżej, mając symbol wieloznaczny jako wartość Access-Control-Allow-Origin
.
- Adres URL
https://example-com.cdn.ampproject.org/r/s/example.com/some/font.tff
- Access-Control-Allow-Origin: *
Nowy sposób działania (od października 2019 r.)
Bieżąca implementacja jest mało restrykcyjna, ale może to prowadzić do nieoczekiwanego wykorzystania czcionek z witryn o różnych źródłach. Wskutekj tej zmiany serwer buforujący AMP zaczyna odpowiadać dokładnie tą samą wartością Access-Control-Allow-Origin
, którą odpowiada serwer źródła. Aby poprawnie załadować czcionki z buforowanego dokumentu AMP, trzeba zaakceptować źródło z serwera buforującego AMP za pomocą nagłówka.
Przykładowa implementacja:
function assertFontCors(req, res, opt_validMethods, opt_exposeHeaders) { var unauthorized = 'Unauthorized Request'; var allowedOrigins = [ 'https://example.com', 'https://example-com.cdn.ampproject.org', ]; // If allowed CORS origin if (allowedOrigins.indexOf(req.headers.origin) != -1) { res.setHeader('Access-Control-Allow-Origin', req.headers.origin); } else { res.statusCode = 403; res.end(JSON.stringify({message: unauthorized})); throw unauthorized; } }
Jeśli na przykład chcesz ładować /some/font.ttf w pliku https://example.com/amp.html
, serwer źródła powinien odpowiedzieć nagłówkiem Access-Control-Allow-Origin jak poniżej.
Access-Control-Allow-Origin
z symbolem wieloznacznym, serwer buforujący AMP będzie również powtarzać tę wartość, czyli zwracać odpowiedź Access-Control-Allow-Origin: *
. Jeśli masz już to ustawienie, nie musisz niczego zmieniać. Planujemy dokonać tej zmiany około połowy października 2019 roku i oczekujemy, że każdy wydawca AMP stosujący czcionki z własnego serwera sprawdzi, czy ma to na nie wpływ.
Plan wdrożenia
- 2019-09-30: wersja zawiera precyzyjniejszą kontrolę nad domenami, których dotyczy ta zmiana. Kompilacja powinna zostać wdrożona w ciągu tego tygodnia.
- 2019-10-07: domeny testowe zostaną udostępnione do celów ręcznego testowania.
- 2019-10-14: (ale w zależności od przebiegu testów): funkcja zostanie ogólnie wdrożona.
Kwestie pokrewne znajdziesz tutaj.
Testowanie CORS w AMP
Testując swoje strony AMP, przetestuj również buforowane wersje stron AMP.
Weryfikowanie strony za pomocą adresu URL na serwerze buforującym
Aby upewnić się, że strona AMP z serwera buforującego jest renderowana i działa prawidłowo:
- W przeglądarce otwórz adres URL, którego serwer buforujący AMP użyje do uzyskania dostępu do danej strony AMP. Możesz ustalić format adresu URL na serwerze buforującym za pomocą tego narzędzia w sekcji AMP By Example.
Przykład:
- Adres URL:
https://amp.dev/documentation/guides-and-tutorials/start/create/
- Format adresu URL na serwerze buforującym AMP:
https://www-ampproject-org.cdn.ampproject.org/c/s/www.ampproject.org/docs/tutorials/create.html
- Otwórz narzędzia programistyczne swojej przeglądarki i sprawdź, czy nie ma żadnych błędów, a wszystkie zasoby zostały załadowane prawidłowo.
Weryfikowanie nagłówków odpowiedzi serwera
Aby sprawdzić, czy Twój serwer wysyła odpowiednie nagłówki odpowiedzi HTTP, możesz użyć polecenia curl
. W poleceniu curl
, podaj adres URL żądania i wszelkie niestandardowe nagłówki, które chcesz dodać.
Składnia: curl url-żądania> -H <nagłówek-niestandardowy> - I
Testowanie żądania z tego samego źródła
W żądaniu tego samego źródła system AMP dodaje nagłówek niestandardowy AMP-Same-Origin:true
.
Oto nasze polecenie curl służące do testowania żądania z https://ampbyexample.com
do pliku examples.json
(w tej samej domenie):
curl 'https://amp.dev/static/samples/json/examples.json' -H 'AMP-Same-Origin: true' -I
Wyniki polecenia pokazują prawidłowe nagłówki odpowiedzi (uwaga: obcięte zostały dodatkowe informacje):
HTTP/2 200 access-control-allow-headers: Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token access-control-allow-credentials: true access-control-allow-origin: https://ampbyexample.com access-control-allow-methods: POST, GET, OPTIONS
Testowanie żądania z buforowanej strony AMP
W żądaniu CORS z innej domeny (tzn. z serwera buforującego) nagłówek Origin
jest częścią żądania.
Oto nasze polecenie curl służące do testowania żądania ze strony AMP buforowanej w usłudze Google AMP Cache do pliku examples.json
:
curl 'https://amp.dev/static/samples/json/examples.json' -H 'origin: https://ampbyexample-com.cdn.ampproject.org' -I
Wyniki polecenia pokazują prawidłowe nagłówki odpowiedzi:
HTTP/2 200
access-control-allow-headers: Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token
access-control-allow-credentials: true
access-control-allow-origin: https://ampbyexample-com.cdn.ampproject.org
access-control-allow-methods: POST, GET, OPTIONS