Featured image of post Content-Security-Policy – nagłówek HTTP zwiększający bezpieczeństwo użytkowników przeciw atakowi XSS

Content-Security-Policy – nagłówek HTTP zwiększający bezpieczeństwo użytkowników przeciw atakowi XSS

O nagłówku Content-Security-Policy napisano już wiele. Dlatego niniejszy artykuł jest prostym wprowadzeniem z wieloma linkami do zewnętrznych źródeł. Zahaczę również o implementację na trzy różne sposoby oraz o kilka wskazówek praktycznych. Na koniec znajdzie się również krótka notatka dla osób pracujących z React.js

Spis treści

Zdjęcie ilustrujące artykuł pochodzi z Unsplash i zostało wykonane przez Scott Webb.

Wstęp

Content Security Policy (CSP) to nagłówek, który umieszczamy w odpowiedzi z serwera bądź w sekcji head za pomocą tagu meta. Służy on do ochrony i zmniejszania zasięgu potencjalnego ataku XSS (z ang. Cross Site Scripts). Pozwala na określenie skąd nasza przeglądarka może pobierać wszelakie zasoby dla naszej strony, które uznajemy za bezpieczne. Tym samym ograniczamy możliwość pobrania szkodliwych elementów z zewnętrznych domen.

Szczegóły konfiguracji znajdziesz, między innymi, na stronie Mozilla Developer Network (Content Security Policy (CSP) - HTTP), Microsoftu (w tym przypadku dla technologii Blazor) Wymuszanie zasad zabezpieczeń zawartości dla ASP.NET Core Blazor. Ciekawe omówienie w języku angielskim znajdziesz również w artykule Content-Security-Policy(CSP) with .Net Core oraz w języku polskim w artykule Czym jest Content Security Policy?.

Pamiętaj, że domyślne ustawienia przeglądarki pozwalają na pobieranie skryptów, stylów CSS, obrazów i innych z każdego miejsca w sieci, co nie zapewnia nam bezpieczeństwa w przypadku ataku XSS.

Przykłady wdrożenia

Możemy dodać nagłówek na wiele różnych sposobów, w tym:

HTML

W sekcji head pliku index.html:

Przykład wprowadzenia polityki CSP w nagłówku strony HTML
<head>
    ...
    <meta http-equiv="Content-Security-Policy" content="default-src 'self';">
</head>
ASP.NET

W odpowiedzi serwera, poniżej jako middleware dla ASP.Net Core. Więcej informacji, na przykład tutaj: Is CSP (Content Security Policy) activated by default in .net core 3.1?.

Warstwa pośrednia (middleware) dla ASP.Net Core dodająca nagłówek CSP
app.Use(async (ctx, next) =>
{
    ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self';");
    await next();
});
Internet Information Services (IIS)

Za pomocą pliku Web.config, gdy używamy IIS. Więcej, między innymi tutaj: Config your IIS server to use the "Content-Security-Policy" header:

Przykład dodania polityki CSP w pliku web.config
<system.webServer>
    <httpProtocol>
        <customHeaders>
            <add name="Content-Security-Policy" value="default-src 'self';" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

Podstawowa konfiguracja

Przytoczone wyżej przykłady ograniczają pobieranie danych tylko do domeny, z którą połączyliśmy się na początku (np. zaprogramujtoraz.pl) i wyklucza poddomeny. Uniemożliwia to pobieranie obrazków, między innymi, ze strony podstrona.zaprogramujtoraz.pl. Jest to preferowane zachowanie, od którego powinniśmy wyjść, robiąc wyjątki, na przykład poprzez konstrukcję Content-Security-Policy: default-src 'self' podstrona.zaprogramujtoraz.pl; lub gdy ufamy całej domenie, możemy użyć wyrażenia wieloznacznego '*': *.zaprogramujtoraz.pl.

Pełną listę elementów, które możemy skonfigurować, znajdziemy tutaj: Content-Security-Policy - HTTP. Te, które widziałem najczęściej to:

  • default-src - domyślne zachowanie, dla nieskonfigurowanych elementów,

  • script-src - określa źródła dla JavaScriptu,

  • img-src - określa lokalizację dla obrazów,

  • font-src - określa lokalizację dla czcionek,

  • object-src - określa lokalizację dla elementów <object>, <embed> oraz <applet> i warto ustawić go na none (object-src: 'none';), ponieważ te elementy w większości są już przestarzałe.

Zwróć uwagę, że stosowanie samej gwiazdki '*' jako znaku pozwalającego na ładowanie czegokolwiek zewsząd wskazuje zapewne na jakiś problem. Jeśli nie jesteś agregatorem stron, warto przemyśleć taką konstrukcję, aby chronić swoich użytkowników przed niechcianymi treściami.

Raportowanie o naruszeniach

Mechanizm CSP pozwala również na raportowanie o naruszeniach pod określony adres. Dzieje się to za pomocą pola report-uri. Więcej w dokumentacji MDN: CSP - Enabling Reporting.

Drugą ciekawą funkcją jest testowanie, czy wprowadzenie polityki CSP nic nie popsuje. Możemy to zrobić za pomocą nagłówka Content-Security-Policy-Report-Only. Więcej na stronie MDN Content-Security-Policy-Report-Only - HTTP.

Kilka uwag przy wdrożeniu

Podczas wdrażania polityki CSP w już istniejącym projekcie, pojawiają się dwa zagadnienia:

  1. Jak upewnić się, że zebrano wszystkie zależności i adresy, które należy uwzględnić w polityce?

  2. Jak stosować osobne polityki dla poszczególnych środowisk (DEV, QA, PROD), tak aby nie pisać kolubryny wykluczeń?

Zbieranie zależności i adresów dla CSP

