Dans le paysage moderne du développement web, la communication inter-domaines n'est pas seulement une fonctionnalité, c'est une exigence fondamentale. Que vous construisiez une application monopage (SPA) hébergée sur app.example.com consommant une API sur api.example.com, ou que vous intégriez des microservices tiers, vous rencontrerez inévitablement le protocole de partage de ressources d'origine croisée (CORS). Bien que le CORS soit conçu pour protéger les utilisateurs, une mauvaise configuration reste l'une des sources les plus courantes de vulnérabilités de sécurité et de problèmes de développement. Ce guide propose une plongée technique approfondie sur la manière de configurer le CORS de manière efficace, sécurisée et performante.
Comprendre la politique d'origine identique
Pour apprécier le CORS, il faut d'abord comprendre la politique d'origine identique (SOP) du navigateur. La SOP est un mécanisme de sécurité critique qui isole les documents potentiellement malveillants en restreignant la manière dont un document ou un script chargé depuis une origine peut interagir avec une ressource provenant d'une autre origine. « Origine » est définie par le schéma, l'hôte et le port (par exemple, https://example.com:443).
Lorsqu'un navigateur initie une requête vers une origine différente, il ne bloque pas la requête directement, mais intercepte la réponse. Si le serveur n'accorde pas explicitement la permission via des en-têtes HTTP spécifiques, le navigateur bloque la lecture de la réponse par le script côté client. Cela empêche les attaquants de voler des données sensibles depuis d'autres domaines via des scripts malveillants sur une page compromise.
En-têtes CORS clés expliqués
Le CORS fonctionne grâce à un ensemble d'en-têtes HTTP échangés entre le client et le serveur. Voici les en-têtes critiques que vous devez maîtriser :
- Access-Control-Allow-Origin (ACAO) : Spécifie quelles origines sont autorisées. Il peut s'agir d'une origine spécifique (
https://frontend.com) ou d'un caractère générique (*). - Access-Control-Allow-Methods (ACAM) : Liste les méthodes HTTP autorisées (GET, POST, PUT, DELETE, etc.).
- Access-Control-Allow-Headers (ACAH) : Définit quels en-têtes peuvent être utilisés lors de la requête effective (par exemple,
Content-Type,Authorization). - Access-Control-Allow-Credentials (ACAC) : Un booléen indiquant si la requête peut inclure des informations d'identification utilisateur telles que les cookies, les en-têtes d'autorisation ou les certificats client TLS.
Il est crucial de noter que lorsque Access-Control-Allow-Credentials est défini sur true, le caractère générique * ne peut pas être utilisé pour Access-Control-Allow-Origin. Le navigateur rejettera la réponse s'il reçoit un ACAO générique accompagné d'informations d'identification. Il s'agit d'un piège courant pour les développeurs qui tentent d'activer l'authentification basée sur les sessions.
Exemples de configuration pratiques
Examinons comment configurer le CORS dans un environnement Node.js/Express typique. Cet exemple démontre une configuration sécurisée qui gère dynamiquement les requêtes préliminaires (preflight) et valide les origines par rapport à une liste blanche.
const express = require('express');
const app = express();
// Définir les origines autorisées pour une gestion dynamique
const allowedOrigins = ['https://frontend.example.com', 'https://admin.example.com'];
app.use((req, res, next) => {
const origin = req.headers.origin;
// Vérifier si l'origine de la requête est dans la liste autorisée
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
// Définir les méthodes et en-têtes autorisés
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// Autoriser les informations d'identification
res.setHeader('Access-Control-Allow-Credentials', true);
// Gérer les requêtes préliminaires
if (req.method === 'OPTIONS') {
return res.sendStatus(204);
}
next();
});
Dans cet extrait, nous évitons le risque de sécurité lié à l'utilisation de * en validant l'en-tête Origin entrant par rapport à une liste blanche stricte. Nous nous assurons également que le client ne peut pas envoyer des en-têtes arbitraires en les listant explicitement dans Allow-Headers. La vérification préliminaire (requête OPTIONS) est gérée correctement pour éviter un traitement inutile pour les requêtes de données réelles.
Meilleures pratiques pour un CORS sécurisé
- Ne jamais utiliser de caractères génériques avec des informations d'identification : Comme mentionné,
Access-Control-Allow-Credentials: trueetAccess-Control-Allow-Origin: *sont mutuellement exclusifs dans les navigateurs conformes. - Minimiser les origines autorisées : Utilisez toujours une liste blanche de domaines spécifiques plutôt que d'ouvrir l'accès à tous les domaines.
- Restreindre les méthodes et les en-têtes : Exposez uniquement les méthodes HTTP et les en-têtes dont votre API a réellement besoin. Des configurations trop permissives augmentent la surface d'attaque.
- Gérer correctement les requêtes préliminaires : Assurez-vous que votre serveur répond avec
204 No Contentou200 OKpour les requêtes OPTIONS sans exécuter votre logique métier. - Éviter de stocker des données sensibles dans les API publiques : Le CORS est une politique imposée par le navigateur. Il n'empêche pas les requêtes HTTP directes provenant de serveurs ou d'outils comme cURL. Par conséquent, ne comptez jamais sur le CORS pour protéger des données sensibles côté serveur ; mettez toujours en place une authentification et une autorisation appropriées.
Conclusion
Le CORS est un outil puissant qui permet l'interopérabilité requise par les applications web modernes, mais il nécessite une configuration minutieuse pour maintenir la sécurité. En comprenant les mécanismes sous-jacents, en utilisant des en-têtes spécifiques et en adhérant au principe du moindre privilège, vous pouvez construire des applications robustes qui sont à la fois fonctionnelles et sécurisées. Rappelez-vous que le CORS n'est qu'une couche de défense ; complétez-le toujours par une authentification forte, une validation des entrées et des vérifications d'autorisation côté serveur.