Application Security

Maîtriser CORS : Le guide de configuration définitif pour des API web sécurisées

Dans l'écosystème moderne des microservices et des applications monopage (SPA), le partage de ressources entre origines (CORS) est devenu l'un des composants les plus mal compris mais essentiels de la sécurité web. Si vous avez déjà rencontré un message d'erreur déroutant dans la console de votre navigateur indiquant « L'accès à fetch à... depuis l'origine... a été bloqué par la politique CORS », vous n'êtes pas seul. Ce guide vise à démystifier CORS, à expliquer les mécanismes de sécurité sous-jacents et à fournir des stratégies de configuration robustes pour les développeurs.

Comprendre la politique de même origine

Pour comprendre pourquoi CORS existe, nous devons d'abord examiner la politique de même origine (SOP). Les navigateurs appliquent la SOP pour isoler les documents potentiellement malveillants. Deux origines sont considérées comme identiques si le protocole, l'hôte (domaine) et le port sont identiques. La SOP empêche un script chargé depuis https://app.example.com de lire des données provenant de https://api.example.com, sauf autorisation explicite.

CORS n'est pas une fonctionnalité de sécurité en soi ; c'est un mécanisme qui permet aux serveurs web d'assouplir la SOP. Il offre un moyen contrôlé pour les serveurs d'autoriser des requêtes inter-origines spécifiques, garantissant que seuls les clients de confiance peuvent accéder aux données sensibles.

Anatomie d'une requête CORS

Lorsqu'un navigateur effectue une requête inter-origine, il agit comme un gardien. Il inspecte les en-têtes de réponse du serveur pour déterminer si la requête doit être autorisée. Il existe deux types de requêtes : simples et préliminaires (preflight).

Les requêtes simples ne nécessitent aucune communication préalable. Il s'agit de requêtes GET ou POST avec des types de contenu standard (comme application/x-www-form-urlencoded ou multipart/form-data). Le navigateur envoie la requête avec un en-tête Origin indiquant d'où provient la requête.

Les requêtes préliminaires (preflight) se produisent lorsque la méthode de requête est DELETE, PUT ou PATCH, ou lorsque des en-têtes personnalisés (comme X-Custom-Header) ou des types de contenu non standard sont utilisés. Dans ces cas, le navigateur envoie d'abord une requête OPTIONS pour demander au serveur : « Est-il acceptable que j'envoie cette requête réelle ? » Seulement si le serveur répond avec des en-têtes et méthodes autorisés spécifiques, le navigateur procède à la requête réelle.

Explication des en-têtes CORS clés

La configuration repose sur la définition correcte des en-têtes HTTP. Voici les plus critiques :

  • Access-Control-Allow-Origin : Spécifie quelles origines sont autorisées. L'utilisation de * permet toutes les origines, mais cela est souvent déconseillé pour les API authentifiées.
  • Access-Control-Allow-Methods : Liste les méthodes HTTP autorisées (par exemple, GET, POST, OPTIONS).
  • Access-Control-Allow-Headers : Spécifie quels en-têtes peuvent être utilisés dans la requête réelle.
  • Access-Control-Allow-Credentials : Si défini sur true, permet l'envoi de cookies et d'informations d'authentification avec la requête.

Exemples de configuration pratiques

Voici des exemples de configuration de CORS dans des frameworks backend populaires. Rappelez-vous que l'objectif est d'être aussi restrictif que possible, en n'autorisant que le strict nécessaire.

Node.js avec Express

L'utilisation du middleware cors est l'approche standard. Pour les API de production, évitez d'utiliser des jokers (*) si vous devez prendre en charge les informations d'identification.

const cors = require('cors');
const express = require('express');
const app = express();

// Autoriser uniquement des origines spécifiques
const corsOptions = {
  origin: ['https://frontend.example.com', 'http://localhost:3000'],
  optionsSuccessStatus: 200, // Certains navigateurs hérités rencontrent des problèmes avec le 204
  credentials: true // Permet l'envoi de cookies
};

app.use(cors(corsOptions));

Python avec Flask

L'extension flask-cors simplifie considérablement ce processus.

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
# Restreindre à un domaine spécifique
CORS(app, origins=["https://frontend.example.com"])

@app.route('/data')
def get_data():
    return {'message': 'Données sécurisées'}

Écueils courants et bonnes pratiques

L'une des erreurs les plus courantes consiste à activer Access-Control-Allow-Credentials: true tout en définissant simultanément Access-Control-Allow-Origin: *. Cette combinaison est invalide selon la spécification CORS. Lorsque des informations d'identification sont impliquées, vous devez spécifier l'origine exacte.

Un autre écueil est la mauvaise gestion des requêtes préliminaires. Assurez-vous que votre serveur renvoie un statut 204 No Content ou 200 OK pour les requêtes OPTIONS. Si la requête préliminaire échoue, la requête réelle ne sera jamais envoyée, ce qui entraîne des erreurs confuses.

Enfin, prenez toujours en compte votre posture de sécurité. Auditez régulièrement vos configurations CORS. Si vous avez un service qui n'a pas besoin d'accès inter-origine, désactivez CORS entièrement ou restreignez-le uniquement à localhost. La mise en œuvre d'une politique de sécurité du contenu (CSP) peut également fournir une couche de défense supplémentaire contre certains types d'attaques.

Conclusion

CORS est un aspect nuancé mais essentiel du développement web. En comprenant la différence entre les requêtes simples et les requêtes préliminaires, et en configurant soigneusement les origines, méthodes et en-têtes autorisés, les développeurs peuvent créer des API à la fois sécurisées et accessibles. Évitez la tentation d'utiliser des configurations larges avec des jokers en production. Adoptez plutôt une approche de liste blanche, en spécifiant exactement qui peut communiquer avec vos services. Avec la bonne configuration, vous pouvez garantir que votre application reste robuste contre les accès inter-origine non autorisés tout en maintenant une fonctionnalité transparente pour les utilisateurs légitimes.

Share: