10.12. CORS e CSRF#

CORS e CSRF sono le due protezioni lato browser di cui una camera esposta su internet ha bisogno, insieme a HTTPS e login. Ciascuna richiede poche righe per essere configurata. Le sezioni seguenti definiscono il termine e mostrano l’integrazione con microdot.

10.12.1. Cosa fa CORS#

Il Cross-Origin Resource Sharing (CORS) è il meccanismo del browser che consente a un server di scegliere esplicitamente di permettere ad altre origini specifiche di leggere le sue risposte. La same-origin policy predefinita del browser blocca questa lettura: il JavaScript su https://example.com non può leggere le risposte da https://yard-cam.example.com, perché un host diverso conta come origine diversa. Il CORS è il modo, lato server, di concedere eccezioni a peer scelti.

Se la dashboard viene servita dalla camera stessa, ogni richiesta è same-origin e il CORS non fa nulla. La configurazione conta quando la dashboard risiede altrove: un URL pubblico come https://app.example.com che comunica con una camera all’indirizzo https://yard-cam.example.com:

from microdot.cors import CORS

cors = CORS(
    app,
    allowed_origins=['https://app.example.com'],
    allow_credentials=True,
    max_age=86400,
)

allowed_origins è l’elenco delle origini autorizzate a leggere le risposte della camera. L’origine della dashboard e solo l’origine della dashboard – non * – così che un sito di terze parti non possa leggere per errore le risposte della camera.

allow_credentials=True consente alle richieste cross-origin di includere il cookie di sessione, che è ciò di cui la dashboard ha bisogno per restare autenticata oltre il confine tra le origini.

max_age=86400 indica al browser che può mettere in cache il risultato del preflight per un giorno. I browser inviano una richiesta OPTIONS aggiuntiva prima di qualsiasi chiamata cross-origin che usa metodi diversi da GET/HEAD/POST o che invia header personalizzati; max_age riduce questo overhead a un preflight al giorno per route.

10.12.2. Cosa fa CSRF#

Il Cross-Site Request Forgery (CSRF) è l’attacco in cui una pagina malevola fa sì che il browser dell’utente invii una richiesta autenticata a un server fidato. Anche con il CORS attivo, un <form> nascosto su evil.com che fa POST verso https://yard-cam.example.com/config raggiungerà la camera, e il browser allegherà il cookie di sessione della camera – i cookie seguono l’host di destinazione, non l’origine della pagina che effettua la richiesta – perciò la camera elaborerà tranquillamente il POST come se provenisse dal proprietario.

La protezione CSRF rifiuta tali richieste. microdot.csrf.CSRF aggiunge un middleware che ispeziona l’header Sec-Fetch-Site su ogni richiesta che modifica lo stato e rifiuta tutto ciò che non è etichettato come same-origin (o che proviene da un’origine consentita dal CORS):

from microdot.csrf import CSRF

CSRF(app, cors=cors)

Passare l’istanza cors consente al middleware di ammettere automaticamente l’origine consentita della dashboard: la camera accetta comunque i POST della dashboard anche se sono cross-origin.

Sec-Fetch-Site viene impostato automaticamente dai browser moderni; la camera non deve fare nulla sul lato client. Per i browser più vecchi che non inviano questo header, l’elenco di origini consentite dal CORS funge da controllo di riserva.

10.12.3. Esenzione dei webhook#

Se la camera necessita di un endpoint webhook per accettare POST da un servizio cloud di terze parti – ad esempio una callback dal fornitore dell’archivio – contrassegna la route con @csrf.exempt in modo che il middleware la lasci passare. L’handler è responsabile di verificare la richiesta in qualche altro modo, di solito tramite un Hash-based Message Authentication Code (HMAC) calcolato sul payload con un segreto condiviso tra la camera e la terza parte, che dimostra che la richiesta proviene da qualcuno che conosceva il segreto. La camera del cortile non ha nulla di tutto ciò, ma il decoratore è disponibile quando ne hai bisogno.

10.12.4. La base di quattro righe#

Una volta che HTTPS è attivo, lo stack consigliato per qualsiasi distribuzione di una camera esposta su internet è:

Session(app, secret_key=SECRET,
        cookie_options={'http_only': True, 'secure': True})
login = Login()
cors = CORS(app, allowed_origins=[...], allow_credentials=True)
CSRF(app, cors=cors)

Sessione e login più indietro nel capitolo, CORS e CSRF qui, HTTPS dall’argomento precedente. I quattro elementi si sovrappongono l’uno all’altro e non interferiscono con il funzionamento di nessuna route.

La camera è ora sicura per essere esposta su internet: HTTPS, login, CSRF, CORS.