Join MDN and developers like you at Mozilla's View Source conference, 12-14 September in Berlin, Germany. Learn more at https://viewsourceconf.org

Contrôle d'accès HTTP

Les requêtes HTTP de type Cross-site sont des requêtes pour des ressources localisées sur un domaine différent de celui à l'origine de la requête. Par exemple, une ressource chargée depuis le domaine A (http://domaina.example) comme une page HTML, effectue une requête pour une ressource provenant du domaine B (http://domainb.foo), comme une image, en utilisant la balise img (http://domainb.foo/image.jpg).  C'est régulièrement le cas aujourd'hui sur le web — des pages chargent un nombre de ressources à la manière Cross-site, en ce inclus des feuilles de style (CSS), des images, des scripts, et d'autres ressources.

Les requêtes HTTP croisées initiées au départ de scripts ont été sujettes à des restrictions bien connues et pour des raisons de sécurité bien compréhensibles. Par exemple les requêtes HTTP effectuées par l'objet XMLHttpRequest sont soumises à la politique de même origine.  En particulier, cela signifiait qu'une application web utilisant XMLHttpRequest ne pouvait que créer des requêtes HTTP vers le domaine d'où elle avait été chargée et non vers d'autres domaines. Les développeurs ont exprimé le désir de voir évoluer en toute sécurité les possibilités comme celles du XMLHttpRequest afin d'effectuer des requêtes croisées (cross-site), pour de meilleures et plus sûres compositions au sein d'applications web.

Le Web Applications Working Group au sein du W3C a recommandé le nouveau mécanisme de Cross-Origin Resource Sharing qui fournit un moyen aux serveurs web de contrôler les accès en mode cross-site et aussi d'effectuer des transferts de données sécurisés en ce mode. Il est particulièrement intéressant de noter que cette spécification est utilisée au sein d'un API container comme le XMLHttpRequest et ce en tant que mécanisme de mitigation, permettant de passer outre la restriction des navigateurs modernes de fonctionner sur base d'une politique "same-domain".  L'information contenue dans cet article est intéressante pour les administrateurs web, les programmeurs de serveurs et les développeurs web.  Un autre article pour les programmeurs serveurs discutant le partage des origines croisées sous l'angle du serveur (avec des extraits de code PHP) constitue une lecture supplémentaire fructueuse.  Sur l'environnement client, le navigateur s'occupe de partager l'origine des ressources, incluant les entêtes et la mise en application de la politique générale en la matière.  En revanche, l'introduction de cette nouvelle possibilité implique nécessairement que les serveurs doivent gérer de nouvelles entêtes, et doivent renvoyer les ressources avec de nouvelles entêtes également.

Ce  standard de partage d'origines croisées est utilisé pour permettre les requêtes HTTP en mode cross-site pour:

Cet article est une discussion générale du partage des ressources d'origines croisées et inclut une discussion du traitement des entêtes HTTP en Firefox 3.5. 

Vue générale

