Jestli provozujete svůj vlastní webový server, je prakticky nutností řešit jeho zabezpečení, a to i tehdy, když se jedná o váš soukromý hobby projekt. Na to mnoho geeku zapomíná a na internetu se dá najít velké množství nezabezpečených webových serverů. Jak je na tom váš server si můžete prověřit jednoduše přes stránky Mozilla Observatory. Jestli vaše stránka dosahuje hodnocení A a lepší, pak gratuluji, většinu doporučení ohledně HTTP hlaviček máte na svém webu již nasazeno. Jedná se o velmi užitečný nástroj a na tomto webu je i výstup z dalších nástrojů jako Qualys SSLLabs, securityheaders.io.
Naštěstí nastavení většiny doporučených nastavení je dnes v celku jednoduché. Tento článek je součástí malého seriálu o zabezpečení Nginx serveru. V dnešním článku si popíšeme funkci a nastavení Content Security Policy (CSP) hlavičky.
Content Security Policy
Content Security Policy je hlavička, kterou posílá server na klienta a informuje prohlížeč, jaký obsah a z jakých zdrojů může nahrávat. Cílem této hlavičky je omezit případný dopad Cross Site Scriptingu (XSS) na vašem webu. Toto nastavení nezabrání vložení škodlivého skriptu, obrázku do vašich stránek např. přes chybu v komentářích, ale omezuje dopad takového útoku.
Při načtení vaší stránky, prohlížeč kromě samotné stránky stahuje hromadu dalších zdrojů. Typicky se jedná o fonty (např. Google Fonty), JS knihovny (jQuery), skripty pro sledování návštěvnosti (Google Analytics). Problém nastane, když případný útočník např. vloží JS skript. Prohlížeč stáhne a spustí všechny skripty, jelikož nemá jak poznat, který skript nepatří k webové stránce. A tady přichází ke slovu CSP, která prostřednictvím HTTP hlavičky sděluje prohlížeči pravidla domény, pro stažení obrázků, fontů, JS skriptů, CSS. Všechny moderní prohlížeče tuto hlavičku vyhodnocují a dle nastavení v této hlavičce nedovolí načíst skript z jiné webové stránky, případně nedovolí zobrazení stránky v iframe na jiné doméně atd.
Nastavení
Nastavení hlavičky se provádí v konfiguraci NGINX pro danou doménu. Hlavička funguje na principu whitelistace. Tzn. uvádí se zdroje, které jsou pro doménu povoleny. Úplně nejzákladnější nastavení:
add_header Content-Security-Policy
"upgrade-insecure-requests;
base-uri 'self';
form-action 'self';
connect-src 'self';
frame-ancestors 'self';
default-src 'none';
img-src 'self';
script-src 'self';
style-src 'self';
font-src 'self';
frame-src 'self'"
Nastavit můžete tyto direktivy (převzato ze stránek OWASP):
- default-src: Define loading policy for all resources type in case of a resource type dedicated directive is not defined (fallback),
- script-src: Define which scripts the protected resource can execute,
- object-src: Define from where the protected resource can load plugins,
- style-src: Define which styles (CSS) the user applies to the protected resource,
- img-src: Define from where the protected resource can load images,
- media-src: Define from where the protected resource can load video and audio,
- frame-src: Define from where the protected resource can embed frames,
- font-src: Define from where the protected resource can load fonts,
- connect-src: Define which URIs the protected resource can load using script interfaces,
- form-action: Define which URIs can be used as the action of HTML form elements,
- sandbox: Specifies an HTML sandbox policy that the user agent applies to the protected resource,
- script-nonce: Define script execution by requiring the presence of the specified nonce on script elements,
- plugin-types: Define the set of plugins that can be invoked by the protected resource by limiting the types of resources that can be embedded,
- reflected-xss: Instructs a user agent to activate or deactivate any heuristics used to filter or block reflected cross-site scripting attacks, equivalent to the effects of the non-standard X-XSS-Protection header,
- report-uri: Specifies a URI to which the user agent sends reports about policy violation
Pro skripty a css můžete nastavit:
- ‚none‘, ‚self‘
- url
- sha hash
- nebo tzv. nonce
- ‚unsafe-inline‘
- ‚unsafe-eval‘
Posledním dvěma nastavením je potřeba se vyhnout, i když si dále ukážeme, že v případě WordPress blogu se asi přepínači ‚unsafe-inline‘ u CSS nevyhnete.
Testování nastavení
Před tím, než toto nastavení použijete na svých stránkách, je dobré jej nejdříve vyzkoušet. To můžete udělat tak, že místo hlavičky Content-Security-Policy budete dočasně vkládat hlavičku Content-Security-Policy-Report-Only. U této hlavičky prohlížeč pouze zaznamená porušení pravidel, ale nezablokuje načítání. Můžete si tak odladit nastavení bez rizika, že si rozbijete stránky. Výše uvedené nastavení vám bude fungovat za předpokladu, že na svých stránkách nevyužíváte žádný inline skript, styl, případně žádné externí zdroje a skripty jako je např. Google Analytics. V opačném případě nastává asi nejzdlouhavější proces nastavení a to doplnění výjímek. Nejjednodušší způsob, jak to udělat, je otevřít si konzoli v developerských nástrojích prohlížeče a projít všechny nahlášené chyby.
Jestli používáte např. Worpress je situace ještě složitější, protože každý plugin si do stánky přidává své skripty, styly, nad kterými nemáte moc kontrolu. Existují i nástroje, které by měli ze stránek vygenerovat nastavení pro CSP, bohužel vše co jsem zkoušel nebylo moc funkční a i tak ve výsledku znamenalo ruční nastavení.
Do hlavičky pro jednotlivé typy zdrojů pak přidávejte buď URL a nebo sha hash. Já se u Worpress blogu nevyhnul použití těchto hashů, jelikož mnoho skriptů vkládají do stránky pluginy. Výsledné nastavení pak u mně vypadá následovně:
add_header Content-Security-Policy "upgrade-insecure-requests;
base-uri 'self'; form-action 'self'; connect-src 'self'; frame-ancestors 'self';
default-src 'none'; img-src 'self' https://www.google-analytics.com;
script-src 'self' https://www.google.com https://www.gstatic.com https://www.google-analytics.com
'sha256-Ujk+B3dO4RcEqJQEiivLy+AFYv8eUZoAaU/CXtIkG5I=' 'sha256-FOojNyNg+GzFo/8e8GsxNhJxnVliU8EGfNHmkFJFfHk='
'sha256-Uh9P3a9iNJz7NbO7t6Ug6B74/DPZ76aUhVbbxRyVHS8=' 'sha256-WO1mMObomvYzvMhna2qzMZsqcP5wDsat5mN8yALo/9k='
'sha256-GrbWt24Lz32INGNtqwO9p9IB3c9b6Ylfr2nOR1+eZ/0=' 'sha256-ViTM8ij8QEmsCmmUts3T93iptRsykT+7dk27ZUXXyDc='
'sha256-bssOLZ7SrS/Id0q9suZ8OjxsHGZQXPS8f9AH/qtEBPc=' 'sha256-SHBT9pu0aRmKUAECInPUqr/psHQipLt9em11Uqks1tI='
'sha256-lqet9k7BkYpF67+DxsW39nPFfthcC2gm2Y8B8VhqE58=' 'sha256-LZrqMXg105/BsVblQvgwyYDKJXiCWIgv2IQ6sU/VwVc='
'sha256-Yy3l6gqbWK7BaVGG2wJF8CpBrgYJLAGKg48htu1FBi8=' 'sha256-HB1JRI09z+czMo1BSKyVXoqMOUP21yZvAjZfhhMGOgY='
'sha256-80GZPnhznSpAXWAlEstu9inXoYNctf5AioxvW0ILTCQ=' 'sha256-Ir6LSwwzuhG84Dw+hREx9AEk63fQswoxsBufu2nc0Ss='
'sha256-Me/9VCQo31Pe98tBaShIIIVjWVXdRpAV+3NCvjZYopI=' 'sha256-lEXt64WDNfnBpWPgee0reu7sezrO2z68XWaSihtFCyM='
'sha256-IZc1vENJ8gU1klxhKxwrI+G0SmHLLzwHwWBK/KOwWjE=' 'sha256-2750hWLbSDkMtW1J1SvCWktrtgX5EVWuAYDfNcj/oQA='
'sha256-gsPJOuqfoYE7YaWq26muQA19WSMMc8WypQgweIV8lsE=' 'sha256-YdAdRvnJHgXR/TAGMNJQv5VxREGAG4rAhjJ7DNyxa08='
'sha256-QBgCiGV3ZlzZyr/lgDotEYfoZVt3C+XVQ1tqbt5hJv4=';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net;
font-src 'self' https://fonts.gstatic.com https://cdn.jsdelivr.net;
frame-src 'self' https://www.youtube.com https://www.google.com;
report-uri https://a16899aac6f0d248e3831655f2f84c13.report-uri.com/r/d/csp/reportOnly
https://a16899aac6f0d248e3831655f2f84c13.report-uri.com/r/d/hpkp/reportOnly
https://a16899aac6f0d248e3831655f2f84c13.report-uri.com/r/d/ct/reportOnly";
Kromě pracně vyjmenovaných hashů pro skripty a url pro externí zdroje jsou rovněž použita tato nastavení:
- upgrade-insecure-requests – říká prohlížeči, že všechny zdroje, ke kterým přistupuje přes HTTP, má stáhnout přes HTTPS
- style-src ‚self‘ ‚unsafe-inline‘ – povolení inline CSS. Tomuto nastavení jsem se bohužel díky FancyBox pluginu nevyhnul. Použití CSS inline je ale o něco méně nebezpečné, než použítí stejné direktivy u skriptů.
- report-uri – definuje url, kam má prohlížeč reportovat případné porušení pravidel z CSP hlavičky.
REPORT-URI
Velmi užitečné rozšíření CSP hlavičky. Umožňuje sdělit prohlížeči URL na která má reportovat zjištěné porušení pravidel. Díky tomu můžete jednoduše identifikovat případné chyby v nastavení. Adresa může směrovat i na váš server, kde běží např. PHP skript, který v případě porušení pravidel pošle email – jako inspirace můžete použít řešení z tohoto článku CSP Reports. Já osobně využívám službu report-uri.io. V bezplatném plánu nabízí 20 000 reportů, což pro malý web, blog bohatě stačí.
Nastavení je opravdu jednoduché a do report-uri stačí vložit URL, která najdete pod položkou v Setup v menu report-uri.io. Kromě reportingu nabízí tato stránka i množství nástrojů pro validaci a generování http hlaviček, certifikátů atd.
CSP a WordPress Admin
Vše máte správně nastaveno, jak ale záhy zjistíte, nastane problém ve WordPress admin sekci, kde je obrovské množství inline skriptů atd. Nastavení hlavičky pro tuto část by bylo opravdu problém a tak řešením je pro tuto část hlavičku neposílat a zabezpečit přístup do admin sekce pouze pro konkrétní IP adresu. Toto nastavení mi díky použití FastCgi-PHP zabralo opravdu hodně času. Nakonec jsem ale našel nastavení, které funguje:
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
include snippets/fastcgi-php.conf;
location ~* ^/wp-admin/.*\.php$ {
allow 11.11.11.11;
allow 22.22.22.22;
deny all;
add_header Content-Security-Policy "";
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
Když vše správně nastavíte, v Mozilla Observatory byste měli vidět tento výsledek:
Příště budeme pokračovat dalšími hlavičkami jako X-Frame-Options, X-XSS-Protection, X-Content-Type-Options, Referrer-Policy, Strict-Transport-Security.