CORS ou la Gestion des Requêtes Cross-Origin
Dans le développement web moderne, il est courant de devoir interagir avec des ressources situées sur d’autres domaines. Cela soulève la question de la sécurité, car afin de protéger les utilisateurs, les navigateurs web appliquent une politique de même origine (Same-Origin Policy). Cette politique limite la manière dont une page web peut faire des requêtes à un autre domaine que celui d’où elle provient. C’est là qu’intervient le Cross-Origin Resource Sharing (CORS).
Le Contexte des Requêtes Cross-Origin
Imaginons une situation typique : vous développez une application web qui récupère des données d’un serveur situé sur un domaine différent de celui de votre application. Par exemple, votre application, accessible via https://mon-app.example.com
, doit obtenir des informations depuis https://api.example.org
. Cette requête trans-domaine est régie par les politiques de sécurité des navigateurs pour empêcher les attaques potentielles, telles que les attaques de type CSRF (Cross-Site Request Forgery) et XSS (Cross-Site Scripting).
Les Contraintes des Navigateurs et CORS
Les requêtes AJAX, couramment utilisées pour échanger des données entre un client et un serveur, sont sujettes aux restrictions CORS lorsqu’elles traversent les frontières des domaines. Les navigateurs imposent ces restrictions pour empêcher les pages web malveillantes d’accéder aux ressources sensibles d’autres sites web.
Voici un exemple de code JavaScript qui tente de faire une requête AJAX vers un domaine différent
fetch('https://api.example.org/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Erreur:', error));
Et voici ce que peut retourner le navigateur si aucune disposition particulière n’est prise
Solutions pour Contourner les Restrictions CORS
1 – Utilisation de JSONP en vanilla script
Le mécanisme de JSONP (JSON with Padding) repose sur l’utilisation des balises <script>
, qui ne sont pas soumises aux restrictions de la politique de même origine imposées aux requêtes AJAX classiques. En créant dynamiquement une balise <script>
et en l’ajoutant au DOM via appendChild
, le navigateur envoie une requête à un serveur externe.
Le serveur répond alors avec du JavaScript encapsulant les données dans une fonction de rappel définie dans la page d’origine. Cette réponse est exécutée immédiatement par le navigateur, permettant ainsi de contourner les restrictions cross-origin sans nécessiter de configurations spéciales côté serveur comme avec CORS.
De ce fait du coté client, nous pouvons mettre en place la gestion dynamique de l’inclusion du script qui va interroger un second serveur
function envoyerRequeteJsonp() {
var scriptDynamique = document.createElement('script');
// Définir les paramètres à envoyer
var methodeRetour= 'traiterReponse';
var param1 = 'value1';
var param2 = 'value2';
// Construire l'URL avec les paramètres
scriptDynamique.src = `https://serveur.com/reponse.php?callback=${methodeRetour}¶m1=${param1}¶m2=${param2}`;
// Ajouter le script au document pour envoyer la requête
document.body.appendChild(scriptDynamique);
}
// Envoyer la requête JSONP lorsque la page se charge
window.onload = envoyerRequeteJsonp;
Du coté second serveur, la récupération des paramètres et l’envoi de la réponse au niveau du fichier reponse.php utilise le format JSON pour formuler sa réponse.
<?php
header('Content-Type: application/javascript');
// Récupérer le nom du callback
$gestionCallback = $_GET['callback'];
// le paramètre transmis <strong>callback </strong>peut être nommé différemment
// Récupérer les paramètres supplémentaires
$param1 = isset($_GET['param1']) ? $_GET['param1'] : 'default1';
$param2 = isset($_GET['param2']) ? $_GET['param2'] : 'default2';
// Créer les données de réponse en fonction des paramètres reçus
$data = array(
'message' => 'Voici une réponse employant JSONP!',
'param1_recu' => $param1,
'param2_recu' => $param2
);
// Retourner la réponse JSONP avec le callback
echo $gestionCallback . '(' . json_encode($data) . ');';
?>
Enfin coté client une fonction de récupération doit être mise en place
function traiterReponse(data) {
console.log("Données reçues :", data);
document.body.innerHTML += `<p>Message : ${data.message}</p>`;
document.body.innerHTML += `<p>Paramètre 1 reçu : ${data.param1_recu}</p>`;
document.body.innerHTML += `<p>Paramètre 2 reçu : ${data.param2_recu}</p>`;
}
Cette technique est particulièrement utile pour charger des données depuis un autre domaine sans nécessiter de configuration CORS. Cependant, il est important de noter que JSONP ne fonctionne qu’avec des requêtes GET, ce qui signifie qu’il ne peut pas être utilisé pour envoyer des données via POST.
2 – Utilisation de JSONP avec jQuery
À l’instar des approches en JavaScript pur, jQuery offre une alternative simple pour gérer les requêtes JSONP en utilisant sa méthode $.ajax()
et en la paramétrant en fonction.
function envoyerRequeteJsonp() {
$.ajax({
url: 'https://your-external-server.com/your_php_script.php', // Remplacez par l'URL de votre serveur
dataType: 'jsonp', // Indiquez que vous attendez une réponse JSONP
data: {
// Vous pouvez ajouter des paramètres supplémentaires ici
param1: 'valeur1',
param2: 'valeur2'
},
jsonp: 'gestionCallback', // Nom du paramètre pour le callback JSONP
success: traiterReponse, // Fonction de rappel pour traiter la réponse
error: function(jqXHR, textStatus, errorThrown) {
console.error('Erreur lors de la requête JSONP:', textStatus, errorThrown);
}
});
}
En utilisant jQuery pour les requêtes JSONP, vous pouvez simplifier la gestion des appels cross-origin tout en bénéficiant de l’abstraction et de la simplicité offertes par la bibliothèque.
3 – Mise en Place de CORS avec Headers Access-Control-Allow-Origin
Contrairement à JSONP, qui a été largement utilisé pour contourner les restrictions imposées par la politique de même origine des navigateurs, les mécanismes modernes comme CORS (Cross-Origin Resource Sharing) offrent une approche plus sécurisée et flexible pour les appels API cross-origin. CORS permet aux serveurs de spécifier exactement quelles origines sont autorisées à accéder à leurs ressources en utilisant les en-têtes HTTP appropriés. Pour configurer CORS, vous devez inclure les en-têtes nécessaires dans les réponses du serveur, comme Access-Control-Allow-Origin
. Par exemple, pour permettre l’accès à partir de tous les domaines, vous pouvez utiliser :
header("Access-Control-Allow-Origin: *");
Pour une approche plus sécurisée, il est recommandé de spécifier uniquement les domaines de confiance, comme :
Access-Control-Allow-Origin: https://mon-app.example.com
En plus de Access-Control-Allow-Origin
, vous pouvez configurer d’autres aspects de CORS, tels que les méthodes HTTP autorisées (Access-Control-Allow-Methods
) et les en-têtes personnalisés (Access-Control-Allow-Headers
), pour un contrôle plus précis et sécurisé des accès à vos ressources. N’hésitez pas à consulter En-têtes HTTP sur MDN
header("Access-Control-Allow-Origin: https://example.com");
// Indique les méthodes HTTP autorisées
header("Access-Control-Allow-Methods: GET, POST");
// Indique les en-têtes personnalisés autorisés
header("Access-Control-Allow-Headers: Content-Type");
// Spécifie le type de contenu de la réponse
header('Content-Type: application/json');
4 – Configuration des Headers CORS avec PHP
Pour les serveurs utilisant PHP, vous pouvez configurer les headers CORS directement dans vos scripts PHP
<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json");
// Exemple de données JSON
$data = array("nom" => "Jean Dupont", "email" => "jean.dupont@example.com");
echo json_encode($data);
?>
Ce code PHP configure les headers CORS et renvoie des données JSON, ce qui permet à l’application web de faire des requêtes AJAX et de recevoir les données sans rencontrer de problèmes de politique de même origine.
<?php
// Spécifiez l'origine autorisée (peut être un domaine spécifique ou * pour tous les domaines)
header("Access-Control-Allow-Origin: https://mon-app.example.com");
// Spécifiez les méthodes HTTP autorisées (GET, POST, PUT, DELETE, etc.)
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
// Spécifiez les en-têtes personnalisés autorisés dans la requête
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
// Indique si les réponses peuvent être mises en cache et pendant combien de temps
header("Access-Control-Max-Age: 3600"); // en secondes
// Réponse à une requête OPTIONS (preflight) pour vérifier les en-têtes et les méthodes
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
// Ajoutez d'autres en-têtes si nécessaire pour les preflights*
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
exit;
}
// Le reste de votre code pour gérer les requêtes GET, POST, etc.
$data = array('message' => 'CORS configuration complete.');
echo json_encode($data);
?>
Les requêtes preflight sont un mécanisme essentiel de CORS (Cross-Origin Resource Sharing) qui permet aux navigateurs de vérifier les conditions de sécurité avant d’effectuer des requêtes HTTP cross-origin plus complexes. Pour une explication détaillée sur les requêtes preflight et leur fonctionnement, consultez la documentation MDN sur CORS.
En gros, lorsqu’une application web essaie d’envoyer une requête avec une méthode autre que GET ou POST, ou avec des en-têtes personnalisés, le navigateur envoie d’abord une requête de type OPTIONS pour s’assurer que le serveur accepte ces paramètres. Cette étape de validation prévient les risques de sécurité en confirmant que le serveur est prêt à gérer la requête principale dans les conditions spécifiées.
5 – Configuration des Headers CORS via .htaccess
Pour configurer les en-têtes CORS sur un serveur utilisant Apache, vous pouvez utiliser le fichier .htaccess
, qui permet de définir des règles de configuration spécifiques au niveau du répertoire. Cette méthode est particulièrement utile pour gérer les permissions cross-origin directement depuis votre environnement d’hébergement sans toucher aux fichiers de configuration du serveur principal.
Pour configurer les en-têtes CORS dans un fichier .htaccess
, vous pouvez définir les règles suivantes pour spécifier quels domaines sont autorisés à accéder à vos ressources. Cette méthode permet de mieux contrôler les autorisations tout en évitant les risques associés à l’utilisation d’un astérisque (*
), qui permet à n’importe quel domaine d’accéder à vos ressources.
# Autoriser les requêtes depuis des domaines spécifiques
# Remplacez les domaines ci-dessous par ceux qui doivent être autorisés
Header set Access-Control-Allow-Origin "https://example.com"
Header set Access-Control-Allow-Origin "https://another-example.com"
# Il est possible d'utiliser plusieurs en-têtes Access-Control-Allow-Origin
# en ajoutant des directives Header supplémentaires pour chaque domaine,
# mais chaque en-tête sera limité à un seul domaine.
# Autoriser les méthodes HTTP spécifiques
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE"
# Autoriser les en-têtes personnalisés
Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
# Gérer les requêtes preflight
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>
Imaginons que vous développez une API que vous souhaitez rendre accessible uniquement à certaines applications front-end spécifiques. En configurant les en-têtes CORS comme montré ci-dessus, vous autorisez uniquement les domaines https://example.com
et https://another-example.com
à accéder à votre API. Cela offre un contrôle plus strict et améliore la sécurité par rapport à l’utilisation de *
, qui permettrait à tout domaine de faire des requêtes vers votre API.
L’utilisation du caractère générique *
dans Access-Control-Allow-Origin
est trop permissive car elle permet à n’importe quel site web de faire des requêtes vers votre serveur, ce qui peut poser des risques de sécurité, notamment en termes d’exposition des données et d’attaques potentielles. En spécifiant les domaines exacts, vous limitez les risques en vous assurant que seules les applications autorisées peuvent interagir avec vos ressources.
Pour une documentation plus approfondie sur les configurations CORS et leur mise en œuvre, consultez la documentation officielle sur MDN. Ce guide vous aide à comprendre comment utiliser .htaccess
pour gérer les configurations CORS de manière sécurisée et appropriée.
Conclusion
En résumé, JSONP et CORS sont deux mécanismes permettant de gérer les requêtes cross-origin, mais chacun a ses propres avantages et limitations. JSONP, bien qu’utile pour contourner les restrictions de même origine avant l’émergence de CORS, présente des faiblesses en matière de sécurité et est aujourd’hui largement remplacé par CORS. Ce dernier offre une solution plus moderne et sécurisée en utilisant des en-têtes HTTP pour contrôler précisément quels domaines, méthodes et en-têtes sont autorisés à accéder aux ressources d’un serveur.
En mettant en place CORS avec des en-têtes appropriés, comme Access-Control-Allow-Origin
, Access-Control-Allow-Methods
, et Access-Control-Allow-Headers
, vous pouvez gérer les interactions entre différents domaines tout en maintenant un contrôle rigoureux sur les accès à vos ressources. Cette approche non seulement améliore la sécurité de vos applications web, mais permet également une meilleure flexibilité pour les intégrations avec des services externes. Pour une mise en œuvre complète, il est recommandé de consulter la documentation MDN sur CORS pour une compréhension approfondie et des exemples pratiques.