Pour un développeur expérimenté, ce texte peut ne pas être surprenant du tout, mais je pense que beaucoup d'articles que j'ai lus sur la configuration CORS dans Rails disaient quelque chose comme : utilisez rack-cors, autorisez n'importe quel hôte à accéder à l'API, et (optionnellement) : vous devriez envisager quelque chose de différent (que d'autoriser n'importe quel hôte) en production.
La proposition de code était toujours proche de celui qui se trouvait en dessous :
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origines ''
resource '', headers : :any, methods : :any
end
end
et, malheureusement, ces textes ne nous expliquaient guère ce qu'il fallait faire concrètement dans la production.
Je suis assez à l'aise avec le copier-coller (Je plaisante parfois sur le fait que les entreprises pourraient embaucher un copiste de Stack Overflow.), dans la mesure où il y a un moment de "réflexion et d'ajustement" entre "copier" et "coller". J'aimerais donc expliquer un peu plus en détail ce que nous faisons ici et comment cela fonctionne dans la vie réelle.
J'espère que vous ne m'en voudrez pas de commencer par une brève introduction à la théorie de l'honneur et de passer ensuite aux exemples Rails.
Introduction
Commençons par le début. Pour mieux expliquer les choses, j'ai divisé l'introduction en trois parties. La première partie explique ce qu'est une origine - le terme clé pour ce dont nous discutons ici. La deuxième partie est consacrée aux procédures opérationnelles normalisées (POS), dont elle donne une brève description. Enfin, la dernière partie traite de la CORS
même.
Qu'est-ce qu'une origine ?
Selon les documents Web du MDN :
- L'origine d'un contenu Web est définie par le schéma (protocole), l'hôte (domaine) et le port de l'URL utilisée pour y accéder. Deux objets n'ont la même origine que si le schéma, l'hôte et le port correspondent (source)
Cela semble assez clair, n'est-ce pas ? Analysons deux exemples tirés de MDN, juste au cas où.
http://example.com/app1/index.html
, http://example.com/app2/index.html
Les 2 ci-dessus ont la même origine car :
- leurs schémas (http) sont les mêmes,
- leurs domaines (exemple.com) sont identiques,
- leurs ports (implicites) sont les mêmes.
http://www.example.com
, http://myapp.example.com
Ces deux éléments ont une origine différente car les domaines (www.example.com
, myapp.example.com
) sont différents.
J'espère que c'est suffisamment clair. Si ce n'est pas le cas, veuillez consulter les MDN Web Docs pour plus d'exemples.
Qu'est-ce que le SOP ?
MDN Web Docs dit (source):
- La politique d'origine identique est un mécanisme de sécurité essentiel qui limite la manière dont un document ou un script chargé à partir d'une origine peut interagir avec une ressource d'une autre origine. Elle permet d'isoler les documents potentiellement malveillants et de réduire les vecteurs d'attaque possibles.
- Les écritures d'origine croisée sont généralement autorisées. Les exemples sont les liens, les redirections et les soumissions de formulaires.
- L'intégration inter-origine est généralement autorisée.
- Les lectures inter-origines sont généralement interdites, mais l'accès à la lecture est souvent dissimulé par l'intégration.
Utilisation CORS
pour permettre l'accès inter-origine
Comme vous pouvez le constater, les définitions du SOP font la part belle aux comportements inter-origines. Ce n'est pas grave. Tout ce que nous devons savoir maintenant, c'est que la même origine a plus de privilèges et que nous pouvons assouplir les règles pour les origines croisées en utilisant CORS. C'est là qu'intervient la section suivante.
Qu'est-ce que CORS ?
En se basant sur les propos de MDN :
- Le partage de ressources inter-origines (CORS) est un mécanisme basé sur l'en-tête HTTP qui permet à un serveur d'indiquer toute autre origine (domaine, schéma ou port) que la sienne à partir de laquelle un navigateur devrait autoriser le chargement de ressources. CORS repose également sur un mécanisme par lequel les navigateurs effectuent une demande de "contrôle préalable" au serveur hébergeant la ressource d'origine croisée, afin de vérifier que le serveur autorisera la demande réelle. Dans ce contrôle préalable, le navigateur envoie des en-têtes qui indiquent la méthode HTTP et les en-têtes qui seront utilisés dans la requête réelle (source).
Ce n'est toujours pas suffisant. Ce qui n'a pas été dit explicitement, c'est que l'en-tête le plus important lors de l'utilisation de CORS
est Contrôle d'accès - Autoriser l'origine
:
- Les
Contrôle d'accès - Autoriser l'origine
indique si la réponse peut être partagée avec le code demandeur de l'origine donnée (source).
Voilà qui devrait être fait. Dans la réalité, lors de la configuration de CORS
En règle générale, nous configurons le ACAO
en premier lieu.
La vraie vie
Voilà pour les définitions. Revenons à Rails et aux exemples concrets.
Comment configurer CORS dans Rails ?
Nous utiliserons certainement des cors à crémaillère (comme on nous l'a dit). Rappelons le premier extrait, celui qui est le plus souvent fourni dans d'autres articles :
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origines ''
resource '', headers : :any, methods : :any
end
end
Le nombre d'options est vaste, voire infini, mais considérons ces deux-là :
- nous construisons l'API qui peut être utilisée par les clients tiers du navigateur,
- nous avons une séparation typique entre le frontend et le backend et nous voulons permettre à nos clients de confiance d'accéder à l'API.
Construction d'une API accessible à des clients tiers
Si vous êtes confronté à la première option, vous pouvez probablement opter pour les origines
* - vous voulez que d'autres personnes construisent un client à partir de votre API, et vous ne savez pas qui ils sont, n'est-ce pas ?
Séparation typique entre le frontend et le backend
Si vous développez ce dernier, vous ne voulez probablement pas que tout le monde fasse des requêtes inter-origines à votre API. Vous voulez plutôt :
- permettre aux clients de production d'accéder à l'API de production,
- Il en va de même pour la mise en scène,
- même chose pour localhost,
- vous voudrez peut-être autoriser les applications de révision de FE à accéder à la phase de mise à l'essai.
Nous continuerons à utiliser des cors à crémaillère (comme on nous l'a dit), mais à notre manière.
Utilisons 2 variables ENV : ORIGINES_AUTORISÉES
pour les définitions littérales de l'origine (un astérisque ou l'URL réel) et AUTORISÉ_ORIGINE_REGEXPS
pour les modèles.
config/initializers/cors.rb
frozenstringliteral : true
toregexp = ->(string) { Regexp.new(string) }
hosts = [
*ENV.fetch('ALLOWEDORIGINS').split(','),
*ENV.fetch('ALLOWEDORIGINREGEXPS').split(';').map(&to_regexp)
]
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins(*hosts)
ressource '*',
methods : %i[get post put patch delete options head],
headers : :any
end
end
Que se passe-t-il ici ?
- Comme vous pouvez le constater, nous séparons les valeurs définies dans les variables ENV par des séparateurs différents. C'est parce qu'un point-virgule a moins de chances d'apparaître dans le modèle de définition de l'URL.
- Les valeurs littérales sont prêtes à être utilisées, mais nous devons faire correspondre les motifs à des instances de Regexp réelles.
- Ensuite, nous réunissons le tout et permettons à ces hôtes d'accéder à n'importe quelle ressource à l'aide des méthodes de la liste blanche que notre API utilise.
Cela devrait vous donner suffisamment de souplesse pour définir les valeurs appropriées dans vos environnements de développement, de simulation et de production.
Conclusions
Résumons tout ce qui précède en quelques points clés :
- utiliser les variables ENV pour configurer
CORS
,
- utiliser des expressions régulières pour permettre à différentes origines d'accéder à l'API de mise à disposition (par exemple, pour les applications de révision),
- toujours mettre "réfléchir et ajuster" entre "copier" et "coller".
Voilà, c'est fait. Bonne journée à tous 🙂 .
En savoir plus :
Pourquoi devriez-vous (probablement) utiliser Typescript ?
10 startups de NYC à mentionner en 2021