10.12. CORS et CSRF

Le CORS et le CSRF sont les deux protections côté navigateur dont une caméra exposée sur l’internet ouvert a besoin, en plus du HTTPS et de l’authentification. Chacune se met en place en quelques lignes. Les sections ci-dessous définissent le terme et montrent l’intégration avec microdot.

10.12.1. Ce que fait le CORS

Le Cross-Origin Resource Sharing (CORS) est le mécanisme du navigateur qui permet à un serveur de choisir d’autoriser des origines tierces précises à lire ses réponses. La politique de même origine par défaut du navigateur bloque cette lecture : le JavaScript sur https://example.com ne peut pas lire les réponses de https://yard-cam.example.com, car un hôte différent constitue une origine différente. Le CORS est le moyen, côté serveur, d’accorder des exceptions à des pairs choisis.

Si le tableau de bord est servi depuis la caméra elle-même, chaque requête est de même origine et le CORS n’a aucun effet. La configuration prend de l’importance lorsque le tableau de bord réside ailleurs – une URL publique comme https://app.example.com qui dialogue avec une caméra située à 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 est la liste des origines autorisées à lire les réponses de la caméra. L’origine du tableau de bord et uniquement celle du tableau de bord – pas * – afin qu’un site tiers ne puisse pas lire les réponses de la caméra par accident.

allow_credentials=True permet aux requêtes inter-origines d’inclure le cookie de session, ce dont le tableau de bord a besoin pour rester connecté au-delà d’une frontière d’origine.

max_age=86400 indique au navigateur qu’il peut mettre en cache le résultat du contrôle préalable (preflight) pendant une journée. Les navigateurs émettent une requête OPTIONS supplémentaire avant tout appel inter-origines utilisant des méthodes autres que GET/HEAD/POST ou envoyant des en-têtes personnalisés ; max_age réduit ce surcoût à un seul contrôle préalable par jour et par route.

10.12.2. Ce que fait le CSRF

La Cross-Site Request Forgery (CSRF) est l’attaque par laquelle une page malveillante fait émettre par le navigateur de l’utilisateur une requête authentifiée vers un serveur de confiance. Même avec le CORS en place, un <form> caché sur evil.com qui envoie un POST vers https://yard-cam.example.com/config atteindra la caméra, et le navigateur y joindra le cookie de session de la caméra – les cookies suivent l’hôte de destination, et non l’origine de la page qui émet la requête – de sorte que la caméra traite volontiers le POST comme s’il provenait du propriétaire.

La protection CSRF rejette ces requêtes. microdot.csrf.CSRF ajoute un intergiciel qui inspecte l’en-tête Sec-Fetch-Site sur chaque requête modifiant l’état et rejette tout ce qui n’est pas étiqueté same-origin (ou provenant d’une origine autorisée par le CORS) :

from microdot.csrf import CSRF

CSRF(app, cors=cors)

Le passage de l’instance cors permet à l’intergiciel d’admettre par antériorité l’origine autorisée du tableau de bord – la caméra accepte toujours les POST du tableau de bord même s’ils sont inter-origines.

Sec-Fetch-Site est défini automatiquement par les navigateurs modernes ; la caméra n’a rien à faire côté client. Pour les navigateurs plus anciens qui n’envoient pas l’en-tête, la liste d’autorisation CORS sert de vérification de repli.

10.12.3. Exempter les webhooks

Si la caméra a besoin d’un point de terminaison webhook pour accepter des POST provenant d’un service cloud tiers – un rappel du fournisseur d’archivage, par exemple – marquez la route @csrf.exempt afin que l’intergiciel la laisse passer. Le gestionnaire est alors responsable de vérifier la requête autrement – généralement au moyen d’un Hash-based Message Authentication Code (HMAC) calculé sur la charge utile à l’aide d’un secret partagé entre la caméra et le tiers, ce qui prouve que la requête provient de quelqu’un connaissant le secret. La caméra de jardin n’en utilise aucun, mais le décorateur est là quand vous en avez besoin.

10.12.4. La base en quatre lignes

Une fois le HTTPS en place, la pile recommandée pour tout déploiement de caméra exposée à l’internet est :

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)

Session et authentification plus tôt dans le chapitre, CORS et CSRF ici, HTTPS dans le sujet précédent. Les quatre éléments s’empilent les uns sur les autres et restent à l’écart de chaque route.

La caméra peut désormais affronter en toute sécurité l’internet ouvert – HTTPS, authentification, CSRF, CORS.