Skryptowanie na wielu stronach oparte na DOM (DOM XSS) występuje, gdy dane z źródła kontrolowanego przez użytkownika (np. nazwa użytkownika lub adres URL przekierowania pobrany z fragmentu adresu URL) docierają do ujścia, czyli funkcji takiej jak eval()
lub ustawiacza właściwości takiego jak .innerHTML
, który może wykonywać dowolny kod JavaScript.
DOM XSS to jedna z najczęstszych luk w zabezpieczeniach internetowych, która często jest nieumyślnie wprowadzana do aplikacji przez zespoły programistów. Trusted Types zapewniają narzędzia do pisania, sprawdzania pod kątem bezpieczeństwa i utrzymywania aplikacji wolnych od luk w zabezpieczeniach DOM XSS, ponieważ domyślnie zabezpieczają niebezpieczne funkcje interfejsu Web API. Trusted Types są dostępne jako polyfill w przypadku przeglądarek, które jeszcze ich nie obsługują.
Tło
Od wielu lat DOM XSS jest jednym z najczęstszych i najgroźniejszych luk w zabezpieczeniach internetowych.
Istnieją 2 rodzaje skryptów cross-site scripting. Niektóre luki w zabezpieczeniach XSS są spowodowane przez kod po stronie serwera, który w niebezpieczny sposób tworzy kod HTML stanowiący witrynę. Inne mają przyczynę główną po stronie klienta, gdzie kod JavaScript wywołuje niebezpieczne funkcje z treściami kontrolowanymi przez użytkownika.
Aby zapobiec atakom XSS po stronie serwera, nie generuj kodu HTML przez łączenie ciągów znaków. Zamiast tego używaj bezpiecznych bibliotek szablonów z automatycznym kontekstowym unikaniem znaków specjalnych oraz zasad CSP opartych na jednorazowym kodzie, aby dodatkowo ograniczyć liczbę błędów.
Przeglądarki mogą teraz też pomagać w zapobieganiu atakom XSS opartym na DOM po stronie klienta, korzystając z dyrektywy Trusted Types.
Wprowadzenie do interfejsu API
Zaufane typy działają przez blokowanie tych ryzykownych funkcji ujścia. Niektóre z nich mogą być Ci już znane, ponieważ dostawcy przeglądarek i frameworków internetowych ze względów bezpieczeństwa odradzają korzystanie z tych funkcji.
- Manipulowanie skryptem:
<script src>
i ustawianie zawartości tekstowej elementów<script>
- Generowanie kodu HTML z ciągu znaków:
- Wykonywanie treści wtyczki:
- Kompilacja kodu JavaScript w czasie działania:
eval
setTimeout
setInterval
new Function()
Zaufane typy wymagają przetworzenia danych przed przekazaniem ich do tych funkcji ujścia. Użycie tylko ciągu tekstowego nie działa, ponieważ przeglądarka nie wie, czy dane są wiarygodne:
anElement.innerHTML = location.href;
Aby wskazać, że dane zostały bezpiecznie przetworzone, utwórz specjalny obiekt – zaufany typ.
anElement.innerHTML = aTrustedHTML;
TrustedHTML
w przypadku elementów docelowych, które oczekują fragmentów kodu HTML. Istnieją też obiekty TrustedScript
i TrustedScriptURL
dla innych wrażliwych miejsc docelowych.
Dyrektywa Trusted Types znacznie zmniejsza powierzchnię ataku typu DOM XSS w Twojej aplikacji. Upraszcza to sprawdzanie zabezpieczeń i umożliwia egzekwowanie w przeglądarce w czasie działania programu sprawdzania zabezpieczeń opartych na typach, które są wykonywane podczas kompilowania, lintowania lub łączenia kodu w pakiety.
Jak korzystać z zaufanych typów
Przygotowywanie raportów o naruszeniach standardu Content Security Policy
Możesz wdrożyć kolektor raportów, np. reporting-api-processor lub go-csp-collector (oba są dostępne na licencji open source), albo użyć jednego z odpowiedników komercyjnych. Możesz też dodać logowanie niestandardowe i debugować naruszenia w przeglądarce za pomocą interfejsu ReportingObserver:
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
if (report.type !== 'csp-violation' ||
report.body.effectiveDirective !== 'require-trusted-types-for') {
continue;
}
const violation = report.body;
console.log('Trusted Types Violation:', violation);
// ... (rest of your logging and reporting logic)
}
}, { buffered: true });
observer.observe();
lub dodając detektor zdarzeń:
document.addEventListener('securitypolicyviolation',
console.error.bind(console));
Dodawanie nagłówka CSP tylko do raportowania
Dodaj ten nagłówek odpowiedzi HTTP do dokumentów, które chcesz przenieść do Trusted Types:
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Obecnie wszystkie naruszenia są zgłaszane do //my-csp-endpoint.example
, ale witryna nadal działa. W następnej sekcji wyjaśnimy, jak działa //my-csp-endpoint.example
.
Identyfikowanie naruszeń dotyczących zaufanych typów
Od tej pory za każdym razem, gdy zaufane typy wykryją naruszenie, przeglądarka wysyła raport do skonfigurowanego report-uri
. Jeśli na przykład aplikacja przekaże ciąg znaków do innerHTML
, przeglądarka wyśle ten raport:
{
"csp-report": {
"document-uri": "https://my.url.example",
"violated-directive": "require-trusted-types-for",
"disposition": "report",
"blocked-uri": "trusted-types-sink",
"line-number": 39,
"column-number": 12,
"source-file": "https://my.url.example/script.js",
"status-code": 0,
"script-sample": "Element innerHTML <img src=x"
}
}
Oznacza to, że w pliku https://my.url.example/script.js
w wierszu 39 wywołano funkcję innerHTML
z ciągiem znaków zaczynającym się od <img src=x
. Te informacje powinny pomóc Ci zawęzić obszar kodu, który może wprowadzać DOM XSS i wymagać zmiany.
Naprawianie naruszeń
Naruszenie zasad dotyczących zaufanych typów można usunąć na kilka sposobów. Możesz usunąć problematyczny kod, użyć biblioteki, utworzyć zasadę zaufanych typów lub, w ostateczności, utworzyć zasadę domyślną.
Zmodyfikuj kod naruszający zasady
Możliwe, że niezgodny kod nie jest już potrzebny lub można go przepisać bez funkcji, które powodują naruszenia:
el.textContent = ''; const img = document.createElement('img'); img.src = 'xyz.jpg'; el.appendChild(img);
el.innerHTML = '<img src=xyz.jpg>';
Korzystanie z biblioteki
Niektóre biblioteki generują już zaufane typy, które możesz przekazywać do funkcji ujścia. Możesz na przykład użyć biblioteki DOMPurify, aby oczyścić fragment kodu HTML i usunąć z niego ładunki XSS.
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});
DOMPurify obsługuje zaufane typy i zwraca oczyszczony kod HTML opakowany w obiekt TrustedHTML
, dzięki czemu przeglądarka nie generuje naruszenia.
Tworzenie zasady Trusted Types
Czasami nie można usunąć kodu powodującego naruszenie zasad i nie ma biblioteki, która mogłaby oczyścić wartość i utworzyć dla Ciebie zaufany typ. W takich przypadkach możesz samodzielnie utworzyć obiekt typu zaufanego.
Najpierw utwórz zasady. Zasady to fabryki zaufanych typów, które wymuszają określone reguły zabezpieczeń na danych wejściowych:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
createHTML: string => string.replace(/\</g, '<')
});
}
Ten kod tworzy zasadę o nazwie myEscapePolicy
, która może tworzyć obiekty TrustedHTML
za pomocą funkcji createHTML()
. Zdefiniowane reguły stosują do znaków <
kodowanie HTML, aby zapobiec tworzeniu nowych elementów HTML.
Zasady te możesz stosować w ten sposób:
const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML); // true
el.innerHTML = escaped; // '<img src=x onerror=alert(1)>'
Używanie zasady domyślnej
Czasami nie możesz zmienić kodu, który narusza zasady, np. jeśli ładujesz bibliotekę innej firmy z sieci CDN. W takim przypadku użyj zasady domyślnej:
if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
trustedTypes.createPolicy('default', {
createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
});
}
Zasady o nazwie default
są używane wszędzie tam, gdzie w miejscu docelowym, które akceptuje tylko typ zaufany, używany jest ciąg znaków.
Przełączanie na egzekwowanie standardu Content Security Policy
Gdy aplikacja przestanie generować naruszenia, możesz zacząć egzekwować stosowanie zaufanych typów:
Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example
Teraz, niezależnie od tego, jak złożona jest Twoja aplikacja internetowa, jedyną rzeczą, która może wprowadzić lukę w zabezpieczeniach DOM XSS, jest kod w jednej z Twoich zasad. Możesz jeszcze bardziej ograniczyć to ryzyko, ograniczając tworzenie zasad.