7. Considérations de sécurité (Security Considerations)
Cette section décrit les domaines potentiels de préoccupation en matière de sécurité avec QPACK :
-
Utilisation de la compression comme un oracle basé sur la longueur pour vérifier les hypothèses sur les secrets qui sont compressés dans un contexte de compression partagé.
-
Déni de service résultant de l'épuisement de la capacité de traitement ou de mémoire au niveau d'un décodeur.
7.1 Sondage de l'état de la table dynamique (Probing Dynamic Table State)
QPACK réduit la taille encodée des sections de champ en exploitant la redondance inhérente aux protocoles comme HTTP. L'objectif ultime de ceci est de réduire la quantité de données qui est requise pour envoyer des requêtes ou des réponses HTTP.
Le contexte de compression utilisé pour encoder les champs d'en-tête et de fin peut être sondé par un attaquant qui peut à la fois définir les champs à encoder et transmettre et observer la longueur de ces champs une fois qu'ils sont encodés. Lorsqu'un attaquant peut faire les deux, il peut modifier de manière adaptative les requêtes afin de confirmer les hypothèses sur l'état de la table dynamique. Si une hypothèse est compressée en une longueur plus courte, l'attaquant peut observer la longueur encodée et déduire que l'hypothèse était correcte.
Cela est possible même sur le protocole Transport Layer Security ([TLS]) et le protocole de transport QUIC ([QUIC-TRANSPORT]), car bien que TLS et QUIC fournissent une protection de confidentialité pour le contenu, ils ne fournissent qu'une quantité limitée de protection pour la longueur de ce contenu.
Note : Les schémas de remplissage ne fournissent qu'une protection limitée contre un attaquant avec ces capacités, ne forçant potentiellement qu'un nombre accru d'hypothèses pour apprendre la longueur associée à une hypothèse donnée. Les schémas de remplissage fonctionnent également directement contre la compression en augmentant le nombre de bits qui sont transmis.
Des attaques comme CRIME ([CRIME]) ont démontré l'existence de ces capacités générales d'attaquant. L'attaque spécifique a exploité le fait que DEFLATE ([RFC1951]) supprime la redondance basée sur la correspondance de préfixe. Cela a permis à l'attaquant de confirmer les hypothèses un caractère à la fois, réduisant une attaque en temps exponentiel en une attaque en temps linéaire.
7.1.1 Applicabilité à QPACK et HTTP (Applicability to QPACK and HTTP)
QPACK atténue, mais n'empêche pas complètement, les attaques modelées sur CRIME ([CRIME]) en forçant une hypothèse à correspondre à une ligne de champ entière plutôt qu'à des caractères individuels. Un attaquant ne peut apprendre que si une hypothèse est correcte ou non, donc l'attaquant est réduit à une force brute pour deviner les valeurs de champ associées à un nom de champ donné.
Par conséquent, la viabilité de récupérer des valeurs de champ spécifiques dépend de l'entropie des valeurs. En conséquence, les valeurs avec une entropie élevée sont peu susceptibles d'être récupérées avec succès. Cependant, les valeurs avec une faible entropie restent vulnérables.
Les attaques de cette nature sont possibles chaque fois que deux entités mutuellement méfiantes contrôlent des requêtes ou des réponses qui sont placées sur une seule connexion HTTP/3. Si le compresseur QPACK partagé permet à une entité d'ajouter des entrées à la table dynamique, et à l'autre de faire référence à ces entrées lors de l'encodage de lignes de champ choisies, alors l'attaquant (la deuxième entité) peut apprendre l'état de la table en observant la longueur de la sortie encodée.
Par exemple, les requêtes ou réponses d'entités mutuellement méfiantes peuvent se produire lorsqu'un intermédiaire soit :
-
envoie des requêtes de plusieurs clients sur une seule connexion vers un serveur d'origine, ou
-
prend des réponses de plusieurs serveurs d'origine et les place sur une connexion partagée vers un client.
Les navigateurs web doivent également supposer que les requêtes faites sur la même connexion par différentes origines web ([RFC6454]) sont faites par des entités mutuellement méfiantes. D'autres scénarios impliquant des entités mutuellement méfiantes sont également possibles.
7.1.2 Atténuation (Mitigation)
Les utilisateurs de HTTP qui exigent la confidentialité pour les champs d'en-tête ou de fin peuvent utiliser des valeurs avec une entropie suffisante pour rendre les hypothèses irréalisables. Cependant, cela est impraticable comme solution générale car elle force tous les utilisateurs de HTTP à prendre des mesures pour atténuer les attaques. Cela imposerait de nouvelles contraintes sur la façon dont HTTP est utilisé.
Plutôt que d'imposer des contraintes aux utilisateurs de HTTP, une implémentation de QPACK peut plutôt contraindre comment la compression est appliquée afin de limiter le potentiel de sondage de la table dynamique.
Une solution idéale sépare l'accès à la table dynamique en fonction de l'entité qui construit le message. Les valeurs de champ qui sont ajoutées à la table sont attribuées à une entité, et seule l'entité qui a créé une valeur particulière peut extraire cette valeur.
Pour améliorer les performances de compression de cette option, certaines entrées pourraient être marquées comme étant publiques. Par exemple, un navigateur web pourrait rendre les valeurs du champ d'en-tête Accept-Encoding disponibles dans toutes les requêtes.
Un encodeur sans bonne connaissance de la provenance des valeurs de champ pourrait plutôt introduire une pénalité pour de nombreuses lignes de champ avec le même nom de champ et des valeurs différentes. Cette pénalité pourrait faire en sorte qu'un grand nombre de tentatives de deviner une valeur de champ entraîne que le champ ne soit pas comparé aux entrées de la table dynamique dans les messages futurs, empêchant efficacement d'autres hypothèses.
Cette réponse pourrait être rendue inversement proportionnelle à la longueur de la valeur du champ. La désactivation de l'accès à la table dynamique pour un nom de champ donné pourrait se produire pour des valeurs plus courtes plus rapidement ou avec une probabilité plus élevée que pour des valeurs plus longues.
Cette atténuation est plus efficace entre deux points d'extrémité. Si les messages sont ré-encodés par un intermédiaire sans connaissance de quelle entité a construit un message donné, l'intermédiaire pourrait fusionner par inadvertance des contextes de compression que l'encodeur d'origine avait spécifiquement gardés séparés.
Note : Simplement supprimer les entrées correspondant au champ de la table dynamique peut être inefficace si l'attaquant dispose d'un moyen fiable de provoquer la réinstallation des valeurs. Par exemple, une requête pour charger une image dans un navigateur web inclut généralement le champ d'en-tête Cookie (une cible potentiellement très valorisée pour ce type d'attaque), et les sites web peuvent facilement forcer le chargement d'une image, rafraîchissant ainsi l'entrée dans la table dynamique.
7.1.3 Littéraux jamais indexés (Never-Indexed Literals)
Les implémentations peuvent également choisir de protéger les champs sensibles en ne les compressant pas et en encodant plutôt leur valeur en tant que littéraux.
Refuser d'insérer une ligne de champ dans la table dynamique n'est efficace que si cela est évité sur tous les sauts. Le bit littéral jamais indexé (voir la section 4.5.4) peut être utilisé pour signaler aux intermédiaires qu'une valeur particulière a été intentionnellement envoyée en tant que littéral.
Un intermédiaire NE DOIT PAS ré-encoder une valeur qui utilise une représentation littérale avec le bit 'N' défini avec une autre représentation qui l'indexerait. Si QPACK est utilisé pour le ré-encodage, une représentation littérale avec le bit 'N' défini DOIT être utilisée. Si HPACK est utilisé pour le ré-encodage, la représentation littérale jamais indexée (voir la section 6.2.3 de [RFC7541]) DOIT être utilisée.
Le choix de marquer qu'une valeur de champ ne devrait jamais être indexée dépend de plusieurs facteurs. Étant donné que QPACK ne protège pas contre la devinette d'une valeur de champ entière, les valeurs courtes ou à faible entropie sont plus facilement récupérées par un adversaire. Par conséquent, un encodeur pourrait choisir de ne pas indexer les valeurs avec une faible entropie.
Un encodeur pourrait également choisir de ne pas indexer les valeurs pour les champs qui sont considérés comme étant très précieux ou sensibles à la récupération, tels que les champs d'en-tête Cookie ou Authorization.
Au contraire, un encodeur pourrait préférer indexer les valeurs pour les champs qui ont peu ou pas de valeur s'ils étaient exposés. Par exemple, un champ d'en-tête User-Agent ne varie généralement pas entre les requêtes et est envoyé à n'importe quel serveur. Dans ce cas, la confirmation qu'une valeur User-Agent particulière a été utilisée fournit peu de valeur.
Notez que ces critères pour décider d'utiliser une représentation littérale jamais indexée évolueront au fil du temps à mesure que de nouvelles attaques seront découvertes.
7.2 Encodage Huffman statique (Static Huffman Encoding)
Il n'existe actuellement aucune attaque connue contre un encodage Huffman statique. Une étude a montré que l'utilisation d'une table d'encodage Huffman statique créait une fuite d'informations ; cependant, cette même étude a conclu qu'un attaquant ne pouvait pas tirer parti de cette fuite d'informations pour récupérer une quantité significative d'informations (voir [PETAL]).
7.3 Consommation de mémoire (Memory Consumption)
Un attaquant peut essayer de faire en sorte qu'un point d'extrémité épuise sa mémoire. QPACK est conçu pour limiter à la fois les quantités de mémoire allouées au pic et de manière stable par un point d'extrémité.
QPACK utilise la définition de la taille maximale de la table dynamique et du nombre maximal de flux bloqués pour limiter la quantité de mémoire que l'encodeur peut amener le décodeur à consommer. Dans HTTP/3, ces valeurs sont contrôlées par le décodeur via les paramètres SETTINGS_QPACK_MAX_TABLE_CAPACITY et SETTINGS_QPACK_BLOCKED_STREAMS, respectivement (voir la section 3.2.3 et la section 2.1.2). La limite sur la taille de la table dynamique tient compte de la taille des données stockées dans la table dynamique, plus une petite allocation pour la surcharge. La limite sur le nombre de flux bloqués n'est qu'un proxy pour la quantité maximale de mémoire requise par le décodeur. La quantité maximale réelle de mémoire dépendra de la quantité de mémoire que le décodeur utilise pour suivre chaque flux bloqué.
Un décodeur peut limiter la quantité de mémoire d'état utilisée pour la table dynamique en définissant une valeur appropriée pour la taille maximale de la table dynamique. Dans HTTP/3, cela est réalisé en définissant une valeur appropriée pour le paramètre SETTINGS_QPACK_MAX_TABLE_CAPACITY. Un encodeur peut limiter la quantité de mémoire d'état qu'il utilise en choisissant une taille de table dynamique plus petite que celle que le décodeur autorise et en signalant cela au décodeur (voir la section 4.3.1).
Un décodeur peut limiter la quantité de mémoire d'état utilisée pour les flux bloqués en définissant une valeur appropriée pour le nombre maximal de flux bloqués. Dans HTTP/3, cela est réalisé en définissant une valeur appropriée pour le paramètre SETTINGS_QPACK_BLOCKED_STREAMS. Les flux qui risquent de devenir bloqués ne consomment pas de mémoire d'état supplémentaire sur l'encodeur.
Un encodeur alloue de la mémoire pour suivre toutes les références de table dynamique dans les sections de champ non reconnues. Une implémentation peut directement limiter la quantité de mémoire d'état en utilisant uniquement autant de références à la table dynamique qu'elle souhaite suivre ; aucune signalisation au décodeur n'est requise. Cependant, limiter les références à la table dynamique réduira l'efficacité de la compression.
La quantité de mémoire temporaire consommée par un encodeur ou un décodeur peut être limitée en traitant les lignes de champ de manière séquentielle. Une implémentation de décodeur n'a pas besoin de conserver une liste complète de lignes de champ lors du décodage d'une section de champ. Une implémentation d'encodeur n'a pas besoin de conserver une liste complète de lignes de champ lors de l'encodage d'une section de champ si elle utilise un algorithme en un seul passage. Notez qu'il pourrait être nécessaire pour une application de conserver une liste complète de lignes de champ pour d'autres raisons ; même si QPACK ne force pas cela à se produire, les contraintes de l'application pourraient rendre cela nécessaire.
Bien que la limite négociée sur la taille de la table dynamique représente une grande partie de la mémoire qui peut être consommée par une implémentation QPACK, les données qui ne peuvent pas être immédiatement envoyées en raison du contrôle de flux ne sont pas affectées par cette limite. Les implémentations devraient limiter la taille des données non envoyées, en particulier sur le flux de décodeur où la flexibilité pour choisir quoi envoyer est limitée. Les réponses possibles à un excès de données non envoyées pourraient inclure la limitation de la capacité du pair à ouvrir de nouveaux flux, la lecture uniquement à partir du flux d'encodeur, ou la fermeture de la connexion.
7.4 Limites d'implémentation (Implementation Limits)
Une implémentation de QPACK doit s'assurer que les grandes valeurs pour les entiers, l'encodage long pour les entiers, ou les longs littéraux de chaîne ne créent pas de faiblesses de sécurité.
Une implémentation doit définir une limite pour les valeurs qu'elle accepte pour les entiers, ainsi que pour la longueur encodée ; voir la section 4.1.1. De la même manière, elle doit définir une limite à la longueur qu'elle accepte pour les littéraux de chaîne ; voir la section 4.1.2. Ces limites DEVRAIENT être suffisamment grandes pour traiter le champ individuel le plus grand que l'implémentation HTTP peut être configurée pour accepter.
Si une implémentation rencontre une valeur plus grande qu'elle est capable de décoder, cela DOIT être traité comme une erreur de flux de type QPACK_DECOMPRESSION_FAILED si sur un flux de requête ou une erreur de connexion du type approprié si sur le flux d'encodeur ou de décodeur.