XSS, możliwość wstrzykiwania złośliwych skryptów do aplikacji internetowej największą lukę w zabezpieczeniach sieci od ponad 10 lat.
Polityka bezpieczeństwa treści (CSP)
to dodatkowa warstwa zabezpieczeń, która pomaga ograniczyć XSS. Aby skonfigurować CSP,
dodaj do strony internetowej nagłówek HTTP Content-Security-Policy
i ustaw wartości, które
kontrolować, jakie zasoby klient użytkownika może wczytywać na tej stronie.
Na tej stronie dowiesz się, jak używać CSP na podstawie liczb jednorazowych lub haszy, aby zniwelować XSS. zamiast często używanych dostawców CSP opartych na liście dozwolonych hosta, którzy często opuszczają stronę narażone na XSS, ponieważ mogą zostać ominięte w większości konfiguracji.
Kluczowe hasło: liczba jednorazowa to liczba losowa używana tylko raz, której można użyć do oznaczenia
<script>
jako zaufany.
Kluczowy termin: funkcja skrótu to funkcja matematyczna, która przekształca dane wejściowe.
w skompresowaną wartość liczbową tzw. hash. Możesz użyć skrótu
(na przykład SHA-256), aby oznaczyć wbudowany
<script>
jako zaufany.
Polityka bezpieczeństwa treści opartą na liczbach jednorazowych lub haszach jest często nazywana rygorystycznego CSP. Jeśli aplikacja używa rygorystycznego CSP, hakerzy, którzy znajdują kod HTML które zwykle nie mogą użyć ich do wymuszenia uruchomienia przeglądarki złośliwe skrypty w dokumencie z luką w zabezpieczeniach. Dzieje się tak, ponieważ tylko rygorystyczne zasady CSP zezwala na zaszyfrowane skrypty lub skrypty z prawidłową wartością jednorazową wygenerowaną w tagu więc osoby przeprowadzające atak nie mogą wykonać skryptu bez znajomości poprawnej wartości jednorazowej dla danej odpowiedzi.
Dlaczego należy używać rygorystycznego CSP?
Jeśli masz już w witrynie wartości CSP podobne do script-src www.googleapis.com
,
ale prawdopodobnie nie będzie skuteczna w innych witrynach. Ten typ CSP jest nazywany
dodanie CSP do listy dozwolonych. Wymagają dużego dostosowania i mogą być
ominięte przez osoby przeprowadzające atak.
Rygorystyczne dostawcy usług CSP opierające się na kryptograficznych liczbach jednorazowych lub haszach, unikają tych pułapek.
Ścisła struktura CSP
Podstawowa rygorystyczna zasada Content Security Policy używa jednej z poniższych odpowiedzi HTTP nagłówki:
Rygorystyczne zasady CSP oparte na identyfikatorach całkowitych
Content-Security-Policy:
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Rygorystyczne zasady CSP oparte na haszach
Content-Security-Policy:
script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
Te właściwości sprawiają, że CSP, taki jak ten, jest „ścisły” i bezpieczne:
- Używa liczb jednorazowych
'nonce-{RANDOM}'
lub haszy'sha256-{HASHED_INLINE_SCRIPT}'
. wskazuje tagi<script>
, które deweloper witryny może wykorzystać do wykonania w przeglądarce użytkownika. - Ustawia wartość
'strict-dynamic'
aby uprościć wdrażanie CSP opartego na liczbie jednorazowej lub opartej na haszach przez automatyczne co umożliwia wykonywanie skryptów tworzonych przez zaufany skrypt. To także umożliwia korzystanie z większości zewnętrznych bibliotek i widżetów JavaScript. - Adres URL nie jest oparty na listach dozwolonych adresów URL, typowych omijania CSP.
- Blokuje niezaufane skrypty wbudowane, takie jak wbudowane moduły obsługi zdarzeń lub
javascript:
Identyfikatory URI. - Blokuje on
object-src
możliwość wyłączania niebezpiecznych wtyczek, takich jak Flash. - Ogranicza działanie usługi
base-uri
do blokowania wstrzykiwania<base>
tagów. Zapobiega to przed zmianą lokalizacji skryptów wczytywanych ze względnych adresów URL.
Stosuj rygorystyczny standard CSP
Aby przyjąć rygorystyczne zasady CSP, musisz:
- Określ, czy aplikacja powinna ustawić CSP na podstawie liczby jednorazowej czy opartej na haszowaniu.
- Skopiuj wartość CSP z sekcji Ścisła struktura CSP i ustaw ją. jako nagłówek odpowiedzi w aplikacji.
- Refaktoryzacja szablonów HTML i kodu po stronie klienta w celu usunięcia wzorców, które niezgodne z CSP.
- Wdróż CSP.
Możesz używać Lighthouse,
(wersja 7.3.0 i nowsza z flagą --preset=experimental
) Audyt Sprawdzone metody
w trakcie tego procesu, by sprawdzić, czy witryna ma CSP
na tyle rygorystyczne, aby były skuteczne w odniesieniu do XSS.
Krok 1. Określ, czy potrzebujesz CSP opartego na liczbie jednorazowej czy opartej na haszach
Oto jak działają te 2 rodzaje rygorystycznego CSP:
CSP oparty na liczbie jednorazowej
W przypadku CSP opartego na liczbie jednorazowej generujesz losową liczbę w czasie działania i uwzględniasz ją w do CSP i powiąż go z każdym tagiem skryptu na stronie. Osoba przeprowadzająca atak nie może umieścić ani uruchomić złośliwego skryptu na stronie, ponieważ musiałby on odgadnij poprawną losową liczbę dla tego skryptu. Działa tylko wtedy, gdy liczba jest nie do odgadnięcia i generuje nowe w czasie działania dla każdej odpowiedzi.
Używaj CSP dla stron HTML renderowanych na serwerze. W przypadku tych stron możesz utworzyć nową losową liczbę dla każdej odpowiedzi.
CSP oparty na haszach
W przypadku CSP opartych na haszach do CSP jest dodawany hasz każdego wbudowanego tagu skryptu. Każdy skrypt ma inny hasz. Osoba przeprowadzająca atak nie może zawierać ani uruchamiać złośliwego oprogramowania skrypt na stronie, bo jego hasz musi znajdować się CSP.
Używaj CSP opartych na haszach w przypadku stron HTML wyświetlanych statycznie lub stron, które mają być w pamięci podręcznej. Na przykład w przypadku witryny jednostronicowej możesz użyć CSP opartego na haszowaniu aplikacji utworzonych na platformach takich jak Angular, React itp., statycznie wyświetlany bez renderowania po stronie serwera.
Krok 2. Ustaw rygorystyczny CSP i przygotuj skrypty
Podczas konfigurowania CSP masz kilka opcji:
- Tryb „tylko raport” (
Content-Security-Policy-Report-Only
) lub tryb egzekwowania (Content-Security-Policy
). W trybie „tylko do raportu” CSP nie blokuje zasobów, więc nic w witrynie nie działa, ale mogą pojawić się błędy i zgłoszenia dotyczące czegoś, co byłoby zablokowane. Lokalnie, gdy jesteś przy ustawianiu CSP, nie ma to znaczenia, ponieważ oba tryby pokazują w konsoli przeglądarki. Tryb wymuszania pomoże Ci znaleźć blokowanych przez CSP wersji roboczej, ponieważ zablokowanie zasobu może sprawić, że strona wygląda na uszkodzoną. Tryb „Tylko raport” staje się najbardziej przydatny na późniejszym etapie (patrz Krok 5). - Nagłówek lub tag HTML
<meta>
. W przypadku programowania lokalnego tag<meta>
może być bardziej jest to wygodne do dostosowania CSP i szybkiego sprawdzania, jak wpływa on na witrynę. Pamiętaj jednak, że:- Później podczas wdrażania CSP w środowisku produkcyjnym zalecamy ustawienie nagłówek HTTP.
- Jeśli chcesz ustawić CSP w trybie „Tylko raport”, ustaw go jako nagłówek, ponieważ metatagi CSP nie obsługują trybu tylko do raportowania.
Ustaw następującą odpowiedź HTTP Content-Security-Policy
nagłówek w aplikacji:
Content-Security-Policy: script-src 'nonce-{RANDOM}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
Generowanie liczby jednorazowej dla CSP
Liczba jednorazowa to liczba losowa używana tylko raz na wczytanie strony. Oparta na liczbie jednorazowej CSP może ograniczyć XSS tylko wtedy, gdy atakujący nie mogą odgadnąć wartości liczby jednorazowej. O Wartość jednorazowa CSP musi być:
- Silna kryptograficznie wartość losowa (najlepiej o długości co najmniej 128 bitów).
- Nowe generowane na potrzeby każdej odpowiedzi
- Z kodowaniem Base64
Oto kilka przykładów dodawania liczby jednorazowej CSP w platformach po stronie serwera:
- Django (Python)
- Szybka kopia (JavaScript):
const app = express(); app.get('/', function(request, response) { // Generate a new random nonce value for every response. const nonce = crypto.randomBytes(16).toString("base64"); // Set the strict nonce-based CSP response header const csp = `script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none';`; response.set("Content-Security-Policy", csp); // Every <script> tag in your application should set the `nonce` attribute to this value. response.render(template, { nonce: nonce }); });
Dodaj atrybut nonce
do elementów <script>
W przypadku CSP opartego na liczbie jednorazowej każdy element <script>
musi
mają atrybut nonce
pasujący do liczby jednorazowej losowej
określona w nagłówku CSP. Wszystkie skrypty mogą mieć taką samą wartość
liczba jednorazowa. Pierwszym krokiem jest dodanie tych atrybutów do wszystkich skryptów, aby
CSP na to pozwala.
Ustaw następującą odpowiedź HTTP Content-Security-Policy
nagłówek w aplikacji:
Content-Security-Policy: script-src 'sha256-{HASHED_INLINE_SCRIPT}' 'strict-dynamic'; object-src 'none'; base-uri 'none';
W przypadku wielu skryptów w tekście składnia wygląda tak:
'sha256-{HASHED_INLINE_SCRIPT_1}' 'sha256-{HASHED_INLINE_SCRIPT_2}'
Dynamiczne ładowanie skryptów źródłowych
Skróty CSP są obsługiwane tylko w przeglądarkach w przypadku skryptów wbudowanych, wszystkie skrypty zewnętrzne muszą być ładowane dynamicznie za pomocą wbudowanego skryptu. Szyfrowanie skryptów źródłowych nie jest obsługiwane w różnych przeglądarkach.
<script> var scripts = [ 'https://example.org/foo.js', 'https://example.org/bar.js']; scripts.forEach(function(scriptUrl) { var s = document.createElement('script'); s.src = scriptUrl; s.async = false; // to preserve execution order document.head.appendChild(s); }); </script>
<script src="https://example.org/foo.js"></script> <script src="https://example.org/bar.js"></script>
Uwagi na temat wczytywania skryptu
Przykładowy skrypt wbudowany dodaje element s.async = false
, aby zapewnić
który jest wykonywany przez foo
przed bar
, nawet jeśli
Najpierw wczytuje się bar
. W tym fragmencie kodu s.async = false
nie blokuje parsera podczas wczytywania skryptów, ponieważ skrypty są
dodawane dynamicznie. Parser zatrzymuje się tylko podczas wykonywania skryptów, jak
tak jak w przypadku async
skryptów. Jednak dzięki temu fragmentowi
pamiętaj:
-
Jeden lub oba skrypty mogą być wykonywane, zanim dokument zostanie ukończony
pobieranie. Jeśli chcesz, aby dokument był gotowy przed
skryptów, poczekaj na zdarzenie
DOMContentLoaded
przed dołączysz skrypty. Jeśli powoduje to problem z wydajnością, ponieważ pobieranie skryptów nie rozpoczyna się wystarczająco wcześnie, użyj wstępnego wczytywania tagów na stronie. -
defer = true
nic nie robi. Jeśli tego potrzebujesz w celu uruchomienia działania skryptu, w razie potrzeby uruchom go ręcznie.
Krok 3. Refaktoryzacja szablonów HTML i kodu po stronie klienta
Wbudowane moduły obsługi zdarzeń (np. onclick="…"
, onerror="…"
) i identyfikatory URI JavaScript
(<a href="javascript:…">
) może służyć do uruchamiania skryptów. Oznacza to, że
Jeśli atakujący znajdzie błąd XSS, może wstrzyknąć taki kod HTML i wykonać złośliwe oprogramowanie
JavaScriptu. Protokół CSP oparty na liczbie jednorazowej lub opartej na haszach nie zezwala na używanie tego rodzaju znaczników.
Jeśli Twoja witryna korzysta z któregoś z tych wzorców, musisz je przekształcić, aby były bezpieczniejsze
i innych rozwiązań.
Jeśli w poprzednim kroku włączono CSP, naruszenia zasad dotyczących CSP pojawią się tutaj: za każdym razem, gdy CSP zablokuje niezgodny wzorzec.
W większości przypadków rozwiązanie problemu jest proste:
Refaktoryzacja wbudowanych modułów obsługi zdarzeń
<span id="things">A thing.</span> <script nonce="${nonce}"> document.getElementById('things').addEventListener('click', doThings); </script>
<span onclick="doThings();">A thing.</span>
Refaktoryzacja identyfikatorów URI javascript:
<a id="foo">foo</a> <script nonce="${nonce}"> document.getElementById('foo').addEventListener('click', linkClicked); </script>
<a href="javascript:linkClicked()">foo</a>
Usuń eval()
z JavaScriptu
Jeśli Twoja aplikacja używa eval()
do konwertowania serializacji ciągów JSON na JS
musisz dokonać refaktoryzacji takich instancji na JSON.parse()
, co również
szybciej.
Jeśli nie możesz usunąć wszystkich zastosowań eval()
, nadal możesz ustawić rygorystyczną liczbę jednorazową
CSP, ale musisz używać słowa kluczowego CSP 'unsafe-eval'
, dzięki któremu
są nieco mniej bezpieczne.
Te i inne przykłady takich refaktoryzacji znajdziesz w tym rygorystycznym CSP codelab:
Krok 4 (opcjonalny). Dodaj wartości zastępcze na potrzeby obsługi starych wersji przeglądarek
Jeśli musisz obsługiwać starsze wersje przeglądarek:
- Użycie elementu
strict-dynamic
wymaga dodania elementuhttps:
jako kreacji zastępczej dla wcześniejszego stanu Safari. Gdy to zrobisz:- Wszystkie przeglądarki, które obsługują
strict-dynamic
, ignorują kreację zastępcząhttps:
. nie obniży to skuteczności zasady. - W starszych przeglądarkach skrypty pozyskane z zewnątrz mogą być wczytywane tylko wtedy, gdy pochodzą z
punkt początkowy HTTPS. Jest to mniej bezpieczne niż rygorystyczne CSP, ale
zapobiega niektórym częstym przyczynom XSS, takim jak wstrzykiwanie identyfikatorów URI typu
javascript:
.
- Wszystkie przeglądarki, które obsługują
- Aby zapewnić zgodność ze starszymi wersjami przeglądarek (ponad 4 lata), możesz dodać
unsafe-inline
jako kreacji zastępczej. Wszystkie ostatnie przeglądarki ignorująunsafe-inline
jeśli jest dostępna wartość jednorazowa lub hasz CSP.
Content-Security-Policy:
script-src 'nonce-{random}' 'strict-dynamic' https: 'unsafe-inline';
object-src 'none';
base-uri 'none';
Krok 5. Wdróż CSP
Jeśli upewnisz się, że CSP nie blokuje prawidłowych skryptów w w lokalnym środowisku programistycznym, możesz wdrożyć CSP na etapie przejściowym, a następnie środowisko produkcyjne:
- (Opcjonalnie) Wdróż CSP w trybie tylko do raportu za pomocą
Nagłówek
Content-Security-Policy-Report-Only
. Tryb „Tylko raportowanie” jest przydatny przetestuj potencjalnie zmieniającą się zmianę, tak jak w przypadku nowego CSP w środowisku produkcyjnym, zacząć egzekwować ograniczenia CSP. W trybie „tylko do raportu” CSP nie wpływa na działanie aplikacji, ale przeglądarka nadal generuje błędy konsoli i zgłoszeniami o naruszeniach w przypadku wzorców niezgodnych z CSP. aby zobaczyć, co niekorzystnie wpłynie na użytkowników. Więcej więcej informacji zawiera Reporting API. - Jeśli masz pewność, że CSP nie zepsuje witryny użytkownikom,
wdrożyć CSP za pomocą nagłówka odpowiedzi
Content-Security-Policy
. Śr zalecamy skonfigurowanie CSP za pomocą nagłówka HTTP po stronie serwera, jest bezpieczny niż tag<meta>
. Gdy wykonasz ten krok, CSP zacznie się aby chronić aplikację przed atakami XSS.
Ograniczenia
Rygorystyczne zasady CSP zapewniają zwykle silny dodatkowy poziom zabezpieczeń, który pomaga
i ograniczanie XSS. W większości przypadków CSP znacznie zmniejsza powierzchnię ataku,
odrzucanie niebezpiecznych wzorców, takich jak identyfikatory URI javascript:
. Jednakże,
używanego CSP (liczba jednorazowa, hasze, z elementem 'strict-dynamic'
lub bez niego),
to przypadki, w których CSP nie chroni Twojej aplikacji również:
- Jeśli generujesz jednorazowy skrypt, ale wstrzykujesz go bezpośrednio do treści lub
Parametr
src
tego elementu<script>
. - Jeśli występują wstrzykiwanie w lokalizacji skryptów tworzonych dynamicznie
(
document.createElement('script')
), w tym w wszystkich funkcjach biblioteki tworzący węzły DOM (script
) na podstawie wartości ich argumentów. Ten zawiera kilka popularnych interfejsów API, takich jak.html()
jQuery, a także.get()
i.post()
w jQuery < 3.0 - Czy w starych aplikacjach AngularJS istnieją wstrzykiwanie szablonów. Osoba przeprowadzająca atak który można wstrzykiwać do szablonu AngularJS, może go używać do wykonywanie dowolnego JavaScriptu.
- Jeśli zasada zawiera
'unsafe-eval'
, wstrzykiwanie tekstu weval()
setTimeout()
i kilka innych rzadko używanych interfejsów API.
Programiści i inżynierowie ds. bezpieczeństwa powinni zwracać szczególną uwagę na podczas sprawdzania kodu i kontroli bezpieczeństwa. Więcej informacji znajdziesz na znajdziesz je w artykule Content Security Policy: A Successful Mess Between Hardening and Łatigation.
.Więcej informacji
- CSP nie żyje, niech żyje CSP! Brak bezpieczeństwa białych list i przyszłość Zasad bezpieczeństwa treści
- Osoba oceniająca CSP
- Konferencja LocoMoco: Content Security Policy – połączenie wzmacniania i łagodzenia skutków problemu
- Prezentacja Google I/O: zabezpieczanie aplikacji internetowych dzięki nowoczesnym funkcjom platformy