Dzisiaj kolejny, na razie ostatni z serii, artykuł dotyczący zabezpieczenia poprzez CSP. W czasie powstawania aplikacji webowej, gdy zespół programistyczny jest mnogi oraz poziom zrozumienia tematyki CSP zróżnicowany warto we wstępnej fazie wdrożyć CSP w trybie raportowania. Inną sytuacją, która może wiązać się z użyciem trybu raportowania jest dodawanie CSP do już prężnie działającego serwisu. Tak czy inaczej, tryb ten przydaje się. Czy zmienia to coś w technice deklarowania dyrektyw? Nie, zmienia się wyłącznie nazwa pola nagłówka z Content-Security-Policy na Content-Security-Policy-Report-Only.
Poniżej prezentuję jeden przykład raportowania. Tryb Report-Only powoduje dopuszczenie wszystkich zasobów, ale każde naruszenie jest raportowane na wskazany w dyrektywie report-uri adres www.
Brzmi fajnie, bo restrykcje nie będą miały charakteru trwałego, a jednocześnie zbieramy informacje czy i które dyrektywy z jakimi konkretnymi źródłami powinniśmy włączyć, by przełączenie z trybu raportowania w tryb właściwy (raportowanie może być włączone również w trybie właściwym) odbyło się bezboleśnie.
Pamiętajcie, że ustawienie nagłówka Content-Security-Policy i Content-Security-Policy-Report-Only może powodować zignorowanie tego drugiego (chyba że w drugim nagłówku dodamy dyrektywę report-only). To jednak należy empirycznie sprawdzić w aktualnie obsługiwanych przeglądarkach.
Rozwiązaniem tej sytuacji, gdy chcemy mieć zarówno raport, jak i blokować zasoby jest uwzględnienie dyrektywy report-uri w nagłówku pierwszym (Content-Security-Policy) – dyrektywa ta jest dozwolona w obu!
Kod przykładu:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title>Project 1</title> <link href='https://fonts.googleapis.com/css?family=Lato:100' rel='stylesheet' type='text/css'> <link href='style.css' rel='stylesheet' type='text/css'> <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> <script>$.get('http://localhost/version', function(data) { $('.title>span').text(' '+data); });</script> </head> <body> <div class="container"> <div class="content"> <div class="title">Laravel<span></span> - brodząc w chmurach</div> </div> </div> </body> </html>
Mamy tutaj ładowanie zewnętrznych i wewnętrznych stylów, zewnętrznej biblioteki JavaScript, a także wewnętrznego skryptu. Przy okazji ładowania zewnętrznego stylu jest wysyłane żądanie po zewnętrzny zasób czcionki. Tyle z omówienia aktualnej sytuacji.
Plik routingu, gdzie ustalam nagłówek CSP wygląda następująco:
Route::get('/', function () { header("Content-Security-Policy-Report-Only: style-src 'self'; script-src 'self'; report-uri http://localhost/csp;"); return view('welcome'); }); Route::get('/version', function() { return '5.2.23'; }); Route::post('/form', function() { return 'Dziękuję'; }); Route::post('/csp', function() { Log::error(Request::json()->all()); });
Strona zostaje wyrenderowana bez obostrzeń, ale restrykcje powodują przesłanie raportu po adres: http://localhost/csp
Rezultat:
Przesłane raporty:
[2016-06-07 16:55:01] local.ERROR: array ( 'csp-report' => array ( 'document-uri' => 'http://localhost/', 'referrer' => '', 'violated-directive' => 'script-src \'self\'', 'effective-directive' => 'script-src', 'original-policy' => 'style-src \'self\'; script-src \'self\'; report-uri http://localhost/csp;', 'blocked-uri' => 'eval', 'status-code' => 200, ), ) [2016-06-07 16:55:01] local.ERROR: array ( 'csp-report' => array ( 'document-uri' => 'http://localhost/', 'referrer' => '', 'violated-directive' => 'style-src \'self\'', 'effective-directive' => 'style-src', 'original-policy' => 'style-src \'self\'; script-src \'self\'; report-uri http://localhost/csp;', 'blocked-uri' => 'https://fonts.googleapis.com', 'status-code' => 200, ), ) [2016-06-07 16:55:01] local.ERROR: array ( 'csp-report' => array ( 'document-uri' => 'http://localhost/', 'referrer' => '', 'violated-directive' => 'script-src \'self\'', 'effective-directive' => 'script-src', 'original-policy' => 'style-src \'self\'; script-src \'self\'; report-uri http://localhost/csp;', 'blocked-uri' => 'https://code.jquery.com', 'status-code' => 200, ), ) [2016-06-07 16:55:01] local.ERROR: array ( 'csp-report' => array ( 'document-uri' => 'http://localhost/', 'referrer' => '', 'violated-directive' => 'script-src \'self\'', 'effective-directive' => 'script-src', 'original-policy' => 'style-src \'self\'; script-src \'self\'; report-uri http://localhost/csp;', 'blocked-uri' => 'inline', 'status-code' => 200, ), )
Pierwszy mówi o funkcji eval (odpowiada za to włączona wtyczka Violentmonkey), drugi o zewnętrznym arkuszu stylów, trzeci o zewnętrznej bibliotece JS, wreszcie czwarty o wewnętrznym skrypcie JS. Wszystko jak na tacy, wystarczy dobrze zinterpretować.
Teraz tylko pokażę, jak renderuje się strona, gdy przejdziemy z trybu raportowania w tryb właściwy. Pozostawiam report-uri, aby zobaczyć co zostało zablokowane.
Nagłówek:
header("Content-Security-Policy: style-src 'self'; script-src 'self'; report-uri http://localhost/csp;");
Rezultat:
Już konsola deweloperska mówi nam co zostało zablokowane, a ja tylko potwierdzę, że raport, który spłynął na wskazany adres był identyczny, jak ten przedstawiony wcześniej.
Dziękuję za uwagę. Poprzednie dwa artykuły dotyczące CSP znajdziecie pod wskazanymi adresami:
CSP – sposób na XSS, Clickjacking, CSS-Injection i inne
Pozdrawiam!
Pingback: CSP – przykłady – Po-słowie na każdy temat
Pingback: CSP – sposób na XSS, Clickjacking, CSS-Injection i inne – Po-słowie na każdy temat