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:
evalsetTimeoutsetIntervalnew 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.