Nous avons réussi à appliquer l'authentification HMAC pour sécuriser l'Api Web et cela a bien fonctionné. Fondamentalement, l'authentification HMAC utilise une clé secrète pour chaque consommateur que le consommateur et le serveur connaissent tous deux pour hacher un message, HMAC256 doit être utilisé. Dans la plupart des cas, le mot de passe haché du consommateur est utilisé comme clé secrète.
Le message est normalement construit à partir des données de la requête HTTP, ou même de données personnalisées qui sont ajoutées dans l'en-tête HTTP, le message peut inclure :
- Timestamp : heure à laquelle la demande est envoyée (heure UTC ou GMT)
- Verbe HTTP : GET, POST, PUT, DELETE.
- les données du message et la chaîne de requête,
- URL
Sous le capot, l'authentification HMAC serait :
Le consommateur envoie une requête HTTP au serveur web, après avoir construit la signature (sortie du hachage hmac), le modèle de la requête HTTP :
User-Agent: {agent}
Host: {host}
Timestamp: {timestamp}
Authentication: {username}:{signature}
Exemple de demande GET :
GET /webapi.hmac/api/values
User-Agent: Fiddler
Host: localhost
Timestamp: Thursday, August 02, 2012 3:30:32 PM
Authentication: cuongle:LohrhqqoDy6PhLrHAXi7dUVACyJZilQtlDzNbLqzXlw=
Le message à hacher pour obtenir la signature :
GET\n
Thursday, August 02, 2012 3:30:32 PM\n
/webapi.hmac/api/values\n
Exemple de demande POST avec querystring (la signature ci-dessous n'est pas correcte, c'est juste un exemple)
POST /webapi.hmac/api/values?key2=value2
User-Agent: Fiddler
Host: localhost
Content-Type: application/x-www-form-urlencoded
Timestamp: Thursday, August 02, 2012 3:30:32 PM
Authentication: cuongle:LohrhqqoDy6PhLrHAXi7dUVACyJZilQtlDzNbLqzXlw=
key1=value1&key3=value3
Le message à hacher pour obtenir la signature
GET\n
Thursday, August 02, 2012 3:30:32 PM\n
/webapi.hmac/api/values\n
key1=value1&key2=value2&key3=value3
Veuillez noter que les données du formulaire et la chaîne de requête doivent être dans l'ordre, afin que le code sur le serveur obtienne la chaîne de requête et les données du formulaire pour construire le message correct.
Lorsqu'une demande HTTP arrive au serveur, un filtre d'action d'authentification est mis en œuvre pour analyser la demande et obtenir des informations : Le verbe HTTP, l'horodatage, l'uri, les données du formulaire et la chaîne de requête, puis, sur la base de ces informations, il construit une signature (en utilisant un hachage hmac) avec une clé secrète (un mot de passe haché) sur le serveur.
La clé secrète est obtenue à partir de la base de données avec le nom d'utilisateur sur la demande.
Ensuite, le code du serveur compare la signature de la demande avec la signature construite, si elle est égale, l'authentification est réussie, sinon, elle échoue.
Le code pour construire la signature :
private static string ComputeHash(string hashedPassword, string message)
{
var key = Encoding.UTF8.GetBytes(hashedPassword.ToUpper());
string hashString;
using (var hmac = new HMACSHA256(key))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
hashString = Convert.ToBase64String(hash);
}
return hashString;
}
Alors, comment prévenir les attaques par relais ?
Ajouter une contrainte pour l'horodatage, quelque chose comme :
servertime - X minutes|seconds <= timestamp <= servertime + X minutes|seconds
(servertime : heure de réception de la demande par le serveur)
Et, mettre en cache la signature de la demande en mémoire (utiliser MemoryCache, devrait garder en limite de temps). Si la demande suivante a la même signature que la précédente, elle sera rejetée.
Le code de démonstration est placé comme ici : https://github.com/cuongle/WebAPI.Hmac