Standard Content Security Policy może znacznie zmniejszyć ryzyko i skutki ataków typu cross-site scripting w nowoczesnych przeglądarkach.
Model bezpieczeństwa w internecie opiera się na zasadzie takiego samego pochodzenia. Na przykład kod z https://mybank.com
musi mieć dostęp tylko do danych https://mybank.com
, a dostęp do danych https://evil.example.com
nigdy nie może być dozwolony.
Teoretycznie każda domena jest odizolowana od reszty internetu, co daje deweloperom bezpieczne środowisko piaskownicy do tworzenia. W praktyce jednak atakujący znaleźli kilka sposobów na obejście systemu.
Ataki cross-site scripting (XSS) omijają zasadę tego samego pochodzenia, oszukując witrynę, aby dostarczała ona złośliwy kod wraz z zamierzoną treścią. To ogromny problem, ponieważ przeglądarki ufają wszystkiemu kodowi, który pojawia się na stronie, jako że jest on legalnym elementem pochodzenia zabezpieczeń tej strony. Poradnik dotyczący XSS to stary, ale reprezentatywny przegląd metod, których może użyć atakujący, aby naruszyć to zaufanie, wstrzykując złośliwy kod. Jeśli atakujący wstrzyknie jakikolwiek kod, spowoduje to naruszenie sesji użytkownika i uzyska dostęp do informacji prywatnych.
Ta strona przedstawia zasady ochrony treści (CSP) jako strategię zmniejszania ryzyka i wpływu ataków typu XSS w nowoczesnych przeglądarkach.
Komponenty CSP
Aby wdrożyć skuteczny mechanizm CSP, wykonaj te czynności:
- Używaj list dozwolonych, aby informować klienta, co jest dozwolone, a co nie.
- Dowiedz się, jakie dyrektywy są dostępne.
- Dowiedz się, jakie słowa kluczowe są przez nie używane.
- Ogranicz użycie kodu wbudowanego i elementu
eval()
. - Zgłaszaj naruszenia zasad na serwerze przed ich egzekwowaniem.
Listy dozwolonych źródeł
Ataki XSS wykorzystują fakt, że przeglądarka nie potrafi odróżnić skryptu będącego częścią aplikacji od skryptu wstrzykniętego przez osobę trzecią. Na przykład przycisk Google +1 na dole tej strony wczytuje i wykonuje kod z https://apis.google.com/js/plusone.js
w kontekście pochodzenia tej strony.
Ufamy temu kodowi, ale nie możemy oczekiwać, że przeglądarka sama z siebie zorientuje się, że kod z apis.google.com
jest bezpieczny do wykonania, a kod z apis.evil.example.com
prawdopodobnie nie. Przeglądarka pobiera i wykonuje każdy kod, którego strona zażąda, niezależnie od źródła.
Nagłówek HTTP Content-Security-Policy
w standardzie CSP umożliwia utworzenie listy dozwolonych źródeł zaufanych treści i informuje przeglądarkę, aby uruchamiała lub renderowała tylko zasoby z tych źródeł. Nawet jeśli atakujący znajdzie lukę, przez którą mógłby wstrzyknąć skrypt, skrypt nie będzie pasował do listy dozwolonych, a więc nie zostanie wykonany.
Ufamy, że apis.google.com
dostarczy prawidłowy kod, i wierzymy, że my też to zrobimy. Oto przykład zasady, która zezwala na wykonywanie skryptów tylko wtedy, gdy pochodzą z jednego z tych 2 źródeł:
Content-Security-Policy: script-src 'self' https://apis.google.com
script-src
to dyrektywa, która kontroluje zestaw uprawnień związanych ze skryptem na stronie. Ten nagłówek 'self'
jako jeden prawidłowy źródło skryptu i https://apis.google.com
jako drugie. Przeglądarka może teraz pobierać i wykonywać kod JavaScript z apis.google.com
przez HTTPS, a także z źródła bieżącej strony, ale nie z żadnego innego źródła. Jeśli atakujący wstrzyknie kod do Twojej witryny, przeglądarka wyświetli błąd i nie uruchomi wstrzykniętego skryptu.
Zasady dotyczą wielu różnych zasobów
CSP udostępnia zestaw dyrektyw zasad, które umożliwiają szczegółową kontrolę zasobów, które strona może wczytać, w tym script-src
z poprzedniego przykładu.
Poniższa lista zawiera pozostałe dyrektywy dotyczące zasobów na poziomie 2. Opracowano specyfikację na poziomie 3, ale jest ona w dużej mierze niezrealizowana w głównych przeglądarkach.
base-uri
- Ogranicza adresy URL, które mogą się pojawiać w elemencie
<base>
strony. child-src
- Wyświetla listę adresów URL pracowników i zawartości wbudowanych ramek. Na przykład
child-src https://youtube.com
umożliwia umieszczanie filmów z YouTube, ale nie z innych źródeł. connect-src
- Ogranicza źródła, z którymi możesz się łączyć za pomocą XHR, WebSockets i EventSource.
font-src
- Określa źródła, które mogą dostarczać czcionki internetowe. Możesz na przykład zezwolić na czcionki internetowe Google za pomocą
font-src https://themes.googleusercontent.com
. form-action
- Wyświetla listę prawidłowych punktów końcowych do przesłania z tagów
<form>
. frame-ancestors
- Określa źródła, które mogą osadzić bieżącą stronę. Ta dyrektywa dotyczy tagów
<frame>
,<iframe>
,<embed>
i<applet>
. Nie można go używać w tagach<meta>
ani w przypadku zasobów HTML. frame-src
- Ta dyrektywa została wycofana na poziomie 2, ale przywrócona na poziomie 3. Jeśli go nie ma, przeglądarka przyjmuje wartość domyślną
child-src
. img-src
- Określa źródła, z których można wczytywać obrazy.
media-src
- Ogranicza źródła, z których można przesyłać dźwięk i obraz.
object-src
- Pozwala kontrolować Flasha i inne wtyczki.
plugin-types
- Ogranicza typy wtyczek, które może wywoływać strona.
report-uri
- Określa adres URL, na który przeglądarka wysyła raporty w przypadku naruszenia zasad bezpieczeństwa treści. Tej dyrektywy nie można używać w tagach
<meta>
. style-src
- Ogranicza źródła, z których strona może używać arkuszy stylów.
upgrade-insecure-requests
- Instrukcje dla klientów użytkowników, aby ponownie zapisywać schematy adresów URL, zmieniając protokół HTTP na HTTPS. Ta dyrektywa jest przeznaczona do witryn z dużą liczbą starych adresów URL, które trzeba przekierować.
worker-src
- Instrukcja CSP poziomu 3, która ogranicza adresy URL, które mogą być wczytywane jako worker, worker współdzielony lub worker usługi. Od lipca 2017 roku ta dyrektywa ma ograniczone wdrożenia.
Domyślnie przeglądarka wczytuje powiązany zasób z dowolnego punktu początkowego bez ograniczeń, chyba że skonfigurujesz zasadę z konkretną dyrektywą. Aby zastąpić ustawienie domyślne, użyj dyrektywy default-src
. Ta dyrektywa określa domyślne wartości dla wszystkich nieokreślonych dyrektyw, które kończą się na -src
. Jeśli na przykład ustawisz default-src
na https://example.com
i nie określisz dyrektywy font-src
, możesz wczytywać czcionki tylko z https://example.com
.
W tych dyrektywach nie jest używany znak default-src
jako wartość domyślna. Pamiętaj, że
nieustawienie tych wartości jest równoznaczne ze zezwoleniem na wszystko:
base-uri
form-action
frame-ancestors
plugin-types
report-uri
sandbox
Podstawowa składnia CSP
Aby używać dyrektyw CSP, umieść je w nagłówku HTTP, rozdzielając je dwukropkami. Pamiętaj, aby w jednym poleceniu podać wszystkie wymagane zasoby danego typu:
script-src https://host1.com https://host2.com
Poniżej znajduje się przykład wielu dyrektyw, w tym przypadku dla aplikacji internetowej, która wczytuje wszystkie zasoby z sieci dostarczania treści na adresie https://cdn.example.net
i nie używa treści ani wtyczek w ramce:
Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'
Szczegóły implementacji
Nowoczesne przeglądarki obsługują nagłówek Content-Security-Policy
bez prefiksu.
Oto zalecany nagłówek. Nagłówki X-WebKit-CSP
i X-Content-Security-Policy
, które możesz zobaczyć w samouczkach online, zostały wycofane.
CSP jest definiowany na podstawie poszczególnych stron. Musisz wysłać nagłówek HTTP z każdą odpowiedzią, którą chcesz chronić. Pozwala to dostosować zasady do konkretnych stron zgodnie z ich konkretnymi potrzebami. Jeśli np. jeden zestaw stron w Twojej witrynie zawiera przycisk +1, a inne nie, możesz zezwolić na wczytywanie kodu przycisku tylko wtedy, gdy jest to konieczne.
Lista źródeł w przypadku każdej dyrektywy jest elastyczna. Źródła możesz określić według schematu (data:
, https:
) lub z różnym zakresem szczegółowości: od nazwy hosta (example.com
, która pasuje do dowolnego źródła na tym hoście: dowolny schemat, dowolny port) do pełnego identyfikatora URI (https://example.com:443
, który pasuje tylko do HTTPS, tylko do example.com
i tylko do portu 443). Symbole wieloznaczne są akceptowane, ale tylko jako schemat, port lub w najbardziej lewej pozycji nazwy hosta: *://*.example.com:*
pasuje do wszystkich subdomen example.com
(ale nie do samej example.com
), używając dowolnego schematu na dowolnym porcie.
Lista źródeł obsługuje też 4 słowa kluczowe:
'none'
nie pasuje do niczego.'self'
pasuje do bieżącego źródła, ale nie do jego subdomen.'unsafe-inline'
umożliwia wstawianie kodu JavaScript i CSS. Więcej informacji znajdziesz w artykule Unikaj kodu w tekście.'unsafe-eval'
umożliwia stosowanie mechanizmów konwersji tekstu na kod JavaScript, takich jakeval
. Więcej informacji znajdziesz w artykule Unikajeval()
.
Te słowa kluczowe wymagają cudzysłowów. Na przykład script-src 'self'
(w cudzysłowie) autoryzuje wykonywanie kodu JavaScript z bieżącego hosta, a script-src self
(bez cudzysłowu) zezwala na wykonywanie kodu JavaScript z serwera o nazwie „self
” (a nie z bieżącego hosta), co prawdopodobnie nie jest tym, co masz na myśli.
Piaskownicy stron
Jest jeszcze jedna dyrektywa, o której warto wspomnieć: sandbox
. Jest on nieco inny niż inne, które analizowaliśmy, ponieważ nakłada ograniczenia na działania, które może wykonać strona, a nie na zasoby, które może wczytać. Jeśli występuje dyrektywa sandbox
, strona jest traktowana tak, jakby została załadowana w ramach elementu <iframe>
z atrybutem sandbox
. Może to mieć na nią różny wpływ: może na przykład wymusić unikalny element docelowy lub uniemożliwić przesyłanie formularzy. Jest to nieco wykraczające poza zakres tej strony, ale pełne informacje o obowiązujących atrybutach piaskownicy znajdziesz w sekcji „Piaskownia” specyfikacji HTML5.
Metatag
Preferowanym mechanizmem dostarczania przez dostawców usług internetowych jest nagłówek HTTP. Może jednak być przydatne ustawienie zasad na stronie bezpośrednio w sygnalizacji. Aby to zrobić, użyj tagu <meta>
z atrybutem http-equiv
:
<meta http-equiv="Content-Security-Policy" content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'">
Nie można go używać w przypadku kart frame-ancestors
, report-uri
i sandbox
.
Unikaj kodu wbudowanego
Pomimo że listy dozwolonych domen używane w regułach CSP są bardzo skuteczne, nie mogą one rozwiązać największego zagrożenia związanego z atakami XSS, czyli wstrzykiwania skryptów w instrukcjach.
Jeśli atakujący wstrzyknie tag skryptu, który zawiera bezpośrednio złośliwy ładunek (np. <script>sendMyDataToEvilDotCom()</script>
), przeglądarka nie będzie mogła odróżnić go od prawidłowego tagu skryptu wstawionego w tekście. CSP rozwiązuje ten problem, całkowicie zakazując skryptów wbudowanych.
Zakaz obejmuje nie tylko skrypty osadzone bezpośrednio w tagach script
, ale też wbudowane moduły obsługi zdarzeń i adresy URL javascript:
. Musisz przenieść zawartość tagów script
do pliku zewnętrznego i zastąpić adresy URL javascript:
oraz <a ...
onclick="[JAVASCRIPT]">
odpowiednimi wywołaniami addEventListener()
. Możesz na przykład przepisać te informacje z:
<script>
function doAmazingThings() {
alert('YOU ARE AMAZING!');
}
</script>
<button onclick='doAmazingThings();'>Am I amazing?</button>
na coś takiego:
<!-- amazing.html -->
<script src='amazing.js'></script>
<button id='amazing'>Am I amazing?</button>
// amazing.js
function doAmazingThings() {
alert('YOU ARE AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('amazing')
.addEventListener('click', doAmazingThings);
});
Napisany od nowa kod jest zgodny nie tylko z CSP, ale też ze wskazówkami dotyczącymi najlepszych praktyk w zakresie projektowania stron internetowych. Wbudowany JavaScript miesza strukturę i zachowanie w sposób, który powoduje, że kod staje się niejasny. Jest też trudniejsza do zcacheowania i skompilowania. Przeniesienie kodu do zasobów zewnętrznych poprawia działanie stron.
Zalecamy też przeniesienie tagów i atrybutów style
do zewnętrznych arkuszy stylów, aby chronić witrynę przed atakami polegającymi na wydobyciu danych za pomocą języka CSS.
Jak tymczasowo zezwalać na skrypty i style w kodzie źródłowym
Aby włączyć skrypty i style w dokumencie, dodaj 'unsafe-inline'
jako dozwolone źródło w dyrektywie script-src
lub style-src
. Poziom 2 CSP umożliwia też dodawanie określonych skryptów wbudowanych do listy dozwolonych za pomocą liczby jednorazowej (numeru użytego raz) lub hasha w ten sposób:
Aby użyć nonce, nadaj tagowi skryptu atrybut nonce. Jego wartość musi być zgodna z wartością na liście zaufanych źródeł. Na przykład:
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
// Some inline code I can't remove yet, but need to as soon as possible.
</script>
Dodaj wartość losową do dyrektywy script-src
po słowie kluczowym nonce-
:
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
W przypadku każdego żądania strony należy wygenerować nowe losowe ciągi znaków, które nie powinny być możliwe do odgadnięcia.
Hashe działają podobnie. Zamiast dodawać kod do tagu skryptu, utwórz skrót SHA skryptu i dodaj go do dyrektywy script-src
.
Jeśli na przykład Twoja strona zawierała ten tekst:
<script>alert('Hello, world.');</script>
Polityka musi zawierać te informacje:
Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='
Prefiks sha*-
określa algorytm generujący hasz. W poprzednim przykładzie użyto atrybutu sha256-
, ale CSP obsługuje też atrybuty sha384-
i sha512-
. Podczas generowania hasha pomiń tagi <script>
. Wielkość liter i odstępy mają znaczenie, w tym odstępy na początku i na końcu.
Rozwiązania do generowania haszy SHA są dostępne w dowolnej liczbie języków. W Chrome 40 lub nowszym możesz otworzyć Narzędzia deweloperskie, a następnie ponownie załadować stronę. Karta Konsola zawiera komunikaty o błędach z prawidłowym ciągiem znaków identyfikatora SHA-256 dla każdego skryptu wbudowanego.
Unikaj eval()
Nawet jeśli atakujący nie może wstrzyknąć skryptu bezpośrednio, może zmusić Twoją aplikację do przekształcenia tekstu wejściowego w wykonalny skrypt JavaScript i wykonanie go w swoim imieniu. eval()
, new Function()
,
setTimeout([string], …)
i setInterval([string], ...)
to wektory, których atakujący mogą używać do uruchamiania złośliwego kodu za pomocą wstrzykniętego tekstu. Domyślną reakcją CSP na to ryzyko jest całkowite zablokowanie wszystkich tych wektorów.
Ma to następujące konsekwencje dla sposobu tworzenia aplikacji:
- Musisz przeanalizować dane JSON za pomocą wbudowanej funkcji
JSON.parse
zamiast polegać na funkcjieval
. Bezpieczne operacje na danych JSON są dostępne w każdej przeglądarce od IE8. Musisz zastąpić wywołania funkcji
setTimeout
lubsetInterval
wywołaniami funkcji wbudowanych zamiast ciągów tekstowych. Jeśli na przykład Twoja strona zawiera te elementy:setTimeout("document.querySelector('a').style.display = 'none';", 10);
Zmień to na:
setTimeout(function () { document.querySelector('a').style.display = 'none'; }, 10); ```
Unikaj szablonów wbudowanych w czasie wykonywania. Wiele bibliotek szablonów często używa
new Function()
, aby przyspieszyć generowanie szablonów w czasie wykonywania, co umożliwia ocenę złośliwego tekstu. Niektóre frameworki obsługują CSP, używając domyślnie niezawodnego parsowania, a w przeciwnym razie używająceval
. Dyrektywa ng-csp w AngularJS jest tego dobrym przykładem. Zamiast tego zalecamy użycie języka szablonów, który oferuje wstępną kompilację, np. Handlebars. Wstępna kompilacja szablonów może zwiększyć szybkość działania witryny i ułatwić użytkownikom korzystanie z niej. Pozwoli też zwiększyć bezpieczeństwo witryny.
Jeśli funkcja eval()
lub inne funkcje konwersji tekstu na JavaScript są niezbędne w Twojej aplikacji, możesz je włączyć, dodając 'unsafe-eval'
jako dozwolone źródło w instrukcji script-src
. Zdecydowanie odradzamy to ze względu na ryzyko związane z wstrzyknięciem kodu.
Zgłaszanie naruszeń zasad
Aby powiadomić serwer o błędach, które mogą umożliwiać wstrzyknięcie złośliwego kodu, możesz zlecić przeglądarce wysyłanie POST
raportów o naruszeniu w formacie JSON do lokalizacji określonej w instrukcji report-uri
:
Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
Te raporty wyglądają tak:
{
"csp-report": {
"document-uri": "http://example.org/page.html",
"referrer": "http://evil.example.com/",
"blocked-uri": "http://evil.example.com/evil.js",
"violated-directive": "script-src 'self' https://apis.google.com",
"original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
}
}
Raport zawiera przydatne informacje, które ułatwiają znalezienie przyczyny naruszenia zasad, w tym stronę, na której wystąpiło naruszenie (document-uri
), identyfikator tej strony (referrer
), zasób, który naruszył zasady (blocked-uri
), konkretną dyrektywę, którą naruszono (violated-directive
), oraz pełne zasady (original-policy
).
Tylko raporty
Jeśli dopiero zaczynasz korzystać z CSP, zalecamy użycie trybu tylko z raportami, aby ocenić stan aplikacji przed zmianą zasad. Aby to zrobić, zamiast wysyłać nagłówek Content-Security-Policy
, wyślij nagłówek Content-Security-Policy-Report-Only
:
Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
Zasady określone w trybie tylko do raportowania nie blokują zasobów objętych ograniczeniami, ale wysyłają raporty o naruszeniu do określonej lokalizacji. Możesz nawet wysłać oba nagłówki, aby egzekwować jedną zasadę, a jednocześnie monitorować drugą. To świetny sposób na przetestowanie zmian w Twoim CSP podczas egzekwowania bieżących zasad: włącz raportowanie nowych zasad, monitoruj raporty o naruszeniu zasad i naprawiaj błędy, a gdy będziesz zadowolony/zadowolona z nowych zasad, zacznij je egzekwować.
Praktyczne zastosowanie
Pierwszym krokiem w kierunku stworzenia zasad dla aplikacji jest ocena zasobów, które wczytuje. Gdy poznasz strukturę aplikacji, utwórz zasady na podstawie jej wymagań. W następnych sekcjach omawiamy kilka typowych przypadków użycia oraz proces podejmowania decyzji o ich obsłudze zgodnie z wytycznymi CSP.
Widgety mediów społecznościowych
- Przycisk polubienia na Facebooku można wdrożyć na kilka sposobów. Zalecamy korzystanie z wersji
<iframe>
, aby oddzielić ją od reszty witryny. Do prawidłowego działania potrzebuje dyrektywychild-src https://facebook.com
. - Przycisk Tweeta aplikacji X wymaga dostępu do skryptu.
Przenieś skrypt do zewnętrznego pliku JavaScript i użyj dyrektywy
script-src https://platform.twitter.com; child-src https://platform.twitter.com
. - Inne platformy mają podobne wymagania i można je rozwiązać w podobny sposób.
Aby przetestować te zasoby, zalecamy ustawienie
default-src
na'none'
i obserwowanie konsoli, aby określić, które zasoby należy włączyć.
Aby użyć wielu widżetów, połącz dyrektywy w ten sposób:
script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com
Blokada
W przypadku niektórych witryn musisz mieć pewność, że można wczytywać tylko zasoby lokalne. W tym przykładzie tworzymy zasadę CSP dla witryny bankowej, zaczynając od domyślnej zasady, która blokuje wszystko (default-src 'none'
).
Witryna wczytuje wszystkie obrazy, styl i skrypt z CDN na stronie https://cdn.mybank.net
, a do połączenia z witryną https://api.mybank.com/
używa XHR do pobierania danych. Używa ramek, ale tylko na stronach dostępnych lokalnie (bez stron zewnętrznych). W witrynie nie ma Flasha, czcionek ani innych dodatków. Najbardziej restrykcyjny nagłówek CSP, który może wysłać, to:
Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'
Tylko SSL
Poniżej znajduje się przykładowy CSP dla administratora forum, który chce mieć pewność, że wszystkie zasoby na forum są wczytywane tylko za pomocą bezpiecznych kanałów, ale nie ma doświadczenia w kodowaniu i nie ma zasobów, aby przepisać oprogramowanie forum firmy zewnętrznej pełne skryptów i stylów w ciele dokumentu:
Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'
Chociaż w sekcji default-src
występuje zmienna https:
, skrypt i style nie dziedziczą automatycznie tego źródła. Każda dyrektywa zastępuje domyślne ustawienie danego typu zasobu.
Rozwój standardu CSP
Polityka bezpieczeństwa treści na poziomie 2 to zalecany standard W3C. Grupa robocza ds. bezpieczeństwa aplikacji internetowych W3C opracowuje kolejną wersję specyfikacji, czyli zasady bezpieczeństwa treści na poziomie 3.
Aby śledzić dyskusję na temat tych nadchodzących funkcji, zapoznaj się z archiwami public-webappsec@ mailing list.