W celu zebrania informacji, na temat zależności najłatwiej posłużyć się dwoma narzędziami. Jeśli znasz jeszcze jakieś, daj znać w komentarzach!

Narzędzie deweloperskie wbudowane w przeglądarkę

Podstawą są narzędzia deweloperskie wbudowane w przeglądarkę, zazwyczaj dostępne pod klawiszem F12. W zakładce Sieć (Network) zobaczymy wszystkie połączenia ustanawiane przez naszą aplikację od momentu uruchomienia narzędzi deweloperskich. Dlatego po ich otworzeniu warto odświeżyć stronę.

W niektórych przeglądarkach, mając otwarte narzędzia deweloperskie, możemy kliknąć prawym przyciskiem myszy (PPM) na przycisku "odśwież" i wymusić pełne przeładowanie strony. Dzięki temu będziemy mieli większą pewność, że nic nam nie ucieknie.

Wtyczka Content Security Evaluator (csper.io)

Wielką pomocą może okazać się dostępna za darmo wtyczka Content Security Policy (CSP) Evaluator (csper.io). Zbiera ona dane i generuje przykładową politykę CSP, podczas gdy my przeglądamy stronę.

Uważaj podczas korzystania z tego rozszerzenia, ponieważ do wynikowej polityki dołączany jest parametr report-to, który wskazuje na stronę dostawcy usługi (csper.io).

Organizacja zasobów

Patrząc na obsługę wyrażeń wieloznacznych w polityce CSP (z ang. wildcards) warto zawczasu pomyśleć nad organizacją naszych zasobów. Może to zaoszczędzić nam dużo pracy, podczas rozwoju aplikacji, kiedy to już zapomnimy o tym, że konfigurowaliśmy coś takiego jak Content-Security-Policy.

Dlatego warto usługom nadawać adresy w następujący sposób: [nazwa serwisu].[środowisko], co będzie skutkowało przykładowym adresem orders.qa.zaprogramujtoraz.pl. Dzięki temu dość łatwo będzie zastosować wyrażenie wieloznaczne w celu dostępu do wszystkich zasobów na przykład: *.qa.zaprogramujtoraz.pl.

Transformacja konfiguracji

Nie możemy zapominać o tym, że wiele narzędzi dostarcza nam możliwość transformacji naszej konfiguracji. Dzięki temu możemy łatwo zmienić naszą politykę, w zależności od tego, gdzie ją uruchomimy.

W ASP.Net możemy ładować różne konfiguracje, zależnie od tego, czy aplikacja uruchamia się w środowisku produkcyjnym, czy też deweloperskim. Więcej o tym w dokumentacji: Używanie wielu środowisk na platformie ASP.NET Core. Wystarczy załadować odpowiedni klucz z konfiguracji.

Podobne efekty możemy uzyskać, wykorzystując transformację pliku web.config, który określa nam niektóre aspekty zachowania usługi IIS (Internet Information Services). Więcej na ten temat znajdziesz również w dokumentacji: Przekształcanie pliku web.config.

Przekształcanie pliku web.config może okazać się konieczne, gdy mamy do czynienia z stroną statyczną.

script-src a React.js:

W celu dalszego utwardzania polityki CSP można natknąć się na problem, który będzie wymagał dodania flagi unsafe-inline do zasady script-src. Pracując w React.js nie musimy się na to godzić. React może automatycznie przenieść skrypty, które umieszcza w linii do osobnych plików. Dzięki czemu można zrezygnować ze wpisu unsafe-inline dla script-src.

Nie udało mi się znaleźć nic na temat znacznego wpływu tego ustawienia na aplikację. Przykładowe pomiary znajdziesz w artykule „Jak uzywać React’a bez wpisu 'unsafe-inline' i dlaczego” (po angielsku) (drag13.io).

Aby to zrobić, należy ustawić zmienną środowiskową INLINE_RUNTIME_CHUNK na false. Wiecej na temat tej zmiennej w dokumentacji „Zaawansowana konfiguracja (po angielsku)”. Można to zrobić na kilka sposobów:

W przypadku, gdy korzystasz z yarn'a i jego przestrzenii (z angielskiego: workspaces), może okazać się, że będziesz musiał umieścić plik .env nie tylko w głównym katalogu, ale również obok pliku package.json projektu podrzędnego.
  • w pliku package.json. Dodając wyrażenie set INLINE_RUNTIME_CHUNK=false w tej samej linii, co określenie skryptu budujacego. Przykład takiego wykonania znajdziesz w poniższym wycinku pliku package.json, w linijce 16.

Podejście to nie jest trwałe i może różnić się zależnie od platformy. Nie mniej jest to wygodne na maszynie deweloperskiej, gdzie nie trzeba pamiętać o dodatkowych ustawieniach.
  • ustawienie zmiennej środowiskowej w narzędziach budowania. Konfiguracja zależy od platformy, więc nie będę jej tutaj omawiał.

W przypadku modyfikacji package.json uważaj na spacje, gdyż nie wszystkie kombinacje działają. Zostało to szczegółowo omówione w wątku na GitHub - "set INLINE_RUNTIME_CHUNK=false && react-scripts build" not working (depending on spacing.
Fragment pliku package.json
{
  "name": "seasons",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "set INLINE_RUNTIME_CHUNK=false&&react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  }
}
comments powered by Disqus
Proszę pamiętaj, że blog jest na ten moment w wersji poglądowej i może zawierać wiele błędów (ale nie merytoycznych!). Mimo to mam nadzieję, że blog Ci się podoba! Wiele ilustracji pojawiło się na blogu dzięki unsplash.com!
Zbudowano z Hugo
Motyw Stack zaprojektowany przez Jimmy