Imaginez que votre application bancaire est piratée et que des informations sensibles sont compromises… Ce cauchemar pourrait devenir réalité en raison d'une mauvaise configuration de CORS. La sécurité des API web est cruciale dans le monde numérique actuel, où les applications interagissent constamment avec des services tiers pour fournir des fonctionnalités riches. La protection de ces interfaces contre les vulnérabilités et les attaques potentielles est donc essentielle. Une des principales menaces est l'exploitation des failles Cross-Origin Resource Sharing (CORS), une technique permettant à un site web d'un domaine différent d'accéder aux ressources d'un autre.
CORS (Cross-Origin Resource Sharing) est un mécanisme de sécurité primordial des navigateurs, conçu pour contrôler l'accès aux ressources web entre différents domaines. Il agit comme un garde-fou, autorisant uniquement les sources légitimes à accéder aux données sensibles de votre API, tout en bloquant les requêtes provenant de sources non fiables. Une configuration incorrecte de CORS peut ouvrir la porte à des attaques cross-site dangereuses, permettant à des attaquants de voler des données d'utilisateurs, de modifier des informations ou même de compromettre votre application.
Same-origin policy : le principe fondamental de CORS
Pour comprendre le rôle de CORS, il est essentiel de connaître son principe fondamental : la Same-Origin Policy (SOP). La SOP est un protocole de sécurité de base implémenté dans tous les navigateurs web modernes. Son objectif est d'empêcher un document ou un script chargé à partir d'une origine d'accéder aux ressources d'une autre. C'est le premier rempart de sécurité qui protège les utilisateurs contre les attaques cross-site et assure la confidentialité des données sensibles. Comprendre la SOP est donc capital pour appréhender les enjeux et l'importance de CORS dans la sécurité web.
L'origine d'une ressource web est définie par trois éléments clés : le schéma (protocole, HTTP ou HTTPS), l'hôte (le nom de domaine) et le port. Deux ressources sont considérées comme ayant la même origine si et seulement si ces trois éléments correspondent exactement. Par exemple, `https://www.example.com:443` et `https://www.example.com:443` partagent la même origine, tandis que `http://www.example.com` et `https://api.example.com` sont considérées comme des origines différentes.
- **Accès autorisés :** Un script chargé depuis `https://www.example.com` peut accéder à d'autres ressources (images, scripts, feuilles de style) hébergées sur le même domaine, sans restriction.
- **Accès bloqués :** Par défaut, une requête XHR (XMLHttpRequest) ou `fetch` initiée depuis `https://www.example.com` vers `https://api.different-domain.com` sera bloquée par le navigateur en raison de la SOP.
Bien que la SOP soit un mécanisme puissant, elle peut également être trop restrictive dans certains cas. Par exemple, une application web peut avoir besoin d'accéder à des API hébergées sur des sous-domaines différents (ex: `app.example.com` et `api.example.com`) ou à des services tiers pour des fonctionnalités spécifiques. Dans ces cas, la SOP empêcherait ces interactions, limitant ainsi la flexibilité et les capacités des applications web. C'est là que CORS entre en jeu, offrant un mécanisme contrôlé pour assouplir la SOP et permettre un accès cross-origin sécurisé.
Fonctionnement détaillé de CORS
CORS permet d'assouplir les restrictions de la SOP de manière contrôlée. Il fonctionne en ajoutant des en-têtes HTTP aux requêtes et aux réponses, permettant aux navigateurs de déterminer si une requête cross-origin est autorisée. Ce mécanisme repose sur la communication entre le client (navigateur) et le serveur, garantissant que seul l'accès autorisé est accordé, tout en protégeant les données sensibles contre les attaques.
Types de requêtes CORS
Les requêtes CORS peuvent être classées en deux catégories : les requêtes simples et les requêtes "preflighted". Le type de requête détermine le mécanisme utilisé par le navigateur pour vérifier si l'accès cross-origin est autorisé. Il est donc essentiel de comprendre ces distinctions pour configurer CORS et éviter les erreurs courantes.
Requêtes simples
Une requête est considérée comme "simple" si elle répond à tous les critères suivants :
- La méthode HTTP est `GET`, `HEAD` ou `POST`.
- Les en-têtes HTTP utilisés se limitent à `Accept`, `Accept-Language`, `Content-Language`, `Content-Type` (avec les valeurs `application/x-www-form-urlencoded`, `multipart/form-data` ou `text/plain`).
Même si une requête semble "simple", il est crucial de ne pas la sous-estimer. Une configuration CORS incorrecte peut rendre une application vulnérable, même avec ces requêtes. Le serveur doit toujours vérifier l'origine de la requête et renvoyer l'en-tête `Access-Control-Allow-Origin` approprié, même pour les requêtes "simples".
Requêtes "preflighted" (OPTIONS)
Si une requête ne répond pas aux critères d'une requête "simple", elle est considérée comme une requête "preflighted". Avant d'envoyer la requête principale, le navigateur envoie automatiquement une requête HTTP `OPTIONS` au serveur pour déterminer si la requête cross-origin sera autorisée. Cette requête "preflight" permet au serveur de spécifier les méthodes HTTP et les en-têtes autorisés pour la requête principale. Si elle échoue, la requête principale ne sera pas envoyée.
La requête `OPTIONS` inclut deux en-têtes importants :
- `Access-Control-Request-Method` : Indique la méthode HTTP qui sera utilisée dans la requête principale (ex: `PUT`, `DELETE`).
- `Access-Control-Request-Headers` : Indique les en-têtes HTTP personnalisés qui seront inclus dans la requête principale (ex: `X-Custom-Header`).
Le navigateur effectue une requête `OPTIONS` pour garantir que le serveur a explicitement autorisé la requête cross-origin avant d'envoyer la requête principale. Cela permet de protéger les données sensibles contre les attaques, car le serveur peut refuser les requêtes provenant de sources non autorisées.
Les en-têtes HTTP clés de CORS
Plusieurs en-têtes HTTP sont utilisés pour contrôler l'accès aux ressources web dans le cadre de CORS. Comprendre le rôle de chaque en-tête est essentiel pour configurer CORS et sécuriser vos API.
- `Access-Control-Allow-Origin` : Spécifie l'origine ou les origines autorisées à accéder à la ressource. Il peut prendre les valeurs suivantes :
- `*` : Autorise toutes les origines (à éviter en production).
- `domain` (ex: `https://example.com`) : Autorise uniquement l'origine spécifiée.
- `null` : Utilisé dans certains cas spécifiques, comme les requêtes provenant de fichiers locaux.
- `Access-Control-Allow-Methods` : Spécifie les méthodes HTTP autorisées pour la requête cross-origin (ex: `GET, POST, PUT, DELETE`).
- `Access-Control-Allow-Headers` : Spécifie les en-têtes HTTP autorisés dans la requête cross-origin (ex: `Content-Type, Authorization, X-Custom-Header`).
- `Access-Control-Allow-Credentials` : Indique si la requête cross-origin peut inclure des informations d'identification, telles que des cookies ou des en-têtes d'authentification.
- `Access-Control-Expose-Headers` : Spécifie les en-têtes de réponse qui doivent être exposés au script côté client.
- `Access-Control-Max-Age` : Spécifie la durée pendant laquelle les résultats de la requête "preflight" (OPTIONS) peuvent être mis en cache.
Configuration de CORS : exemples pratiques
La configuration de CORS se fait côté serveur, en ajoutant les en-têtes HTTP aux réponses. La méthode varie selon la plateforme et le framework utilisés. Les exemples suivants illustrent la configuration de CORS dans différents environnements.
Configuration côté serveur (backend)
Node.js (express)
const express = require('express'); const cors = require('cors'); const app = express(); // Utilisation du middleware cors app.use(cors()); // Configuration CORS personnalisée app.use(cors({ origin: 'https://www.example.com', methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'] })); app.get('/api/data', (req, res) => { res.json({ message: 'Données de l'API' }); }); app.listen(3000, () => { console.log('Serveur démarré sur le port 3000'); });
Python (flask)
from flask import Flask from flask_cors import CORS app = Flask(__name__) CORS(app) # Active CORS pour toutes les routes @app.route("/api/data") def hello_world(): return {"message": "Données de l'API"}
PHP
<?php header("Access-Control-Allow-Origin: https://www.example.com"); header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE"); header("Access-Control-Allow-Headers: Content-Type, Authorization"); // Votre code API ici ?>
Nginx
server { listen 80; server_name example.com; location /api/ { add_header 'Access-Control-Allow-Origin' "https://www.example.com"; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization'; if ($request_method = OPTIONS) { add_header 'Access-Control-Allow-Origin' "https://www.example.com"; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization'; add_header 'Content-Type' 'text/plain charset=UTF-8'; add_header 'Content-Length' 0; return 204; } } # ... autres configurations ... }
Apache (.htaccess)
<IfModule mod_headers.c> Header set Access-Control-Allow-Origin "https://www.example.com" Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" Header set Access-Control-Allow-Headers "Content-Type, Authorization" </IfModule>
Configuration côté client (frontend)
Il est essentiel d'éviter de contourner CORS côté client, par exemple en désactivant la sécurité du navigateur. Cette pratique compromet la sécurité de l'application et des utilisateurs. Dans certains cas, une configuration supplémentaire côté client peut être requise, notamment lors de l'envoi d'informations d'identification. L'utilisation de proxies peut être une solution, mais doit être effectuée avec prudence et en tenant compte des risques de sécurité.
Dépannage : erreurs fréquentes de CORS
Les erreurs de configuration CORS sont fréquentes et peuvent entraîner des problèmes d'accès aux API. Comprendre les causes de ces erreurs et savoir comment les diagnostiquer est essentiel pour assurer le bon fonctionnement de vos applications.
- **"No 'Access-Control-Allow-Origin' header is present on the requested resource" :** Le serveur n'a pas renvoyé l'en-tête `Access-Control-Allow-Origin` dans sa réponse.
- **"Preflight response is not successful" :** La requête "preflight" (OPTIONS) a échoué.
- **"Response to preflight request doesn't pass access control check" :** La réponse à la requête "preflight" ne satisfait pas les exigences de sécurité CORS.
- **Problèmes liés aux cookies :** Une configuration incorrecte de `Access-Control-Allow-Credentials` peut entraîner des problèmes avec les cookies.
Pour diagnostiquer et résoudre les problèmes CORS, vous pouvez utiliser les outils de développement de votre navigateur (inspecteur réseau), les extensions de navigateur pour vérifier les en-têtes CORS et les services en ligne pour tester la configuration.
Si vous rencontrez l'erreur "No 'Access-Control-Allow-Origin' header is present on the requested resource", vérifiez les points suivants :
- Assurez-vous que votre serveur inclut bien l'en-tête `Access-Control-Allow-Origin` dans la réponse.
- Vérifiez l'orthographe de l'en-tête.
- Assurez-vous que le serveur répond avec un code de statut HTTP 200 OK. Une erreur serveur (500, 404, etc.) peut empêcher l'envoi des en-têtes CORS.
- Si vous utilisez un proxy, assurez-vous qu'il transmet correctement les en-têtes CORS.
Sécurité avancée : au-delà de la configuration de base
Au-delà de la configuration de base, il est important de prendre en compte des aspects de sécurité avancée pour protéger vos API contre les attaques potentielles. Autoriser l'origine `*` en production est une erreur courante qui peut ouvrir la porte à des attaques cross-site. Il est préférable d'autoriser uniquement les origines spécifiques qui ont besoin d'accéder à votre API. En effet, l'utilisation de `*` désactive la vérification d'origine, permettant à n'importe quel site web d'accéder à vos ressources, ce qui est particulièrement dangereux si votre API gère des données sensibles.
Voici quelques exemples d'attaques possibles et des mesures de protection à envisager :
- **Attaque par redirection d'origine :** Un attaquant peut rediriger l'origine d'une requête pour contourner les restrictions CORS. Pour vous protéger, validez et nettoyez toujours les données d'entrée et utilisez un Content Security Policy (CSP) pour limiter les sources autorisées à charger des ressources sur votre site.
- **Attaque par injection de script :** Une vulnérabilité XSS peut permettre à un attaquant d'exécuter du code malveillant dans le contexte de votre application, contournant ainsi les protections CORS. Utilisez une CSP stricte, échappez les données d'entrée et de sortie, et mettez régulièrement à jour vos bibliothèques et frameworks pour corriger les failles de sécurité connues.
- **Attaque par "clickjacking" :** Un attaquant peut superposer un iframe transparent sur votre site web, incitant les utilisateurs à cliquer sur des éléments cachés, potentiellement en effectuant des actions non désirées sur votre API. Utilisez l'en-tête `X-Frame-Options` ou une CSP pour empêcher votre site d'être intégré dans des iframes provenant de domaines non autorisés.
CORS peut atténuer les risques d'attaques Cross-Site Scripting (XSS), mais ne les élimine pas. Il est important d'utiliser Content Security Policy (CSP) en complément de CORS pour renforcer la sécurité. La CSP permet de définir une liste blanche des sources autorisées pour les ressources (scripts, images, feuilles de style, etc.), réduisant ainsi le risque d'exécution de code malveillant.
Pour les applications web multi-sous-domaines, il est important de prendre en compte les spécificités liées à CORS. La configuration CORS doit être cohérente sur tous les sous-domaines pour éviter les problèmes d'accès et garantir la sécurité.
Alternatives à CORS : cas d'utilisation
Bien que CORS soit la solution la plus répandue pour gérer l'accès cross-origin, il existe d'autres alternatives qui peuvent être plus appropriées dans certains cas. La décision d'utiliser CORS ou une alternative dépendra des besoins spécifiques de votre application, des compromis entre sécurité, performance et complexité.
- **JSONP (JSON with Padding) :** Solution ancienne qui contourne la SOP en utilisant la balise `<script>`. Cependant, elle présente des limitations et des risques importants et doit être évitée dans la plupart des cas. En effet, JSONP ne supporte que les requêtes GET, ce qui limite son utilisation. De plus, elle est vulnérable aux attaques XSS si le serveur renvoie des données malveillantes.
- **Utilisation d'un proxy côté serveur :** Un proxy peut être utilisé pour masquer l'origine de la requête et contourner la SOP. Cependant, cela peut introduire des problèmes de performance et de sécurité. Un proxy inverse peut simplifier la configuration CORS en agissant comme un intermédiaire entre le client et le serveur d'API, mais il faut veiller à ce que le proxy lui-même soit correctement sécurisé.
- **WebSockets :** Les WebSockets permettent des communications bidirectionnelles en temps réel. Ils ne sont pas soumis aux restrictions que les requêtes HTTP et peuvent être utilisés pour contourner la SOP dans certains cas. Cependant, ils nécessitent des aspects de sécurité spécifiques. Les WebSockets nécessitent une gestion attentive de l'authentification et de l'autorisation, car ils maintiennent une connexion persistante.
- **Server-Sent Events (SSE) :** Les SSE permettent les communications unidirectionnelles du serveur vers le client. Comme les WebSockets, ils ne sont pas soumis aux restrictions des requêtes HTTP et peuvent être utilisés dans certains cas. Les SSE sont utiles pour les mises à jour en temps réel du serveur vers le client, comme les flux d'actualités ou les mises à jour de progression.
Sécuriser vos API : un impératif constant
Une configuration CORS correcte est un élément fondamental de la sécurité des API. Elle permet de contrôler l'accès aux ressources, de prévenir les attaques cross-site et d'assurer la compatibilité entre les domaines. Une compréhension des principes de CORS, des en-têtes HTTP et des erreurs est essentielle pour protéger vos API contre les vulnérabilités.
Il est fortement recommandé de revoir régulièrement votre configuration et de mettre en œuvre les meilleures pratiques pour assurer la sécurité. CORS n'est qu'une partie de la sécurité globale de vos API. Il est important de prendre en compte d'autres mesures, telles que l'authentification, l'autorisation, la validation des données et la protection contre les attaques par injection. La sécurité des API est un processus constant qui nécessite une vigilance et une adaptation aux nouvelles menaces.
Plateforme/Framework | Option de configuration CORS | Facilité d'utilisation | Meilleures pratiques |
---|---|---|---|
Node.js (Express) | Middleware `cors` | Élevée | Définir `origin` avec une liste blanche de domaines. |
Python (Flask) | Extension `flask_cors` | Moyenne | Spécifier les origines et les méthodes HTTP explicitement. |
Java (Spring) | Spring Security | Complexe | Utiliser des annotations et configurer les filtres CORS. |
PHP | Fonctions `header()` | Faible | Valider l'origine de la requête et utiliser les en-têtes. |
Nginx | `add_header` directive | Moyenne | Définir les en-têtes CORS dans le bloc `server` ou `location`. |
Apache | `Header` directive | Moyenne | Définir les en-têtes CORS dans le fichier `.htaccess`. |