Le standard de partage de ressources d'origines croisées fonctionne grâce à l'ajout d'entêtes HTTP qui permettent aux serveurs de décrire l'ensemble des origines permises. C'est ensuite le navigateur qui lit cette information et en fait l'usage adéquat. Par ailleurs, pour les requêtes HTTP dont les méthodes pourraient avoir des effets secondaires sur les données utilisateur (en particulier pour les méthodes HTTP autres que GET, ou pour l'utilisation du POST avec certains types MIME), la spécification mandate les navigateurs pour "pré-vérifier" la requête en sollicitant le serveur pour connaître les méthodes approuvées. Cette pré-vérification s'effectue avec la méthode HTTP OPTIONS, et ensuite, après "approbation" du serveur, envoit la requête véritable. Les serveurs peuvent aussi notifier les clients de ce que les permissions (incluant les cookies et les données d'authentification HTTP) doivent être envoyées avec les requêtes.

Les sections suivantes discutent des scenarios, autant que de la décomposition des entêtes utilisés. 

Exemples de scenarios de contrôles d'accès

Ici, nous présentons 3 scenarios qui illustrent le fonctionnement du partage de ressources d'origines croisées. Tous ces exemples font usage de l'objet XMLHttpRequest, qui peut être utilisé pour invoquer du cross-site dans n'importe quel navigateur qui le supporte.

Les fragments de JavaScript qui sont inclus dans ces sections (et les occurrences tournant du côté du serveur qui gèrent ces demandes d'origines croisées) peuvent  peuvent être consultées "en action" ici, et fonctionneront dans les navigateurs qui supportent les origines croisées via l'objet XMLHttpRequest.  Une discussion du partage de ressources croisées dans la perspective serveur (en ce inclus les extraits PHP) peut être consulté ici.

Requêtes simples

Une requête cross-site simple est une requête qui:

  • N'utilise que les options GET, HEAD ou POST. Si POST est utlisé pour envoyer des données au serveur le Content-Type des données envoyé au serveur est soit application/x-www-form-urlencoded, multipart/form-data, ou text/plain.
  • Ne positionne pas d'entêtes personnalisés avec la requête HTTP Request (comme par exemple X-Modified, etc.)
Note: Ce sont les mêmes types de requêtes cross-site que du contenu web peuvent déjà lancer, et aucune donnée de réponse n'est soumise à l'appelant sauf si le serveur envoit un entête approprié. Ainsi donc, les sites qui empêchent la falsification de requête inter-site ne doivent rien craindre du contrôle d'accès HTTP.

Par exemple, supposez que le contenu web sur le domaine http://foo.example souhaite invoquer du contenu sur le domaine http://bar.other.  Un code de ce type pourrait être utilisé dans du JavaScript déployé sur foo.example:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
   
function callOtherDomain() {
  if(invocation) {    
    invocation.open('GET', url, true);
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }
}

Voyons à présent ce que le navigateur va envoyer au serveur dans ce cas, et voyons ce que le serveur répond:

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[XML Data]

 Lines 1 - 10 sont les entêtes envoyés par Firefox 3.5.  Notez que l'entête principal ici est l'entête Origin: entête à la ligne 10 ici au-dessus, qui montre que l'invocation vient d'un contenu provenant du domaine http://foo.example.

Lines 13 - 22 montrent que la réponse HTTP du serveur sur le domaine http://bar.other.  En réponse, le server renvoie une entête Access-Control-Allow-Origin:, montrée ci-dessus en ligne 16.  L'utilisation de l'entête Origin: et de Access-Control-Allow-Origin: montrent l'accès au protocle de contrôle dans sa forme la plus simple.  Dans ce cas, le serveur répond avec un Access-Control-Allow-Origin: * qui signifie que la ressource peut être accédée de n'importe quel domaine de manière croisée (cross-site).  Si les propriétaires de la ressource sur http://bar.other souhaitaient restreindre l'accès à la ressource au seul domaine http://foo.example, ils enverraient :

Access-Control-Allow-Origin: http://foo.example

Notez que maintenant, aucun domaine autre que http://foo.example (identifié par l'entête ORIGIN: dans la requête, comme à la ligne 10 ci-dessus) ne peut accéder la ressource d'une manière croisée (cross-site).  L'entête Access-Control-Allow-Origin devrait contenir la valeur qui a été envoyée dans l'entête Origin de la requête. 

Requêtes pré-vérifiées

Contrairement aux requêtes simples (discutées ci-avant), les requêtes prévérifiées ("preflighted requests", stricto sensu, les requêtes avant décollage) envoient d'abord une requête HTTP avec l'entête OPTIONS dans la ressource souhaitée de l'autre domaine et cela en vue de déterminer si la véritable requête est sûre à envoyer. Les requêtes croisées entre site sont prévérifiées de cette manière parce qu'elles peuvent avoir des implications sur les données utilisateur.  En particulier, une requête est prévérifiée si :

  • elle utilise des méthodes autres que GET, HEAD ou POST.  Aussi, si POST est utilisée pour envoyer des reqûetes de données avec un Content-Type autre que application/x-www-form-urlencoded, multipart/form-data, ou text/plain, par exemple si la requête POST envoie au serveur un contenu utile XML en utilisant application/xml ou text/xml, alors la requête est pré-vérifiée.
  • elle positionne des entêtes propres (ex: la requête utilise une entête comme X-PINGOTHER)

Gecko 2.0 note
(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)

À partir de Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1), les encodages de données text/plain, application/x-www-form-urlencoded et multipart/form-data peuvent aussi être envoyés en mode croisé (cross-site) sans prévérification. Avant cela, seul text/plain pouvait être envoyé sans cette prévérification.

Un exemple de ce type d'invocation pourrait être:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
    
function callOtherDomain(){
  if(invocation)
    {
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
      invocation.setRequestHeader('Content-Type', 'application/xml');
      invocation.onreadystatechange = handler;
      invocation.send(body); 
    }

......

Dans l'exemple ci-dessus, la ligne 3 crée un corps XML à envoyer avec une requête de type POST (ligne 8).  Aussi, à la ligne 9, un entête "personnalisé" (c'est-à-dire non-standard) est positionné dans la requête HTTP (X-PINGOTHER: pingpong).  De tels entêtes ne sont pas spécifiés dans la documentation du protocole HTTP/1.1, mais sont en général bien utiles à des applications web.  Puisque la requête (POST) utilise un Content-Type application/xml, et puisque un entête personnalisé est employé, cette requête est prévérifiée.

Jetons un oeil aux échanges complets entre le client et le serveur:

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

Lignes 1 - 12 ci-dessus: représentent la requête de pré-vérification avec l'entête OPTIONS.  Firefox 3.1 détermine s'il a besoin d'envoyer ceci sur base des paramètres de la requête que l'extrait de JavaScript avait utilisés, de telle sorte que le serveur puisse répondre s'il est acceptable d'envoyer la requête avec de tels paramètres.  OPTIONS est une méthode HTTP/1.1 qui est utilisée pour déterminer de futures informations des serveurs, et est une méthode idempotente, ce qui signifie qu'elle ne peut être utilisée pour modifier la ressource.  Notez qu'en sus, deux autres entêtes sont envoyés (lignes 11 et 12 respectivement):

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER

L'entête Access-Control-Request-Method notifie le serveur, comme élément de la requête de pré-vérification, que lorsque la véritable requête est envoyée, elle sera sera envoyée avec la méthode POST. L'entête Access-Control-Request-Headers notifie le serveur que lorsque la véritable requête est envoyée  elle le sera avec une entête X-PINGOTHER personnalisée.  Le serveur a maintenant l'opportunité de déterminer s'il souhaite accepter une requête dans ses circonstances.

Lignes 15 - 27 ci-dessus sont la réponse que le serveur renvoit indiquant que la méthode de la requête (POST) et les entêtes de la requête (X-PINGOTHER) sont acceptables.  En particulier, voyons les lignes 18-21:

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000

Le serveur répond avec Access-Control-Allow-Methods et indique que les  méthodes POST, GET, et OPTIONS sont des méthodes acceptables pour aller quérir la ressource en question.  Notez que cet entête est similaire à l'entête de réponse HTTP/1.1 Allow, mais qu'il est utilisé de manière stricte dans le contexte du contrôle d'accès.  Le serveur envoie aussi Access-Control-Allow-Headers avec une valeur de X-PINGOTHER, confirmant en cela que c'est un entête permis dans le cadre de la requête effective.  Tout comme Access-Control-Allow-Methods, Access-Control-Allow-Headers est une liste séparée par des virgules d'entêtes acceptés.  Enfin, Access-Control-Max-Age indique la durée en secondes pendant laquelle la requête de pré-vérification peut être mise en cache sans qu'il soit nécessaire d'en envoyer une autre. Ici, 1728000 secondes est 20 jours.

Requêtes avec habilitations

La possibilité la plus intéressante exposée par l'objet XMLHttpRequest et par le contrôle d'accès (Access Control) est de créer des requêtes habilitées qui ont connaissance des cookies HTTP et des informations d'authentification en HTTP.  Par défaut, dans les invocations XMLHttpRequest croisées (cross-site) les navigateurs ne vont pas envoyerles habilitations.  Un tag spécifique doit être positionné dans l'objet XMLHttpRequest quand il est invoqué.

Dans cet exemple, le contenu chargé originellement de http://foo.example effecture une simple requête GET vers une ressource de http://bar.other qui positionne les cookies.  Le contenu dur foo.example peut contenir du JavaScript similaire à :

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
    
function callOtherDomain(){
  if(invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send(); 
  }

La ligne 7 montre la propriété de XMLHttpRequest qui doit être positionnée pour rendre effective l'invocation avec les Cookies, pour ne pas la nommer la valeur logique withCredentials.  Par défaut, l'invocation est faite sans les Cookies.  Comme il s'agit ici d'un requête GET simple, elle n'est pas pré-vérifiée, mais le navigateur rejettera toute réponse qui n'a pas d'entête Access-Control-Allow-Credentials: true, et NE RENDRA PAS disponible la réponse au contenu web qui l'a demandé.

Voyez ici un échantillon de l'change entre le client et le serveur:

GET /resources/access-control-with-credentials/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: pageAccess=2


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2.0.61 (Unix) PHP/4.4.7 mod_ssl/2.0.61 OpenSSL/0.9.7e mod_fastcgi/2.4.2 DAV/2 SVN/1.4.2
X-Powered-By: PHP/5.2.6
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain


[text/plain payload]

Bien que la ligne 12 contienne le cookie destiné au contenu sur http://bar.other, si bar.other ne répondait pas avec un Access-Control-Allow-Credentials: true (ligne 20) la réponse serait ignorée et rendue indisponible.  Note importante: quand on répond à une requête avec informations de compte, le serveur doit spécifier un domaine, et ne peut pas utiliser de jokers (wildcards).  L'exemple ci-dessus échouerait si l'entête utilisait des jokers comme dans: Access-Control-Allow-Origin: *.  Puisque Access-Control-Allow-Origin mentionne explicitement http://foo.example, le contenu lié aux aspects de compte (credentials) est retourné au contenu web qui l'a demandé.  Notez qu'à la ligne 23, un autre cookie est positionné.

Tous ces exemples peuvet être consultés en fonctionnement ici.  La prochaine section traite des entêtes HTTP de manière plus précise.

Les entêtes HTTP de réponse

This section lists the HTTP response headers that servers send back for access control requests as defined by the Cross-Origin Resource Sharing specification.  The previous section gives an overview of these in action.

Access-Control-Allow-Origin

A returned resource may have one Access-Control-Allow-Origin header, with the following syntax:

Access-Control-Allow-Origin: <origin> | *

The origin parameter specifies a URI that may access the resource.  The browser must enforce this.  For requests without credentials, the server may specify "*" as a wildcard, thereby allowing any origin to access the resource.

For example, to allow http://mozilla.com to access the resource, you can specify:

Access-Control-Allow-Origin: http://mozilla.com

Access-Control-Expose-Headers

(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)

This header lets a server whitelist headers that browsers are allowed to access. For example:

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

This allows the X-My-Custom-Header and X-Another-Custom-Header headers to be exposed to the browser.

Access-Control-Max-Age

This header indicates how long the results of a preflight request can be cached.  For an example of a preflight request, see the above examples.

Access-Control-Max-Age: <delta-seconds>

The delta-seconds parameter indicates the number of seconds the results can be cached.

Access-Control-Allow-Credentials

Indicates whether or not the response to the request can be exposed when the credentials flag is true.  When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials.  Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials, if this header is not returned with the resource, the response is ignored by the browser and not returned to web content.

Access-Control-Allow-Credentials: true | false

Credentialed requests are discussed above.

Access-Control-Allow-Methods

Specifies the method or methods allowed when accessing the resource.  This is used in response to a preflight request.  The conditions under which a request is preflighted are discussed above.

Access-Control-Allow-Methods: <method>[, <method>]*

An example of a preflight request is given above, including an example which sends this header to the browser.

Access-Control-Allow-Headers

Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.

Access-Control-Allow-Headers: <field-name>[, <field-name>]*

Les entêtes HTTP de la requête

This section lists headers that clients may use when issuing HTTP requests in order to make use of the cross-origin sharing feature.  Note that these headers are set for you when making invocations to servers.  Developers using cross-site XMLHttpRequest capability do not have to set any cross-origing sharing request headers programmatically.

Origine

Indicates the origin of the cross-site access request or preflight request.

Origin: <origin>

The origin is a URI indicating the server from which the request initiated.  It does not include any path information, but only the server name.

Note: The origin can be the empty string; this is useful, for example, if the source is a data URL.

Note that in any access control request, the ORIGIN header is always sent.

Access-Control-Request-Method

Used when issuing a preflight request to let the server know what HTTP method will be used when the actual request is made.

Access-Control-Request-Method: <method>

Examples of this usage can be found above.

Access-Control-Request-Headers

Used when issuing a preflight request to let the server know what HTTP headers will be used when the actual request is made.

Access-Control-Request-Headers: <field-name>[, <field-name>]*

Examples of this usage can be found above.

Compatibilité navigateurs

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support 4 3.5 8 (via XDomainRequest)
10
12 4
Feature Android Chrome Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Basic support 2.1 yes yes ? 12 3.2

Note

Internet Explorer 8 and 9 expose CORS via the XDomainRequest object, but have a full implementation in IE 10.  While Firefox 3.5 introduced support for cross-site XMLHttpRequests and Web Fonts, certain requests were limited until later versions. Specifically, Firefox 7 introduced the ability for cross-site HTTP requests for WebGL Textures, and Firefox 9 added support for Images drawn on a canvas using drawImage.

Voir aussi

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : nlaug, cguillemette, Zzortell, fmasy, patboens
 Dernière mise à jour par : nlaug,