Un guide complet pour comprendre et implémenter le Cross-Origin Resource Sharing (CORS) pour une communication JavaScript sécurisée entre différents domaines.
Implémentation de la sécurité Cross-Origin : Meilleures pratiques pour la communication en JavaScript
Dans le web interconnectĂ© d'aujourd'hui, les applications JavaScript ont frĂ©quemment besoin d'interagir avec des ressources provenant de diffĂ©rentes origines (domaines, protocoles ou ports). Cette interaction est rĂ©gie par la politique de mĂȘme origine (Same-Origin Policy) du navigateur, un mĂ©canisme de sĂ©curitĂ© crucial conçu pour empĂȘcher les scripts malveillants d'accĂ©der Ă des donnĂ©es sensibles au-delĂ des frontiĂšres de domaine. Cependant, une communication lĂ©gitime entre origines diffĂ©rentes est souvent nĂ©cessaire. C'est lĂ qu'intervient le Cross-Origin Resource Sharing (CORS). Cet article offre un aperçu complet du CORS, de son implĂ©mentation et des meilleures pratiques pour une communication cross-origin sĂ©curisĂ©e en JavaScript.
Comprendre la politique de mĂȘme origine (Same-Origin Policy)
La politique de mĂȘme origine (Same-Origin Policy ou SOP) est un concept de sĂ©curitĂ© fondamental dans les navigateurs web. Elle restreint les scripts exĂ©cutĂ©s sur une origine d'accĂ©der Ă des ressources d'une origine diffĂ©rente. Une origine est dĂ©finie par la combinaison du protocole (par exemple, HTTP ou HTTPS), du nom de domaine (par exemple, example.com) et du numĂ©ro de port (par exemple, 80 ou 443). Deux URL ont la mĂȘme origine uniquement si ces trois composants correspondent exactement.
Par exemple :
http://www.example.comethttp://www.example.com/path: MĂȘme originehttp://www.example.comethttps://www.example.com: Origine diffĂ©rente (protocole diffĂ©rent)http://www.example.comethttp://subdomain.example.com: Origine diffĂ©rente (domaine diffĂ©rent)http://www.example.com:80ethttp://www.example.com:8080: Origine diffĂ©rente (port diffĂ©rent)
La SOP est une dĂ©fense essentielle contre les attaques de Cross-Site Scripting (XSS), oĂč des scripts malveillants injectĂ©s dans un site web peuvent voler des donnĂ©es utilisateur ou effectuer des actions non autorisĂ©es au nom de l'utilisateur.
Qu'est-ce que le Cross-Origin Resource Sharing (CORS) ?
CORS est un mĂ©canisme qui utilise des en-tĂȘtes HTTP pour permettre aux serveurs d'indiquer quelles origines (domaines, schĂ©mas ou ports) sont autorisĂ©es Ă accĂ©der Ă leurs ressources. Il assouplit essentiellement la politique de mĂȘme origine pour des requĂȘtes cross-origin spĂ©cifiques, permettant une communication lĂ©gitime tout en protĂ©geant contre les attaques malveillantes.
CORS fonctionne en ajoutant de nouveaux en-tĂȘtes HTTP qui spĂ©cifient les origines autorisĂ©es et les mĂ©thodes (par exemple, GET, POST, PUT, DELETE) permises pour les requĂȘtes cross-origin. Lorsqu'un navigateur effectue une requĂȘte cross-origin, il envoie un en-tĂȘte Origin avec la requĂȘte. Le serveur rĂ©pond avec un en-tĂȘte Access-Control-Allow-Origin qui spĂ©cifie la ou les origines autorisĂ©es. Si l'origine de la requĂȘte correspond Ă la valeur de l'en-tĂȘte Access-Control-Allow-Origin (ou si la valeur est *), le navigateur autorise le code JavaScript Ă accĂ©der Ă la rĂ©ponse.
Comment fonctionne le CORS : Une explication détaillée
Le processus CORS implique gĂ©nĂ©ralement deux types de requĂȘtes :
- RequĂȘtes simples : Ce sont des requĂȘtes qui rĂ©pondent Ă des critĂšres spĂ©cifiques. Si une requĂȘte remplit ces conditions, le navigateur envoie directement la requĂȘte.
- RequĂȘtes prĂ©-vĂ©rifiĂ©es (Preflighted) : Ce sont des requĂȘtes plus complexes qui nĂ©cessitent que le navigateur envoie d'abord une requĂȘte "preflight" OPTIONS au serveur pour dĂ©terminer si la requĂȘte rĂ©elle peut ĂȘtre envoyĂ©e en toute sĂ©curitĂ©.
1. RequĂȘtes simples
Une requĂȘte est considĂ©rĂ©e comme "simple" si elle remplit toutes les conditions suivantes :
- La méthode est
GET,HEADouPOST. - Si la méthode est
POST, l'en-tĂȘteContent-Typeest l'un des suivants : application/x-www-form-urlencodedmultipart/form-datatext/plain- Aucun en-tĂȘte personnalisĂ© n'est dĂ©fini.
Exemple d'une requĂȘte simple :
GET /resource HTTP/1.1
Origin: http://www.example.com
Exemple d'une réponse de serveur autorisant l'origine :
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Content-Type: application/json
{
"data": "Quelques données"
}
Si l'en-tĂȘte Access-Control-Allow-Origin est prĂ©sent et que sa valeur correspond Ă l'origine de la requĂȘte ou est dĂ©finie sur *, le navigateur autorise le script Ă accĂ©der aux donnĂ©es de la rĂ©ponse. Sinon, le navigateur bloque l'accĂšs Ă la rĂ©ponse et un message d'erreur s'affiche dans la console.
2. RequĂȘtes prĂ©-vĂ©rifiĂ©es (Preflighted)
Une requĂȘte est considĂ©rĂ©e comme "prĂ©-vĂ©rifiĂ©e" si elle ne remplit pas les critĂšres d'une requĂȘte simple. Cela se produit gĂ©nĂ©ralement lorsque la requĂȘte utilise une mĂ©thode HTTP diffĂ©rente (par exemple, PUT, DELETE), dĂ©finit des en-tĂȘtes personnalisĂ©s ou utilise un Content-Type autre que les valeurs autorisĂ©es.
Avant d'envoyer la requĂȘte rĂ©elle, le navigateur envoie d'abord une requĂȘte OPTIONS au serveur. Cette requĂȘte "preflight" inclut les en-tĂȘtes suivants :
Origin: L'origine de la page Ă©mettrice.Access-Control-Request-Method: La mĂ©thode HTTP qui sera utilisĂ©e dans la requĂȘte rĂ©elle (par exemple,PUT,DELETE).Access-Control-Request-Headers: Une liste sĂ©parĂ©e par des virgules des en-tĂȘtes personnalisĂ©s qui seront envoyĂ©s dans la requĂȘte rĂ©elle.
Exemple d'une requĂȘte preflight :
OPTIONS /resource HTTP/1.1
Origin: http://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header, Content-Type
Le serveur doit rĂ©pondre Ă la requĂȘte OPTIONS avec les en-tĂȘtes suivants :
Access-Control-Allow-Origin: L'origine qui est autorisĂ©e Ă effectuer la requĂȘte (ou*pour autoriser n'importe quelle origine).Access-Control-Allow-Methods: Une liste sĂ©parĂ©e par des virgules des mĂ©thodes HTTP autorisĂ©es pour les requĂȘtes cross-origin (par exemple,GET,POST,PUT,DELETE).Access-Control-Allow-Headers: Une liste sĂ©parĂ©e par des virgules des en-tĂȘtes personnalisĂ©s qui sont autorisĂ©s Ă ĂȘtre envoyĂ©s dans la requĂȘte.Access-Control-Max-Age: Le nombre de secondes pendant lesquelles la rĂ©ponse preflight peut ĂȘtre mise en cache par le navigateur.
Exemple d'une rĂ©ponse de serveur Ă une requĂȘte preflight :
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header, Content-Type
Access-Control-Max-Age: 86400
Si la rĂ©ponse du serveur Ă la requĂȘte preflight indique que la requĂȘte rĂ©elle est autorisĂ©e, le navigateur enverra alors la requĂȘte rĂ©elle. Sinon, le navigateur bloquera la requĂȘte et affichera un message d'erreur.
Implémenter le CORS cÎté serveur
Le CORS est principalement implĂ©mentĂ© cĂŽtĂ© serveur en dĂ©finissant les en-tĂȘtes HTTP appropriĂ©s dans la rĂ©ponse. Les dĂ©tails spĂ©cifiques de l'implĂ©mentation varieront en fonction de la technologie cĂŽtĂ© serveur utilisĂ©e.
Exemple avec Node.js et Express :
const express = require('express');
const cors = require('cors');
const app = express();
// Active le CORS pour toutes les origines
app.use(cors());
// Alternativement, configurez le CORS pour des origines spécifiques
// const corsOptions = {
// origin: 'http://www.example.com'
// };
// app.use(cors(corsOptions));
app.get('/resource', (req, res) => {
res.json({ message: 'Ceci est une ressource compatible CORS' });
});
app.listen(3000, () => {
console.log('Serveur en écoute sur le port 3000');
});
Le middleware cors simplifie le processus de dĂ©finition des en-tĂȘtes CORS dans Express. Vous pouvez activer le CORS pour toutes les origines en utilisant cors() ou le configurer pour des origines spĂ©cifiques en utilisant cors(corsOptions).
Exemple avec Python et Flask :
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/resource")
def hello():
return {"message": "Ceci est une ressource compatible CORS"}
if __name__ == '__main__':
app.run(debug=True)
L'extension flask_cors offre un moyen simple d'activer le CORS dans les applications Flask. Vous pouvez activer le CORS pour toutes les origines en passant app à CORS(). La configuration pour des origines spécifiques est également possible.
Exemple avec Java et Spring Boot :
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/resource")
.allowedOrigins("http://www.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("Content-Type", "X-Custom-Header")
.allowCredentials(true)
.maxAge(3600);
}
}
Dans Spring Boot, vous pouvez configurer le CORS en utilisant un WebMvcConfigurer. Cela permet un contrĂŽle fin sur les origines, mĂ©thodes, en-tĂȘtes autorisĂ©s et autres paramĂštres CORS.
DĂ©finir les en-tĂȘtes CORS directement (Exemple gĂ©nĂ©rique)
Si vous n'utilisez aucun framework, vous pouvez dĂ©finir les en-tĂȘtes directement dans votre code cĂŽtĂ© serveur (par exemple, PHP, Ruby on Rails, etc.) :
Meilleures pratiques CORS
Pour garantir une communication cross-origin sécurisée et efficace, suivez ces meilleures pratiques :
- Ăvitez d'utiliser
Access-Control-Allow-Origin: *en production : Autoriser toutes les origines Ă accĂ©der Ă vos ressources peut ĂȘtre un risque de sĂ©curitĂ©. SpĂ©cifiez plutĂŽt les origines exactes qui sont autorisĂ©es. - Utilisez HTTPS : Utilisez toujours HTTPS pour l'origine Ă©mettrice et l'origine du serveur afin de protĂ©ger les donnĂ©es en transit.
- Validez les entrĂ©es : Validez et nettoyez toujours les donnĂ©es reçues des requĂȘtes cross-origin pour prĂ©venir les attaques par injection.
- Implémentez une authentification et une autorisation appropriées : Assurez-vous que seuls les utilisateurs autorisés peuvent accéder aux ressources sensibles.
- Mettez en cache les réponses preflight : Utilisez
Access-Control-Max-Agepour mettre en cache les rĂ©ponses preflight et rĂ©duire le nombre de requĂȘtesOPTIONS. - Envisagez d'utiliser les informations d'identification (credentials) : Si votre API nĂ©cessite une authentification avec des cookies ou une authentification HTTP, vous devez dĂ©finir l'en-tĂȘte
Access-Control-Allow-CredentialssurtruecĂŽtĂ© serveur et l'optioncredentialssur'include'dans votre code JavaScript (par exemple, en utilisantfetchouXMLHttpRequest). Soyez extrĂȘmement prudent en utilisant cette option, car elle peut introduire des vulnĂ©rabilitĂ©s de sĂ©curitĂ© si elle n'est pas gĂ©rĂ©e correctement. De plus, lorsque Access-Control-Allow-Credentials est dĂ©fini sur true, Access-Control-Allow-Origin ne peut pas ĂȘtre dĂ©fini sur "*". Vous devez spĂ©cifier explicitement la ou les origines autorisĂ©es. - RĂ©visez et mettez Ă jour rĂ©guliĂšrement la configuration CORS : Ă mesure que votre application Ă©volue, rĂ©visez et mettez Ă jour rĂ©guliĂšrement votre configuration CORS pour vous assurer qu'elle reste sĂ©curisĂ©e et rĂ©pond Ă vos besoins.
- Comprenez les implications des différentes configurations CORS : Soyez conscient des implications de sécurité des différentes configurations CORS et choisissez celle qui est appropriée pour votre application.
- Testez votre implĂ©mentation CORS : Testez minutieusement votre implĂ©mentation CORS pour vous assurer qu'elle fonctionne comme prĂ©vu et qu'elle n'introduit aucune vulnĂ©rabilitĂ© de sĂ©curitĂ©. Utilisez les outils de dĂ©veloppement du navigateur pour inspecter les requĂȘtes et rĂ©ponses rĂ©seau, et utilisez des outils de test automatisĂ©s pour vĂ©rifier le comportement du CORS.
Exemple : Utilisation de l'API Fetch avec CORS
Voici un exemple d'utilisation de l'API fetch pour effectuer une requĂȘte cross-origin :
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors', // Indique au navigateur qu'il s'agit d'une requĂȘte CORS
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
}
})
.then(response => {
if (!response.ok) {
throw new Error('La réponse du réseau n'était pas OK');
}
return response.json();
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error('Un problÚme est survenu avec l'opération fetch :', error);
});
L'option mode: 'cors' indique au navigateur qu'il s'agit d'une requĂȘte CORS. Si le serveur n'autorise pas l'origine, le navigateur bloquera l'accĂšs Ă la rĂ©ponse, et une erreur sera levĂ©e.
Si vous utilisez des informations d'identification (par exemple, des cookies), vous devez définir l'option credentials sur 'include' :
fetch('https://api.example.com/data', {
method: 'GET',
mode: 'cors',
credentials: 'include', // Inclure les cookies dans la requĂȘte
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
// ...
});
CORS et JSONP
JSON with Padding (JSONP) est une ancienne technique pour contourner la politique de mĂȘme origine. Elle fonctionne en crĂ©ant dynamiquement une balise <script> qui charge des donnĂ©es depuis un domaine diffĂ©rent. Bien que JSONP puisse ĂȘtre utile dans certaines situations, il prĂ©sente des limitations de sĂ©curitĂ© importantes et doit ĂȘtre Ă©vitĂ© lorsque cela est possible. CORS est la solution privilĂ©giĂ©e pour la communication cross-origin car il offre un mĂ©canisme plus sĂ©curisĂ© et flexible.
Différences clés entre CORS et JSONP :
- Sécurité : CORS est plus sécurisé que JSONP car il permet au serveur de contrÎler quelles origines sont autorisées à accéder à ses ressources. JSONP ne fournit aucun contrÎle d'origine.
- Méthodes HTTP : CORS prend en charge toutes les méthodes HTTP (par exemple,
GET,POST,PUT,DELETE), tandis que JSONP ne prend en charge que les requĂȘtesGET. - Gestion des erreurs : CORS offre une meilleure gestion des erreurs que JSONP. Lorsqu'une requĂȘte CORS Ă©choue, le navigateur fournit des messages d'erreur dĂ©taillĂ©s. La gestion des erreurs JSONP se limite Ă dĂ©tecter si le script a Ă©tĂ© chargĂ© avec succĂšs.
Dépannage des problÚmes CORS
Les problĂšmes CORS peuvent ĂȘtre frustrants Ă dĂ©boguer. Voici quelques conseils de dĂ©pannage courants :
- Vérifiez la console du navigateur : La console du navigateur fournira généralement des messages d'erreur détaillés sur les problÚmes CORS.
- Inspectez les requĂȘtes rĂ©seau : Utilisez les outils de dĂ©veloppement du navigateur pour inspecter les en-tĂȘtes HTTP de la requĂȘte et de la rĂ©ponse. VĂ©rifiez que les en-tĂȘtes
OriginetAccess-Control-Allow-Originsont correctement dĂ©finis. - VĂ©rifiez la configuration cĂŽtĂ© serveur : RevĂ©rifiez votre configuration CORS cĂŽtĂ© serveur pour vous assurer qu'elle autorise les bonnes origines, mĂ©thodes et en-tĂȘtes.
- Videz le cache du navigateur : Parfois, les rĂ©ponses preflight mises en cache peuvent causer des problĂšmes CORS. Essayez de vider le cache de votre navigateur ou d'utiliser une fenĂȘtre de navigation privĂ©e.
- Utilisez un proxy CORS : Dans certains cas, vous pourriez avoir besoin d'utiliser un proxy CORS pour contourner les restrictions CORS. Cependant, soyez conscient que l'utilisation d'un proxy CORS peut introduire des risques de sécurité.
- Recherchez les erreurs de configuration : Recherchez les erreurs de configuration courantes telles qu'un en-tĂȘte
Access-Control-Allow-Originmanquant, des valeurs incorrectes pourAccess-Control-Allow-MethodsouAccess-Control-Allow-Headers, ou un en-tĂȘteOriginincorrect dans la requĂȘte.
Conclusion
Le Cross-Origin Resource Sharing (CORS) est un mĂ©canisme essentiel pour permettre une communication cross-origin sĂ©curisĂ©e dans les applications JavaScript. En comprenant la politique de mĂȘme origine, le fonctionnement de CORS et les divers en-tĂȘtes HTTP impliquĂ©s, les dĂ©veloppeurs peuvent implĂ©menter CORS efficacement pour protĂ©ger leurs applications contre les vulnĂ©rabilitĂ©s de sĂ©curitĂ© tout en autorisant les requĂȘtes cross-origin lĂ©gitimes. Suivre les meilleures pratiques pour la configuration CORS et rĂ©viser rĂ©guliĂšrement votre implĂ©mentation sont cruciaux pour maintenir une application web sĂ©curisĂ©e et robuste.
Ce guide complet fournit une base solide pour comprendre et implémenter le CORS. N'oubliez pas de consulter la documentation officielle et les ressources pour votre technologie cÎté serveur spécifique afin de vous assurer que vous implémentez le CORS correctement et en toute sécurité.