Guida CSP: bloccare Shopify Hydrogen
Il Cross-Site Scripting (XSS) è la minaccia numero 1 al commercio headless. Una guida tecnica per implementare Strict CSP con Nonces in Remix e Hydrogen.
Nell’e-commerce, la Fiducia è l’unica valuta. Se un cliente digita il numero della sua carta di credito nel tuo sito, si fida di te per proteggerlo. Ma se si dispone di una vulnerabilità Cross-Site Scripting (XSS), un utente malintenzionato può iniettare uno script che legge i tasti premuti. L’aggressore ottiene la carta di credito. Ottieni la causa.
La difesa contro XSS è la Content Security Policy (CSP). È una lista bianca. Dice al browser: “Carica solo script da questi domini. Blocca tutto il resto.”
L’implementazione di CSP in un’app a pagina singola (SPA) come Shopify Hydrogen (Remix) è notoriamente difficile a causa dell’idratazione e dei tag di terze parti. Questa è la guida definitiva per farlo bene.
Perché Maison Code ne parla
In Maison Code Paris, agiamo come la coscienza architettonica dei nostri clienti. Spesso ereditiamo stack “moderni” costruiti senza una comprensione fondamentale della scala.
Discutiamo di questo argomento perché rappresenta un punto di svolta critico nella maturità ingegneristica. Implementarlo correttamente differenzia un MVP fragile da una piattaforma resiliente di livello aziendale.
1. La strategia: CSP rigoroso con Nonces
Ai vecchi tempi utilizzavamo la “Whitelist dei domini”.
script-src 'self' https://google-analytics.com https://facebook.com
Questo è insicuro. Se Google Analytics presenta una vulnerabilità Open Redirect, l’aggressore può aggirare il tuo CSP.
Lo standard moderno è Nonces (numero utilizzato UNA VOLTA).
- Il Server genera un token crittografico casuale (
abc123yz) per ogni richiesta. - Il server aggiunge un’intestazione:
Content-Security-Policy: script-src 'nonce-abc123yz'. - L’HTML include il token:
<script nonce="abc123yz">.
Se un utente malintenzionato inserisce <script>alert(1)</script>, non ha alcun nonce. Il browser lo blocca.
2. Implementazione in Remix (Idrogeno)
In Remix, dobbiamo generare il nonce nel caricatore root e trasmetterlo.
Passaggio 1: generare il Nonce in entry.server.tsx
“tsx // app/entry.server.tsx import { generateNonce } da ’./utils’; // crypto.randomBytes(16).toString(‘base64’)
esporta la funzione predefinita handleRequest(richiesta, rispostaStatusCode, rispostaHeaders, remixContext) { const nonce = generateNonce();
// Definisce le direttive
const csp = [
“default-src ‘self’”,
script-src 'self' 'nonce-€{nonce}' https://cdn.shopify.com https://challenges.cloudflare.com,
“style-src ‘self’ ‘unsafe-inline’ https://fonts.googleapis.com”, // unsafe-inline necessario solitamente per CSS-in-JS
“img-src dati ‘auto’: https://cdn.shopify.com”,
“connect-src ‘self’ https://monorail-edge.shopifysvc.com https://www.google-analytics.com”,
“frame-antenati ‘none’”, // Anti-clickjacking
].join(’; ’);
rispostaHeaders.set(‘Content-Security-Policy’, csp);
// Passa il nonce al contesto in modo che
### Passaggio 2: allega il Nonce agli script in `root.tsx`
"tsx
// app/root.tsx
import { useNonce } da '@shopify/idrogeno';
esporta la funzione predefinita App() {
const nonce = useNonce();
ritorno (
<html lang="it">
<testa>
<Meta/>
<Link />
</testa>
<corpo>
<Outlet />
<ScrollRestoration nonce={nonce} />
<Script nonce={nonce} />
</corpo>
</html>
);
}
Ora, ogni tag di script generato da Remix avrà legalmente il nonce.
3. Il sistema di sicurezza “Solo report”.
L’implementazione di un CSP è spaventosa. Se dimentichi un dominio (ad esempio “fonts.gstatic.com”), il tuo sito si interrompe. Soluzione: Modalità solo report.
- Imposta l’intestazione “Content-Security-Policy-Report-Only”.
- Il browser caricherà le risorse ma registrerà la violazione sulla console (e su un endpoint).
- Eseguilo per 2 settimane. Monitorare i log.
- Una volta puliti i log, passare alla modalità di applicazione.
4. Segnalazione: utilizzo di Sentry
La lettura delle violazioni CSP nella console è inutile per gli utenti di produzione.
È necessario un endpoint di raccolta.
Sentry (e Datadog) supportano i valori-chiave di reporting CSP.
report-uri https://o450.ingest.sentry.io/api/.../security/?sentry_key=...;
Quando un utente in Brasile attiva una violazione CSP (forse un’estensione malware), Sentry ti avvisa.
Filtro rumore: vedrai molto rumore dalle estensioni del browser (LastPass causa violazioni). Ignora gli schemi “moz-extension” e “chrome-extension”. Concentrati sulle iniezioni “http”.
5. Integrità delle sottorisorse (SRI)
Cosa succede se la CDN viene violata?
Se carichi “https://code.jquery.com/jquery.min.js” e un hacker modifica quel file sul CDN, ignora il tuo CSP (perché il dominio è nella lista bianca).
Correzione: utilizza SRI.
<script src="..." integrità="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/ux..." crossorigin="anonymous"></script>
Il browser esegue l’hashing del file scaricato. Se non corrisponde all’attributo “integrità”, rifiuta di essere eseguito.
Ciò garantisce che il codice non possa cambiare senza che tu distribuisca un nuovo file HTML.
6. Inferno di terze parti (Google Tag Manager)
Il marketing ama GTM. La sicurezza odia GTM. GTM inserisce gli script in modo dinamico. GTM propaga il nonce? Sì, se configurato correttamente.
Nel tuo snippet GTM, devi controllare il “nonce” dello script principale e trasmetterlo.
Tuttavia, la maggior parte dei pixel di terze parti (Facebook) non sono consapevoli.
Compromesso: spesso devi inserire nella whitelist i loro domini in script-src oltre al nonce.
script-src 'self' 'nonce-...' https://connect.facebook.net ...
5. Prevenire il clickjacking (antenati dei frame)
Il clickjacking avviene quando un utente malintenzionato incorpora il tuo sito in un <iframe> sul suo sito (evil.com).
Hanno messo un pulsante invisibile “Vinci iPhone” sopra il pulsante “Acquista ora”.
L’utente pensa di fare clic su “Vinci”, ma sta facendo clic su “Acquista”.
Correzione: frame-antenati 'nessuno'.
Ciò impedisce a chiunque di eseguire l’iframe sul tuo sito.
Se hai bisogno di essere iframe (ad esempio da un partner), inseriscili nella whitelist: frame-ancestors 'self' https://partner.com.
6. CSP violato? Che succede?
Quando viene violato un CSP, il browser genera un errore semplificato nella console.
Rifiutato di caricare lo script da 'http://evil.com/hack.js' perché viola la seguente direttiva sulla politica di sicurezza dei contenuti: "script-src 'self' ...".
Per l’utente, lo script dannoso semplicemente non viene eseguito. Il resto del sito funziona bene. L’attacco viene neutralizzato silenziosamente.
7. Tipi attendibili (il futuro della difesa XSS)
CSP blocca il caricamento di script dannosi.
Tipi attendibili blocca la scrittura di codice dannoso.
Ti costringe a disinfettare le corde prima che tocchino il DOM.
element.innerHTML = dirtyString; -> Errore browser bloccato.
Devi avvolgerlo:
element.innerHTML = DOMPurify.sanitize(dirtyString);
Questo crea un oggetto “Politica”. Il browser garantisce che solo le stringhe create dalla policy possano toccare il DOM.
Distrugge un’intera classe di vulnerabilità XSS basate su DOM.
8. Sicurezza dei lavoratori web
I lavoratori (Service Worker, Web Worker) sono isolati. Ma possono comunque essere pericolosi (Crypto Mining). È necessario limitarli tramite direttive specifiche:
worker-src 'self' blob:;: consente solo i lavoratori del tuo dominio.child-src 'self': Limita iframe e lavoratori. Gli aggressori adorano avviare un Worker in background per eseguire DDOS su altri siti o estrarre Bitcoin. Il tuo CSP lo impedisce.
10. CSP per WebAssembly (WASM)
Se usi WASM (ad esempio per ridimensionare le immagini lato client), hai bisogno di direttive speciali.
script-src 'unsafe-eval' (per la compilazione WASM) è pericoloso.
I browser moderni supportano: script-src 'wasm-unsafe-eval'.
Ciò consente a WASM di compilare senza aprire la porta a JavaScript eval().
Mantiene le parti “Bad Good” separate dalle parti “Bad Bad”.
11. Il ciclo di vita del Nonce (ambito della richiesta)
Un Nonce è inutile se è statico.
Se inserisci nonce="123" nel codice HTML, un utente malintenzionato inserisce semplicemente <script nonce="123">.
Il Nonce DEVE essere generato per richiesta.
Modello remix:
entry.server.tsx: generanonce = crypto.randomUUID().- Passare a
<RemixServer nonce={nonce}>. - Idratare il client con
<Script nonce={nonce}>. Ciò garantisce che se aggiorno la pagina, ottengo un nuovo nonce. L’attaccante non può indovinarlo.
12. Stili e script (“unsafe-inline”)
Spesso consentiamo style-src 'unsafe-inline'.
Perché?
Poiché le librerie CSS-in-JS (Emotion, Styled Components) inseriscono i tag " (Zero-Runtime CSS) che genera file .css statici.
Quindi puoi rimuovere 'unsafe-inline' e ottenere il CSP Nirvana.
13. Perché Maison Code?
Noi di Maison Code crediamo che Sicurezza sia reputazione. Un hack non costa solo denaro; costa fiducia. Non ci accontentiamo del “Funziona”. Chiediamo “È sicuro”. Implementiamo CSP Strict Crypto-Nonce per ogni cliente. Monitoriamo le violazioni (Sentry) e risolviamo i buchi prima che vengano sfruttati. Dormiamo bene la notte perché sappiamo che il browser sta applicando le nostre regole.
12. Conclusione
Un CSP rigoroso è il segno distintivo di un team di ingegneri maturo. Dimostra che comprendi l’ambiente ostile del web. Protegge i tuoi clienti da Magecart, keylogger ed esfiltrazione di dati. È difficile da configurare, ma obbligatorio per qualsiasi marchio che acquisisce i dati di pagamento. Non aspettare la violazione. Chiudi la porta adesso.
Il tuo negozio è vulnerabile?
Eseguiamo Penetration Test e implementazione CSP per i marchi Shopify Plus.