14 KiB
Table of Contents
[TOC]
L'authentification est le fait de vérifier une déclaration d'identité, généralement en vue d'autoriser l'accès à des ressources (e.g. un compte). Pour cela l'utilisateur apporte une preuve de son identité qui sera vérifiée par un tiers.
Dans le cadre de services distants, l'utilisateur envoie une preuve d'identité auprès du fournisseur de service (FdS) afin de pouvoir s'authentifier et d'accéder ensuite aux ressources. Il est ainsi nécessaire d'utiliser un protocole d'authentification, i.e. de définir la manière dont le client et le FdS vont échanger des messages afin de permettre l'authentification, tout en respectant des propriétés de sécurités.
L'authentification est généralement inclue dans une négotiation de paramètres de sécurités (cf TLS) qu'on appelle handshake (poignée de main en anglais). 2-way handshake et 3-way handshake font référence au nombre de messages nécessaires à l'authentification/négotiation.
Dans la suite de ce document, nous partirons du principe que la preuve d'identité est un mot de passe. Cependant, il est tout à fait possible d'utiliser n'importe quelle suite arbitraire de charactères voire d'octets, quelle que soit son origine (e.g. suite d'octets aléatoires).
Password Authentication Protocol (PAP)
Le protocole d'authentification le plus simple (et le moins sécurisé) reste tout simplement d'envoyer le mot de passe au serveur qui informera le client du succès ou de l'échec de l'authentification. Ce protocole est donc un 2-way handshake.
C'est sur ce principe qu'est construit HTTP Basic Access Authentication (HTTP BA) dans lequel l'en-tête de la requête HTTP contient un champ Authorization: Basic [credentials]
intégrant le login et mot de passe encodé en base 64.
Vous noterez que le FdS doit alors avoir connaissance du mot de passe afin de pouvoir le comparer à celui reçu. Ce qui implique de le stocker en clair dans une base de donnée. Une variante de ce protocole serait d'envoyer et de stocker un hash (potentiellement salé et poivré) du mot de passe au lieu de directement l'utiliser.
TLS : Sécuriser les échanges
Lors du processus d'authentification, il est bien évidemment nécessaire de protéger l'authenticité, l'intégrité, la confidentialité, et la destination des messages. Certains protocoles d'authentifications peuvent assurer eux-mêmes ces propriétés de sécurités, d'autres ont besoin d'une communication sécurisée (généralement TLS).
TLS est un protocole de communication de couches 5 et 6 permettant de sécuriser une communication. Les paramètres de sécurités sont négotiés au début de la communication :
-
la version de TLS à utiliser ;
-
un secret partagé pour l'algorithme d'échange de clefs ;
-
la suite de chiffrement (cipher suite) à utiliser (e.g. ECDHE-RSA-AES128-GCM-SHA256) constitué de :
-
l'algorithme d'échange de clefs (e.g. ECDHE) ;
-
l'algorithme d'authentification (e.g. authentification du serveur par signature RSA) ;
-
l'algorithme de chiffrement par bloc (e.g. AES en mode GCM) ;
-
l'algorithme d'authentification de messages (e.g. SHA256).
-
Depuis la version 1.3, TLS utilise un 3-way handshake :
-
CLIENT HELLO : le client envoie des données nécessaires à l'échange de clefs et propose des suites de chiffrements supportés.
-
SERVER HELLO + SERVER FINISHED : le serveur envoie son certificat, une signature, la suite de chiffrement choisie, et des données nécessaires à l'échange de clefs.
-
CLIENT FINISHED : le client authentifie le serveur a partir de sa signature et de son certificat.
Il est ensuite possible d'échanger des messages de manière sécurisée grâce aux algorithmes de chiffrement par bloc et d'authentification de messages.
TLS permet d'authentifier le serveur, le client, ou les deux. Usuellement, le serveur est authentifié, seul, via une chaîne de certificats (certificate chain), ce afin d'éviter les attaques de type Man In The Middle (MITM). L'utilisateur pourra ensuite s'authentifier directement auprès du service distant, par dessus la comunication sécurisée ainsi établie.
OTP et forward secrecy
Il arrive que pour diverses raisons, un système se retrouve vulnérable à un instant T :
-
l'authentification du serveur a été compromise du fait d'une authorité de certification compromise, d'un faux certificat déposé dans la liste des certificats de confiances du client, ou de l'utilisateur décidant de faire confiance au certificat malgré des avertissements.
-
TLS n'a pas été utilisé lors d'un des échanges (ou des paramètres de sécurités faibles ont été utilisés), potentiellement du fait d'un défaut de configuration ou de mises à jour.
-
L'utilisateur a utilisé un client corrompu, ou s'est fait avoir par du phishing (e.g. typosquatting).
Il est alors important d'assurer les deux propriétés suivantes :
-
forward secrecy : la vulnérabilité du système à un instant T ne compromet pas les échanges futurs.
-
backward secrecy : la vulnérabilité du système à un instant T ne compromet pas les échanges passés.
Si TLS assure ces 2 propriétés pour les communications entre le client et le serveur, il ne peut cependant pas protéger les informations transmises en cas de vulnérabilités. En effet, si une authentification est effectuée alors que la communication (ou le serveur) est vulnérable, un attaquant pourrait alors lire les messages échangés et en extraire le mot de passe envoyé du client vers le serveur. En connaissant le mot de passe, l'attaquant serait alors en capacité de s'authentifier. De manière plus générale, on souhaite éviter les attaques par rejeux.
L'objectif est alors d'utiliser une sorte de mot de passe unique à chaque authentification, un One Time Password (OTP). On peut voir l'OTP comme le produit d'une fonction OTP(secret, i)
où secret
serait le mot de passe maître.
Il existe plusieurs types d'OTP :
-
asynchrone où
i
est incrémenté à chaque authentifications réussies. Il ne peut cependant pas être utilisé pour s'authentifier sur plusieurs serveurs différents. -
synchrone où
i
est calculé en fonction du temps, il n'est alors valide que pour une période de temps donné. Une désynchronisation des horloges du client et du serveur peut alors empêcher toute authentification. -
basé sur un challenge, cf section suivante.
La fonction OTP
repose généralement sur une fonction non-inversible, e.g. un hash, une signature, voire un chiffrement symmétrique, qu'on nommera H
ou Sign_{key}
ou Cipher_{key}
. Plusieurs architectures sont alors possibles :
-
OTP(secret, i) = H(secret | i)
ouSign_{secret}(i)
ouCipher_{secret}(i)
.-
Si
H
est basé sur une fonction de hashage, le serveur connaîtsecret
et est capable de calculer l'OTP de son côté pour ensuite le comparer avec celui reçu. -
Si
H
est basé sur une signature, le serveur connaît la clé publique et est capable de vérifier la signature reçue ainsi que le contenu du message reçu. -
Si
H
est basé sur un chiffrement symmétrique, le serveur connaîtsecret
et est capable de déchiffrer le message reçu.
-
-
Une chaîne de hash (hash chain) :
OTP(secret, i-1) = H( OTP(secret, i) )
, etOTP(secret, n) = secret
. Reformulé autrement,OTP(secret, i)
correspond au secret hashén-i
fois. À la i-ème tentative d'authentification, le serveur reçoitOTP(secret, i)
. ConnaissantOTP(secret, i-1)
, qui est aussiH(OTP(secret, i))
, il lui suffit de calculer le hash l'OTP reçu puis de le comparer à l'OTP stocké. Si l'authentification réussie, il stocke l'OTP reçu pour la prochaine authentification. L'inconvéniant de cette architecture est dans le calcul initial deOTP(secret, 0)
, qui correspondra ausecret
hashén - 0 = n
fois. Par construction, il ne sera ainsi pas possible d'effectuer plus den
authentifications. En effet, cela necéssiterait alors pour le client de pouvoir calculerOTP(secret, n+1) = H^{-1}(OTP(secret, n)) = H^{-1}(secret)
. Or une fonction de hashage est, par construction, non-inversible. -
De manière analague, il est aussi possible de construire une chaîne de signature :
OTP(secret, i + 1) = Sign_{secret}(OTP(secret, i))
etOTP(secret, 0) = Sign_{secret}( H(secret) )
.
Challenge-Response Protocols (CRP)
Pour des raisons évidentes, vous noterez qu'on évite le nom de Challenge-Response Authentification Protocol.
Lors d'une authentification, le serveur n'a en réalité pas besoin de connaître le mot de passe, mais de vérifier la connaissance du mot de passe par le client. Le principe est alors d'envoyer un challenge au client, auquel il ne peut correctement répondre que s'il connaît le secret. Il s'agit alors d'un 3-way handshake.
Un protocole d'authentification challenge-réponse ne dévoilant aucune information au serveur est nommé 0-knowledge authentication protocol (preuve à divulgation nulle de connaissance), ou 0-Knowledge Interactive proof (ZKIP) based authentication. Tous les CRP ne sont pas nécessairement des ZKIP.
Une manière d'implémenter cela est d'utiliser un OTP(secret, i)
basé sur un challenge, où i
serait alors un challenge généré aléatoirement à chaque authentification.
Cela est par exemple le cas de Challenge Handshake Authentication Protocol (CHAP), où OTP(secret, i) = H(secret|i)
avec i
un nombre aléatoire généré par le serveur et envoyé au client.
Ce protocole reste toutefois vulnérable à plusieurs attaques. HTTP Digest Access Authentication (HTTP DAA) implémente plusieurs protections additionnelles.
Authorization: Digest username:realm:nonce:uri:response:qop:cnonce:nc
est ainsi calculé à partir d'informations additionnelles :
-
realm, un texte affiché à l'utilisateur pour informations. Il contient généralement le domaine du serveur sous la forme
group@domain.com
, évitant ainsi théoriquement les attaques par relais, phishing et/ou typosquatting. -
le serveur stocke les server nonce (nonce), i.e. les challenges, récemment émis, qui peuvent aussi contenir un timestamp afin d'éviter les attaques par rejeux ainsi qu'une réutilisation d'un nonce.
-
un client nonce (cnonce), i.e. une chaîne de caractères aléatoire choisi par le client afin d'éviter des attaques par textes clairs choisis.
-
un nonce count (nc) comptant le nombre de fois qu'un nonce a été utilisé. Par exemple lorsque l'utilisateur se trompe dans la saisie de son mot de passe et rééssaye de s'authentifier.
-
la ressource demandée (
HA2
), permettant de lier l'authentification à la ressource demandée afin de garantir l'intégrité de la demande. En effet, lors d'une authentification, un attaquant pourrait modifier un message, sans toucher la réponse au challenge, e.g. modifier le montant d'une transaction bancaire. -
un hash du mot de passe est utilisé (
HA1
), évitant au serveur d'avoir à connaître et donc à stocker le mot de passe en clair.
response
est alors calculé ainsi :
HA1 = H(username:realm:password)
HA2 = H(method:URI:H(body))
response = H(HA1:nonce:nc:cnonce:qop:HA2)
HTTP DAA souffre cependant de plusieurs vulnérabilités :
-
Il ne permet pas de vérifier l'authenticité du serveur, et requiert donc d'être utilisé par-dessus TLS.
-
le serveur peut demander une authentification HTTP BA.
-
si la base de donnée du serveur fuite, un attaquant peut alors utiliser les
HA1
stockés pour s'authentifier.
La sécurité d'un protocole d'authentification
Garantir l'authenticité d'un serveur ne suffit pas à garantir son honnêteté et intégrité.
Un serveur malhonnête peut tout à fait prendre des libertés avec le protocole d'authentification en :
-
forçant des paramètres de sécurités faibles ;
-
ne générant pas aléatoirement les nonces ;
-
récupérant le challenge d'un autre serveur afin de construire le challenge envoyé au client ;
-
en envoyant des messages sans respecter l'ordre, le format, ou les données définis par le protocole ;
-
collaborant avec d'autres serveurs ;
-
etc.
Si certaines contre-mesures sont simples, comme n'envoyer que le mot de passe hashé, de poivrer ce hash avec le nom de domaine du serveur, etc. certaines attaques peuvent se révéler bien plus complexes à prendre en considération.
Garantir qu'un protocole d'authentification est fiable n'est pas chose aisée. Il est facile de montrer qu'une attaque/vulnérabilité donnée existe au sein d'un protocole donné, mais prouver l'absence de vulnérabilités est impossible car nécessiterait d'avoir conscience de toutes les attaques possibles.
En général, on établi des preuves de sécurité à partir d'un modèle d'attaque/attaquant, i.e. on défini l'attaquant et ses capacités (actions autorisées, puissance de calcul, connaissances de certains secrets, etc.). Pour cela on utilise les méthodes formelles, qui permettent de prouver mathématiquement qu'un secret ne peut être connu par un attaquant, pour un modèle d'attaque et protocole donnés.
Cependant, même avec des preuves de sécurité, le protocole peut être vulnérable face à un attaquant ayant des capacités supérieures à celles définies par le modèle d'attaque. Par exemple, si l'attaquant a la capacité de casser TLS ou de collaborer avec un tiers, certaines propriétés de sécurités de certains protocoles peuvent se retrouver menacés. On évalue donc la sécurité d'un système d'authentification relativement au(x) modèle(s) d'attaque(s) contre lesquels on souhaite se protéger.
Un protocole sécurisé peut aussi être vulnérable dans son implémentation, que ce soit lié à une ambiguité dans le protocole, à une erreur de programmation, à une vulnérabilité dans une bibliothèque utilisée, dans le système d'exploitation, voire même dans le matériel utilisé (RAM, CPU, etc).