Yksityiskohtainen opas Content Security Policy (CSP) -käytännön toteuttamiseen JavaScriptillä verkkoturvallisuuden parantamiseksi, XSS-hyökkäyksiltä suojautumiseksi ja yleisen verkkosivuston eheyden parantamiseksi. Keskity käytännön toteutukseen ja globaaleihin parhaisiin käytäntöihin.
Web-suojausotsikoiden toteutus: JavaScript Content Security Policy (CSP) turvalliseen verkkoon
Nykypäivän digitaalisessa ympäristössä verkkoturvallisuus on ensiarvoisen tärkeää. Verkkosivustosi ja sen käyttäjien suojaaminen haitallisilta hyökkäyksiltä ei ole enää valinnaista, vaan välttämättömyys. Cross-Site Scripting (XSS) on edelleen yleinen uhka, ja yksi tehokkaimmista puolustuskeinoista on vahvan Content Security Policy (CSP) -käytännön toteuttaminen. Tämä opas keskittyy JavaScriptin hyödyntämiseen CSP:n hallinnassa ja käyttöönotossa, mikä tarjoaa dynaamisen ja joustavan lähestymistavan verkkosovellusten turvaamiseen globaalisti.
Mikä on Content Security Policy (CSP)?
Content Security Policy (CSP) on HTTP-vastausotsikko, jonka avulla voit hallita resursseja, jotka käyttäjäagentti (selain) saa ladata tietylle sivulle. Pohjimmiltaan se toimii sallittujen listana, joka määrittää, mistä alkuperistä komentosarjoja, tyylitiedostoja, kuvia, fontteja ja muita resursseja voidaan ladata. Määrittämällä nämä lähteet eksplisiittisesti voit merkittävästi pienentää verkkosivustosi hyökkäyspintaa, mikä vaikeuttaa hyökkääjien haitallisen koodin lisäämistä ja XSS-hyökkäysten toteuttamista. Se on tärkeä osa monikerroksista suojausta.
Miksi JavaScriptiä CSP:n toteutuksessa?
Vaikka CSP voidaan määrittää suoraan web-palvelimen määritystiedostoissa (esim. Apachen .htaccess- tai Nginxin config-tiedosto), JavaScriptin käyttö tarjoaa useita etuja, erityisesti monimutkaisissa tai dynaamisissa sovelluksissa:
- Dynaaminen käytäntöjen luonti: JavaScriptin avulla voit luoda CSP-käytäntöjä dynaamisesti käyttäjäroolien, sovelluksen tilan tai muiden suoritusaikaisten ehtojen perusteella. Tämä on erityisen hyödyllistä yhden sivun sovelluksissa (SPA) tai sovelluksissa, jotka luottavat voimakkaasti asiakaspuolen renderöintiin.
- Nonce-pohjainen CSP: Noncien (kryptografisesti satunnaisia, kertakäyttöisiä tunnuksia) käyttö on erittäin tehokas tapa suojata sisäisiä komentosarjoja ja tyylejä. JavaScript voi luoda näitä nonceja ja lisätä ne sekä CSP-otsikkoon että sisäisiin komentosarja-/tyylitunnisteisiin.
- Hash-pohjainen CSP: Staattisille sisäisille komentosarjoille tai tyyleille voit käyttää hash-arvoja tiettyjen koodinpätkien sallimiseen. JavaScript voi laskea nämä hash-arvot ja sisällyttää ne CSP-otsikkoon.
- Joustavuus ja hallinta: JavaScript antaa sinulle hienojakoisen hallinnan CSP-otsikkoon, jolloin voit muokata sitä lennossa tiettyjen sovellustarpeiden perusteella.
- Virheenkorjaus ja raportointi: JavaScriptiä voidaan käyttää CSP-rikkomusraporttien keräämiseen ja lähettämiseen keskitettyyn lokipalvelimeen analysointia varten, mikä auttaa sinua tunnistamaan ja korjaamaan tietoturvaongelmia.
JavaScript CSP:n määrittäminen
Yleinen lähestymistapa sisältää CSP-otsikkoketjun luomisen JavaScriptissä ja sitten sopivan HTTP-vastausotsikon asettamisen palvelinpuolella (yleensä taustaohjelmistokehyksesi kautta). Tarkastelemme erityisiä esimerkkejä eri tilanteisiin.
1. Noncien luominen
Nonce (number used once) on satunnaisesti luotu, yksilöllinen arvo, jota käytetään tiettyjen sisäisten komentosarjojen tai tyylien sallimiseen. Tässä on, miten voit luoda noncen JavaScriptissä:
function generateNonce() {
const crypto = window.crypto || window.msCrypto; // For IE support
if (!crypto || !crypto.getRandomValues) {
// Fallback for older browsers without crypto API
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
const arr = new Uint32Array(1);
crypto.getRandomValues(arr);
return btoa(String.fromCharCode.apply(null, new Uint8Array(arr.buffer)));
}
const nonce = generateNonce();
console.log("Generated Nonce:", nonce);
Tämä koodinpätkä luo kryptografisesti turvallisen noncen selaimen sisäänrakennetun crypto-rajapinnan avulla (jos saatavilla) ja siirtyy vähemmän turvalliseen menetelmään, jos rajapintaa ei tueta. Luotu nonce on sitten base64-koodattu käytettäväksi CSP-otsikossa.
2. Noncien lisääminen sisäisiin komentosarjoihin
Kun sinulla on nonce, sinun on lisättävä se sekä CSP-otsikkoon että <script>-tunnisteeseen:
HTML:
<script nonce="YOUR_NONCE_HERE">
// Your inline script code here
console.log("Hello from inline script!");
</script>
JavaScript (Tausta):
const nonce = generateNonce();
const cspHeader = `default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'unsafe-inline'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests;`;
// Example using Node.js with Express:
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy', cspHeader);
// Pass the nonce to the view or template engine
res.locals.nonce = nonce;
next();
});
Tärkeitä huomioita:
- Korvaa
YOUR_NONCE_HEREHTML:ssä todellisella luodulla noncella. Tämä tehdään usein palvelinpuolella mallinnusmoottorin avulla. Yllä oleva esimerkki havainnollistaa noncen välittämistä mallinnusmoottorille. - CSP-otsikon
script-src-direktiivi sisältää nyt'nonce-${nonce}', mikä sallii komentosarjojen suorittamisen vastaavalla noncella. 'strict-dynamic'on lisätty `script-src`-direktiiviin. Tämä direktiivi kertoo selaimelle, että sen on luotettava komentosarjojen lataamiin komentosarjoihin. Jos skriptitunnisteella on kelvollinen nonce, kaikki sen dynaamisesti lataamat komentosarjat (esim. käyttäen `document.createElement('script')`) ovat myös luotettuja. Tämä vähentää tarvetta sallia lukuisia yksittäisiä verkkotunnuksia ja CDN-URL-osoitteita ja yksinkertaistaa huomattavasti CSP:n ylläpitoa.'unsafe-inline'ei yleensä ole suositeltavaa, kun käytetään nonceja, koska se heikentää CSP:tä. Se sisältyy kuitenkin tähän demonstraatiotarkoituksiin, ja se tulisi poistaa tuotannossa. Poista tämä heti kun voit.
3. Hashien luominen sisäisille komentosarjoille
Staattisille sisäisille komentosarjoille, jotka harvoin muuttuvat, voit käyttää hasheja noncien sijaan. Hash on komentosarjan sisällön kryptografinen tiiviste. Jos komentosarjan sisältö muuttuu, hash muuttuu, ja selain estää komentosarjan.
Hashin laskeminen:
Voit käyttää online-työkaluja tai komentorivityökaluja, kuten OpenSSL, luodaksesi sisäisen komentosarjasi SHA256-hashin. Esimerkiksi:
openssl dgst -sha256 -binary your_script.js | openssl base64
Esimerkki:
Oletetaan, että sisäinen komentosarjasi on:
<script>
console.log("Hello from inline script!");
</script>
Tämän komentosarjan (ilman <script>-tunnisteita) SHA256-hash voisi olla:
sha256-YOUR_HASH_HERE
CSP-otsikko:
const cspHeader = `default-src 'self'; script-src 'self' 'sha256-YOUR_HASH_HERE'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests;`;
Korvaa YOUR_HASH_HERE komentosarjasi sisällön todellisella SHA256-hashilla.
Tärkeitä huomioita hasheihin:
- Hash on laskettava komentosarjan tarkan sisällön perusteella, välilyönnit mukaan lukien. Kaikki komentosarjaan tehdyt muutokset, jopa yksittäinen merkki, mitätöivät hashin.
- Hashit sopivat parhaiten staattisille komentosarjoille, jotka harvoin muuttuvat. Dynaamisille komentosarjoille noncet ovat parempi vaihtoehto.
4. CSP-otsikon asettaminen palvelimelle
Viimeinen vaihe on asettaa Content-Security-Policy HTTP-vastausotsikko palvelimellesi. Tarkka menetelmä riippuu palvelinpuolen tekniikastasi.
Node.js Expressillä:
app.use((req, res, next) => {
const nonce = generateNonce();
const cspHeader = `default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'unsafe-inline'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests;`;
res.setHeader('Content-Security-Policy', cspHeader);
res.locals.nonce = nonce; // Make nonce available to templates
next();
});
Python Flaskilla:
from flask import Flask, make_response, render_template, g
import os
import base64
app = Flask(__name__)
def generate_nonce():
return base64.b64encode(os.urandom(16)).decode('utf-8')
@app.before_request
def before_request():
g.nonce = generate_nonce()
@app.after_request
def after_request(response):
csp = "default-src 'self'; script-src 'self' 'nonce-{nonce}' 'strict-dynamic' 'unsafe-inline'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests".format(nonce=g.nonce)
response.headers['Content-Security-Policy'] = csp
return response
@app.route('/')
def index():
return render_template('index.html', nonce=g.nonce)
PHP:
<?php
function generateNonce() {
return base64_encode(random_bytes(16));
}
$nonce = generateNonce();
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-" . $nonce . "' 'strict-dynamic' 'unsafe-inline'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests");
?>
<!DOCTYPE html>
<html>
<head>
<title>CSP Example</title>
</head>
<body>
<script nonce="<?php echo htmlspecialchars($nonce, ENT_QUOTES, 'UTF-8'); ?>">
console.log("Hello from inline script!");
</script>
</body>
</html>
Apache (.htaccess):
Vaikka se ei ole suositeltavaa dynaamiselle CSP:lle, voit *asettaa* staattisen CSP:n .htaccess:n avulla:
<IfModule mod_headers.c>
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests;"
</IfModule>
Nginx:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests;";
Tärkeitä huomioita:
- Korvaa
'self'todellisilla verkkotunnuksilla, joista haluat sallia resurssien lataamisen. - Ole erittäin varovainen käyttäessäsi
'unsafe-inline'- ja'unsafe-eval'-direktiivejä. Nämä direktiivit heikentävät merkittävästi CSP:tä, ja niitä tulisi välttää aina kun mahdollista. - Käytä
upgrade-insecure-requests-toimintoa päivittääksesi kaikki HTTP-pyynnöt automaattisesti HTTPS:ksi. - Harkitse
report-uri- taireport-to-toiminnon käyttöä määrittääksesi päätepisteen CSP-rikkomusraporttien vastaanottamiseen.
CSP-direktiivien selitykset
CSP käyttää direktiivejä määrittääkseen sallitut lähteet eri resurssityypeille. Tässä on lyhyt yleiskatsaus joihinkin yleisimmistä direktiiveistä:
default-src: Määrittää oletuslähteen kaikille resursseille, joita muut direktiivit eivät nimenomaisesti kata.script-src: Määrittää sallitut JavaScript-lähteet.style-src: Määrittää sallitut tyylitiedostojen lähteet.img-src: Määrittää sallitut kuvien lähteet.font-src: Määrittää sallitut fonttien lähteet.media-src: Määrittää sallitut ääni- ja videolähteet.object-src: Määrittää sallitut laajennusten lähteet (esim. Flash). Yleensä sinun tulisi asettaa tämä arvoon'none'poistaaksesi laajennukset käytöstä.frame-src: Määrittää sallitut kehysten ja iframejen lähteet.connect-src: Määrittää sallitut XMLHttpRequest-, WebSocket- ja EventSource-yhteyksien lähteet.base-uri: Määrittää asiakirjan sallitut perus-URI:t.form-action: Määrittää lomakkeiden lähetysten sallitut päätepisteet.upgrade-insecure-requests: Kehottaa käyttäjäagenttia käsittelemään kaikkia sivuston suojaamattomia URL-osoitteita (ne, jotka tarjotaan HTTP:n kautta) ikään kuin ne olisi korvattu suojatuilla URL-osoitteilla (ne, jotka tarjotaan HTTPS:n kautta). Tämä direktiivi on tarkoitettu verkkosivustoille, jotka on siirretty kokonaan HTTPS:ään.report-uri: Määrittää URI:n, johon selaimen tulisi lähettää raportteja CSP-rikkomuksista. Tämä direktiivi on vanhentunut ja sen sijaan suositellaan `report-to`-direktiivin käyttöä.report-to: Määrittää nimetyn päätepisteen, johon selaimen tulisi lähettää raportteja CSP-rikkomuksista.
CSP-lähdeluettelon avainsanat
Jokainen direktiivi käyttää lähdeluetteloa määrittääkseen sallitut lähteet. Lähdeluettelo voi sisältää seuraavat avainsanat:
'self': Sallii resurssit samasta alkuperästä (järjestelmä, isäntä ja portti).'none': Estää resurssit mistä tahansa alkuperästä.'unsafe-inline': Sallii sisäiset komentosarjat ja tyylit. Vältä tätä aina kun mahdollista.'unsafe-eval': Salliieval():n ja siihen liittyvien funktioiden käytön. Vältä tätä aina kun mahdollista.'strict-dynamic': Määrittää, että luottamus, jonka selain antaa komentosarjalle sivulla sen mukana olevan noncen tai hashin vuoksi, siirretään kyseisen komentosarjan lataamiin komentosarjoihin.'data:': Sallii resurssit, jotka on ladattudata:-järjestelmän kautta (esim. sisäiset kuvat). Käytä varoen.'mediastream:': Sallii resurssit, jotka on ladattumediastream:-järjestelmän kautta.https:: Sallii resurssit, jotka on ladattu HTTPS:n kautta.http:: Sallii resurssit, jotka on ladattu HTTP:n kautta. Yleensä ei suositella.*: Sallii resurssit mistä tahansa alkuperästä. Vältä tätä; se mitätöi CSP:n tarkoituksen.
CSP-rikkomusraportointi
CSP-rikkomusraportointi on ratkaisevan tärkeää CSP:n valvomiseksi ja virheenkorjaukseen. Kun resurssi rikkoo CSP:tä, selain voi lähettää raportin määritettyyn URI:in.
Raportointipäätepisteen määrittäminen:
Tarvitset palvelinpuolen päätepisteen CSP-rikkomusraporttien vastaanottamiseen ja käsittelyyn. Raportti lähetetään JSON-hyötykuormana.
Esimerkki (Node.js Expressillä):
app.post('/csp-report', (req, res) => {
console.log('CSP Violation Report:', req.body);
// Process the report (e.g., log to a file or database)
res.status(204).end(); // Respond with a 204 No Content status
});
report-uri- tai report-to-direktiivin määrittäminen:
Lisää report-uri- tai `report-to`-direktiivi CSP-otsikkoosi. `report-uri` on vanhentunut, joten suosi `report-to`-direktiivin käyttöä.
const cspHeader = `default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests; report-to csp-endpoint;`;
Sinun on myös määritettävä Reporting API -päätepiste käyttämällä `Report-To`-otsikkoa.
Report-To: { "group": "csp-endpoint", "max_age": 10886400, "endpoints": [{"url": "/csp-report"}], "include_subdomains": true }
Huomautus:
- `Report-To`-otsikko on asetettava jokaisessa pyynnössä palvelimellesi, tai selain voi hylätä kokoonpanon.
- `report-uri` on vähemmän turvallinen kuin `report-to`, koska se ei salli raportin TLS-salausta, ja se on vanhentunut, joten suosi `report-to`-direktiivin käyttöä.
Esimerkki CSP-rikkomusraportista (JSON):
{
"csp-report": {
"document-uri": "https://example.com/page.html",
"referrer": "",
"violated-directive": "script-src 'self' 'nonce-YOUR_NONCE_HERE'",
"effective-directive": "script-src",
"original-policy": "default-src 'self'; script-src 'self' 'nonce-YOUR_NONCE_HERE'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests; report-uri /csp-report;",
"blocked-uri": "https://evil.com/malicious.js",
"status-code": 200,
"script-sample": ""
}
}
Analysoimalla näitä raportteja voit tunnistaa ja korjata CSP-rikkomuksia ja varmistaa, että verkkosivustosi pysyy turvallisena.
Parhaat käytännöt CSP:n toteuttamiseen
- Aloita rajoittavalla käytännöllä: Aloita käytännöllä, joka sallii resurssit vain omasta alkuperästäsi ja löysää sitä vähitellen tarpeen mukaan.
- Käytä nonceja tai hasheja sisäisille komentosarjoille ja tyyleille: Vältä
'unsafe-inline'-käyttöä aina kun mahdollista. - Käytä
'strict-dynamic'yksinkertaistaaksesi CSP:n ylläpitoa. - Vältä
'unsafe-eval'-käyttöä: Jos sinun on käytettäväeval()-funktiota, harkitse vaihtoehtoisia lähestymistapoja. - Käytä
upgrade-insecure-requests-toimintoa: Päivitä kaikki HTTP-pyynnöt automaattisesti HTTPS:ksi. - Ota käyttöön CSP-rikkomusraportointi: Valvo CSP:täsi rikkomusten varalta ja korjaa ne viipymättä.
- Testaa CSP:si perusteellisesti: Käytä selaimen kehittäjätyökaluja CSP-ongelmien tunnistamiseen ja ratkaisemiseen.
- Käytä CSP-validaattoria: Online-työkalut voivat auttaa sinua validoimaan CSP-otsikon syntaksin ja tunnistamaan mahdollisia ongelmia.
- Harkitse CSP-kehyksen tai -kirjaston käyttöä: Useat kehykset ja kirjastot voivat auttaa sinua yksinkertaistamaan CSP:n toteutusta ja hallintaa.
- Tarkista CSP:si säännöllisesti: Kun sovelluksesi kehittyy, CSP:si on ehkä päivitettävä.
- Kouluta tiimisi: Varmista, että kehittäjäsi ymmärtävät CSP:n ja sen tärkeyden.
- Ota CSP käyttöön vaiheittain: Aloita ottamalla CSP käyttöön vain raportointitilassa, jotta voit valvoa rikkomuksia estämättä resursseja. Kun olet varma, että käytäntösi on oikea, voit ottaa sen käyttöön täytäntöönpanotilassa.
- Dokumentoi CSP:si: Pidä kirjaa CSP-käytännöstäsi ja kunkin direktiivin perusteista.
- Ota huomioon selaimen yhteensopivuus: CSP-tuki vaihtelee eri selaimissa. Testaa CSP:si eri selaimissa varmistaaksesi, että se toimii odotetulla tavalla.
- Priorisoi turvallisuus: CSP on tehokas työkalu verkkoturvallisuuden parantamiseen, mutta se ei ole hopealuoti. Käytä sitä yhdessä muiden tietoturvan parhaiden käytäntöjen kanssa suojataksesi verkkosivustoasi hyökkäyksiltä.
- Harkitse Web Application Firewallin (WAF) käyttöä: WAF voi auttaa sinua panemaan täytäntöön CSP-käytäntöjä ja suojaamaan verkkosivustoasi muuntyyppisiltä hyökkäyksiltä.
Yleisiä CSP-toteutuksen haasteita
- Kolmannen osapuolen komentosarjat: Kaikkien kolmannen osapuolen komentosarjojen vaatimien verkkotunnusten tunnistaminen ja salliminen voi olla haastavaa. Käytä `strict-dynamic` -toimintoa aina kun mahdollista.
- Sisäiset tyylit ja tapahtumakäsittelijät: Sisäisten tyylien ja tapahtumakäsittelijöiden muuntaminen ulkoisiksi tyylitiedostoiksi ja JavaScript-tiedostoiksi voi olla aikaa vievää.
- Selaimen yhteensopivuusongelmat: CSP-tuki vaihtelee eri selaimissa. Testaa CSP:si eri selaimissa varmistaaksesi, että se toimii odotetulla tavalla.
- Ylläpitokustannukset: CSP:n pitäminen ajan tasalla sovelluksesi kehittyessä voi olla haastavaa.
- Suorituskykyvaikutus: CSP voi aiheuttaa pientä suorituskykyvaikutusta, koska resurssit on validoitava käytäntöä vasten.
Globaalit näkökohdat CSP:ssä
Kun toteutat CSP:tä maailmanlaajuiselle yleisölle, ota huomioon seuraavat asiat:
- CDN-palveluntarjoajat: Jos käytät CDN:iä, varmista, että sallit sopivat CDN-verkkotunnukset. Monet CDN:t tarjoavat alueellisia päätepisteitä; näiden käyttäminen voi parantaa suorituskykyä eri maantieteellisillä alueilla oleville käyttäjille.
- Kielikohtaiset resurssit: Jos verkkosivustosi tukee useita kieliä, varmista, että sallit tarvittavat resurssit kullekin kielelle.
- Alueelliset määräykset: Ole tietoinen alueellisista määräyksistä, jotka voivat vaikuttaa CSP-vaatimuksiisi.
- Saavutettavuus: Varmista, että CSP ei vahingossa estä saavutettavuusominaisuuksien vaatimia resursseja.
- Testaus eri alueilla: Testaa CSP:täsi eri maantieteellisillä alueilla varmistaaksesi, että se toimii odotetulla tavalla kaikille käyttäjille.