Tutorial: Odczyt zminifikowanego skryptu obcego, zanim trafi na stronę

Management Summary
Zewnętrzne skrypty dodają usługi do witryny, ale mogą też odczytywać informacje strony i łączyć się z innymi serwerami. Kod zminifikowany jest zagęszczony do trudnej do czytania postaci, aby ograniczyć rozmiar pobrania. Artykuł wyjaśnia, jak analiza kodu i zachowania pomaga zrozumieć dostęp wprowadzany przed instalacją. Wspiera to kontrolę udostępniania danych i późniejszych zmian. Sam kod witryny nie ujawnia jednak wszystkiego, co dostawca robi z informacjami po ich otrzymaniu.
Spis treści
Skrypt zewnętrzny uruchomiony bezpośrednio na stronie może korzystać z DOM-u i wielu interfejsów przeglądarki z uprawnieniami tej strony. DOM to reprezentacja strony w przeglądarce, obejmująca tekst, formularze i inne elementy. Nadal obowiązują granice przeglądarki: ramki z innych originów, ciasteczka HttpOnly i interfejsy wymagające uprawnień nie stają się automatycznie dostępne. Przegląd musi więc ustalić rzeczywisty kontekst integracji.
Formatowanie i wyszukiwanie słów kluczowych pomagają przy pierwszej kontroli. Sześć przeszukań nie opisuje jednak wszystkich możliwości, nie dowodzi nieszkodliwości i nie zastępuje przeglądu bezpieczeństwa. Przydatny wynik to zapis dowodów: dokładny plik, potencjalne funkcje, zaobserwowane zachowanie, wymagane cele i otwarte pytania.
Odtwarzalna kopia przed analizą
Poniższy przykład Bash wymaga curl, sha256sum oraz już zainstalowanej, zatwierdzonej wersji js-beautify. Zarezerwowany adres przykładowy wymaga zastąpienia rzeczywistym adresem dostawcy. Osobny katalog przeglądu zachowuje każdą kopię z datą. Pobieranie zapisuje nagłówki i końcowy URL, odrzuca błędy HTTP i dopuszcza tylko przekierowania HTTPS. Pobrany skrypt nie jest wykonywany.
set -eu
curl --fail --show-error --silent --location \
--proto '=https' --proto-redir '=https' \
--connect-timeout 10 --max-time 60 \
--dump-header widget.headers.txt \
--write-out '%{url_effective}\n' \
--output widget.raw.js \
'https://cdn.vendor.example/widget.js' > widget.final-url.txt
test -s widget.raw.js
sha256sum widget.raw.js > widget.sha256
js-beautify --version
js-beautify widget.raw.js -o widget.pretty.jsNawet udane pobranie wymaga sprawdzenia treści i Content-Type: strona logowania HTML ze statusem 200 nie jest oczekiwanym JavaScriptem. Wersja narzędzia formatującego i czas pobrania należą do dokumentacji. Podstawą sum kontrolnych pozostają niezmienione bajty; sformatowana kopia służy czytaniu. Różnica rozmiaru nie jest miarą bezpieczeństwa ani wiarygodnym pomiarem kompresji transmisji.
Formatowanie nie odtwarza nazw, komentarzy ani struktury źródeł usuniętych podczas budowania. Mapy źródeł przypisują miejsca w dostarczonym pliku do oryginalnego kodu. Dostępne mapy mogą ułatwić analizę, lecz muszą odpowiadać dostarczonemu plikowi. DevTools pozwala też zapisać treść odpowiedzi i wyeksportować zapis sieciowy, więc kontrola w przeglądarce może być udokumentowana. Zapisane adresy i dane żądań mogą wymagać oczyszczenia przed udostępnieniem.
Sześć przeszukań potencjalnych funkcji
Przykłady wykrywają niektóre częste zapisy. Trafienie może znajdować się w komentarzu albo nieużywanej gałęzi; brak trafienia nie wyklucza danej możliwości. Skrypt może wywołać globalThis["fe" + "tch"](...), użyć aliasu lub wczytać inny plik. Sześć kategorii porządkuje pracę, a nie certyfikuje skrypt.
# Network-related candidates
grep -nE 'fetch[[:space:]]*\(|XMLHttpRequest|sendBeacon|WebSocket|EventSource|new[[:space:]]+Image' widget.pretty.js
# Storage and cookie candidates
grep -nE 'localStorage|sessionStorage|cookie|indexedDB|caches' widget.pretty.js
# Additional code or resource loading
grep -nE 'createElement|import[[:space:]]*\(|importScripts|Worker|\.src[[:space:]]*=' widget.pretty.js
# Input and form handling
grep -nE 'addEventListener|oninput|onkeydown|onsubmit|FormData|\.value' widget.pretty.js
# Device and rendering features
grep -nE 'getContext|toDataURL|AudioContext|hardwareConcurrency|RTCPeerConnection' widget.pretty.js
# Literal HTTP(S) host candidates only
grep -oiE 'https?://[a-z0-9.-]+' widget.pretty.js | sort -uWyszukiwanie pamięci nie odróżnia odczytu od zapisu. Pamięć sesyjna nie oznacza przechowywania bez końca. Obsługa wejścia wymaga sprawdzenia rzeczywistego elementu, delegowania zdarzeń, odczytywanych wartości i późniejszej transmisji. Samo użycie canvas, audio lub WebRTC nie dowodzi fingerprintingu: możliwe są również testy funkcji i prawidłowe renderowanie. O ustaleniu rozstrzygają cel i przepływ danych.
O zgodzie nie rozstrzyga samo połączenie „zapis i transmisja”. Odczyt informacji z urządzenia może mieć znaczenie bez nowego ciasteczka. Skrypt bez trafień słów kluczowych nie jest automatycznie nieszkodliwą pomocą w renderowaniu. Cel, operacje dostępu i zapisu, dane osobowe oraz mające zastosowanie wyjątki wymagają osobnej oceny. Wytyczne EROD opisują zakres techniczny szerszy niż tradycyjne ciasteczka.

