AMP

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?

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ć?

  1. 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)
  2. Wykonaj zawarte w niniejszym dokumencie instrukcje dotyczące obsługi żądań i odpowiedzi 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 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:

  1. Zweryfikować żądanie.
  2. Wysłać odpowiednie nagłówki odpowiedzi.

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:

  1. Sprawdź, czy nagłówek CORS Origin jest dozwolonym źródłem (źródło wydawcy + serwery buforujące AMP).
  2. 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ład https://nytimes-com.cdn.ampproject.org)

Informacje na temat formatów adresów URL serwerów buforujących AMP znajdują się w tych zasobach:

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łówka Origin.

Przetwarzanie żądań zmiany stanu

Wykonaj te kontrole poprawności _ przed _ przetworzeniem wniosku. Taka walidacja pozwala zapewnić ochronę przed atakami CSRF i uniknąć przetwarzania żądań niezaufanych źródeł.

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:

  1. 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 ( * ).

  1. W przeciwnym razie przetwórz żądanie.

Jeśli nagłówek Origin nie jest ustawiony:

  1. 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.
  2. 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:

  1. Żądanie z tego samego źródła.
  2. Żą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 wydawcy
  • example-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.


Jeśli dostęp do pliku z czcionką można uzyskiwać z dowolnego źródła, możesz zastosować odpowiedź 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:

  1. 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
  1. 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