Minifikacja i zaciemnianie
Minifikacja zmniejsza kod, np. przez usuwanie odstępów i skracanie nazw wewnętrznych, a zaciemnianie ma utrudniać jego zrozumienie. Tablice kodowanych ciągów, obliczane nazwy właściwości i dynamiczne generowanie kodu są tropami do analizy, nie dowodem szkodliwego zamiaru. Dekodowanie Base64 przez atob ma prawidłowe zastosowania niezwiązane z obrazami. Z kolei obliczany dostęp do właściwości może ominąć proste wyszukiwanie bez rozbudowanego zaciemniania.
grep -nE 'eval[[:space:]]*\(|Function[[:space:]]*\(|atob[[:space:]]*\(|String\.fromCharCode|\\x[0-9a-fA-F]{2}' widget.pretty.jsZaciemniony kod nadal może podlegać analizie przez dekodowanie, mapy źródeł lub specjalistyczną analizę statyczną. Izolowany test działania dostarcza innego rodzaju dowodów, ale wyłącznie dla wykonanych ścieżek. Strona testowa powinna zawierać dane syntetyczne i nie korzystać z aktywnych kont produkcyjnych. Nieznany kod nie powinien trafiać do zwykłej zalogowanej przeglądarki ani na witrynę produkcyjną tylko w celu obserwacji zachowania.
Lista hostów nie tworzy automatycznie polityki CSP
Wyrażenie hostów powyżej celowo znajduje tylko proste, dosłowne prefiksy HTTP(S). Pomija adresy względne, adresy bez podanego protokołu, WebSockety, kodowane ciągi i cele składane dynamicznie; usuwa też informację o porcie. Komentarze i adresy dokumentacji mogą dawać nieistotne trafienia. Content Security Policy (CSP) określa dozwolone źródła skryptów, połączeń i innych zasobów przeglądarki. Użyteczny wykaz celów łączy kontekst kodu, rzeczywiste żądania, przekierowania i dokumentację dostawcy.
Typ zasobu określa dyrektywę CSP: ładowanie skryptu, połączenia API i pobieranie obrazów to różne operacje. Sam host w tekście nie uzasadnia dopuszczenia hosta. Poniższy przykład trybu raportowania przedstawia małą fikcyjną witrynę. Domeny i odbiornik raportów są zastępcze; pełna polityka aplikacji wymaga własnego przeglądu.
Reporting-Endpoints: csp="https://reports.site.example/csp"
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://cdn.vendor.example; connect-src 'self' https://api.vendor.example; img-src 'self' https://images.vendor.example; report-to cspKażdy nagłówek zajmuje jedną linię nagłówka HTTP. Punkt odbioru raportów musi rzeczywiście istnieć. Report-Only obserwuje naruszenia bez blokowania. Raporty zależą od obsługi w przeglądarce, dostarczenia i wykonanych ścieżek. Brak zgłoszeń nie dowodzi kompletności, a stały tydzień nie obejmuje wszystkich warunków. Obserwacja konsoli i sieci nadal należy do testu.
Lista dozwolonych hostów nie przypina treści pliku. Kolejny skrypt z już dopuszczonego hosta może załadować się bez nowego wpisu. Przy zaufaniu opartym na nonce lub hashu i 'strict-dynamic' obsługujące mechanizm przeglądarki mogą również dopuszczać dynamicznie ładowane zależności bez osobnych wpisów hostów. CSP nie zamyka więc każdej luki pozostawionej przez niezmieniony skrypt ładujący.
SRI przypina sprawdzone bajty
Dla odpowiedniej stabilnej odpowiedzi skryptu Subresource Integrity porównuje bajty z oczekiwanym hashem przed wykonaniem. Hash musi pochodzić ze sprawdzonego, niezmienionego pliku, a nie ze sformatowanej kopii. Ten przykład Pythona wypisuje rzeczywistą wartość integralności SHA-384:
import base64
import hashlib
from pathlib import Path
raw = Path("widget.raw.js").read_bytes()
digest = base64.b64encode(hashlib.sha384(raw).digest()).decode("ascii")
print("sha384-" + digest)Pełny wynik zastępuje poniższy tekst zastępczy. Tekst zastępczy celowo nie jest prawidłowym hashem do wdrożenia.
<script src="https://cdn.vendor.example/widget.js"
integrity="sha384-REPLACE_WITH_REVIEWED_BASE64_DIGEST"
crossorigin="anonymous"></script>Inna odpowiedź nie przechodzi sprawdzenia integralności. Aplikacja potrzebuje określonego zachowania przy takim błędzie; automatyczne przyjęcie aktualnego hasha dostawcy nie byłoby ponownym przeglądem. SRI między różnymi originami wymaga też odpowiedniej odpowiedzi CORS. crossorigin="anonymous" stosuje CORS z trybem poświadczeń same-origin: poświadczenia są pomijane dla innego originu, lecz nadal mogą być wysyłane dla tego samego.
Zmienny kontener tagów nie jest z zasady niezgodny z hashowaniem. W praktyce dynamiczne lub spersonalizowane odpowiedzi, brak obsługi CORS i częste publikacje mogą sprawić, że stabilne wdrożenie SRI jest nieodpowiednie albo nieobsługiwane przez dostawcę. Bez przypięcia potrzebne pozostają kontrola publikacji, sprawdzone wersje i monitoring. Żaden z tych mechanizmów nie zatwierdza dowolnego przyszłego kodu.
Pytania i odpowiedzi
Czy hash skryptu ładującego obejmuje wszystkie zależności?
Nie. Hash elementu script obejmuje zasób pobrany dla tego elementu, a nie wszystkie później ładowane skrypty lub odpowiedzi konfiguracyjne. Dynamicznie tworzony skrypt może otrzymać własne metadane integralności, jeśli skrypt ładujący to obsługuje. W pozostałych przypadkach każda zależność wymaga osobnej oceny; dozwolony host nie gwarantuje integralności.
Czy niezmieniony cotygodniowy hash dowodzi tego samego zachowania?
Nie. Identyczne bajty mogą zachowywać się inaczej przez zdalną konfigurację, czas, lokalizację, zgodę, stan konta lub zmienione zależności. Zmieniony hash jest sygnałem do przeglądu, a nie werdyktem. Częstotliwość porównań powinna odpowiadać procesowi aktualizacji i ryzyku integracji, a testy działania obejmować istotne warunki.
Co obejmuje przegląd aktualizacji?
- Nowa niezmieniona kopia wraz z dowodami pobrania, obok poprzednio zaakceptowanej wersji.
- Porównanie kodu, potencjalnych celów i zależności w kontekście; zmiana minifikacji może utrudnić porównanie wierszy.
- Izolowane testy odpowiednich stron, stanów zgody i interakcji, z obserwacją sieci oraz pamięci.
- Udokumentowana decyzja dotycząca każdego nowego celu lub funkcji przed zmianą CSP albo zaakceptowanej wartości integralności.
Wykorzystania danych na serwerze, retencji i dalszego udostępniania nie da się ustalić wyłącznie z kodu przeglądarki. Potrzebne są dowody dostawcy i odpowiednia dokumentacja umowna. Przegląd techniczny precyzuje pytania, ale nie weryfikuje samodzielnie całego łańcucha przetwarzania dostawcy.
Źródła
- MDN: dokumentacja JavaScript
- MDN: dostęp do właściwości JavaScript
- MDN: zasady tego samego originu
- MDN: Document.cookie
- Chrome DevTools: diagnoza kodu oryginalnego z mapami źródeł
- Chrome DevTools: funkcje panelu sieciowego
- js-beautify: dokumentacja projektu i wiersz poleceń
- curl: podręcznik wiersza poleceń
- EROD: wytyczne 2/2023 o zakresie technicznym art. 5 ust. 3, wersja 2.0
- MDN: Content-Security-Policy script-src
- MDN: Content-Security-Policy-Report-Only
- W3C: Content Security Policy poziom 3
- MDN: Subresource Integrity
- W3C: Subresource Integrity
- WHATWG HTML: atrybuty ustawień CORS


