Signature des requêtes
Pour une sécurité supplémentaire, vous pouvez utiliser des signatures pour toutes les requêtes vers et depuis notre API. L'utilisation de signatures est optionnelle. Cependant, si une signature est envoyée, elle doit être valide.
Lors de l'envoi de requêtes vers notre API, vous générez une signature que vous envoyez avec vos données. À la réception, le webhook entrant contient la signature et tous les champs dont vous avez besoin pour créer la signature dans votre application afin de vérifier que les deux signatures correspondent et que le webhook est donc correct.
Cette signature peut être utilisée pour s'assurer que
- la requête provient effectivement de la bonne source,
- la requête n'a pas été falsifiée en cours de route,
- les requêtes ne sont pas interceptées et répétées plus tard.
La signature doit contenir certaines données telles que l'horodatage, l'URL cible, un nonce et les données envoyées.
Structure de la signature
Les données suivantes sont nécessaires pour créer une signature :
| valeur | description |
|---|---|
| Nonce | Une chaîne aléatoire composée de 32 caractères alphanumériques. Elle doit être unique pour chaque requête. |
| Horodatage | L'heure de la requête sous forme d'horodatage Unix, ne doit pas dater de plus de 30 secondes |
| Méthode HTTP | La méthode de requête HTTP, par exemple POST ou GET |
| URL cible | L'URL complète à laquelle la requête est envoyée |
| Contenu de la requête | Hash MD5 du contenu de la requête |
Pour générer la signature, l'algorithme SHA256 HMAC est utilisé avec la clé de signature de votre compte. Vous pouvez trouver la clé dans votre connexion dans la zone Développeur dans les paramètres API.
Structure de la chaîne de caractères à signer
La chaîne à signer doit être structurée comme suit :
STRING_TO_SIGN =
Horodatage + \n +
Nonce + \n +
Méthode HTTP + \n +
URL_Cible + \n +
ContentBodyMD5
Par exemple, si vous envoyez cette requête :
POST https://gateway.seven.io/api/sms
{"to": "49170123456789", "text": "Hello World! :-)", "from": "seven"}
La chaîne à signer serait structurée comme suit :
STRING_TO_SIGN =
1634641200
fpPRhAd1s8GXacfR39mWqKPynmmXfJnc
POST
https://gateway.seven.io/api/sms
62dd06ffb3101dc2456517b177b744ae
| valeur | explication |
|---|---|
| 1634641200 | L'horodatage actuel au moment où la requête a été envoyée, ici le 19.10.2021 à 13:00:00 |
| fpPRhAd1s8GXacfR39mWqKPynmmXfJnc | Une chaîne générée aléatoirement avec 32 caractères |
| POST | La méthode HTTP utilisée |
| https://gateway.seven.io/api/sms | L'URL de destination |
| 62dd06ffb3101dc2456517b177b744ae | MD5( {"to": "49170123456789", "text": "Hello World! :-)", "from": "seven"} ) |
Optionnel : intégration de X-Account-Id
Si vous utilisez l'en-tête X-Account-Id pour agir au nom d'un sous-compte, ajoutez l'ID du sous-compte comme ligne supplémentaire à la chaîne à signer. Sans cette liaison, un attaquant capable de man-in-the-middle pourrait rediriger l'en-tête vers un autre de vos sous-comptes sans casser la signature.
STRING_TO_SIGN =
Timestamp + \n +
Nonce + \n +
Méthode HTTP + \n +
URL cible + \n +
ContentBodyMD5 + \n +
X-Account-Id # uniquement lorsque l'en-tête est défini
Lorsque X-Account-Id n'est pas envoyé, la chaîne se termine à ContentBodyMD5 comme indiqué ci-dessus - les clients existants n'ont rien à changer.
Création de la signature
Pour créer la signature, utilisez l'algorithme SHA256 HMAC en conjonction avec la clé de signature de votre compte.
printf "${STRING_TO_SIGN}" | openssl dgst -sha256 -hmac "${SIGNING_SECRET}"
Des implémentations similaires sont disponibles dans la plupart des langages de programmation courants, comme PHP :
$signature = hash_hmac('sha256', $STRING_TO_SIGN, $SIGNING_SECRET);
Envoi de la signature
La signature, le nonce et l'horodatage doivent être envoyés dans les en-têtes HTTP de la requête.
- Name
X-Signature- Type
- string
- Description
La signature générée par vous
- Name
X-Timestamp- Type
- integer
- Description
L'horodatage auquel la signature a été créée
- Name
X-Nonce- Type
- string
- Description
Le nonce que vous avez créé
Valider les webhooks que vous recevez
Les webhooks que nous envoyons à votre serveur sont signés selon la même procédure. Appliquez de votre côté les mêmes règles que celles que nous appliquons aux requêtes entrantes de l'API :
- Reconstruisez la chaîne à signer à partir de la requête reçue (
X-Timestamp,X-Nonce, la méthode HTTP, l'URL complète de votre endpoint et le hash MD5 du corps de requête non modifié) et comparez le résultat avec l'en-têteX-Signature, idéalement avec une comparaison à temps constant. - Rejetez la requête si la valeur de
X-Timestampdate de plus de 30 secondes. - Conservez les valeurs de
X-Noncedans un cache de courte durée et rejetez un nonce déjà vu (protection contre le rejeu). Une durée de cache légèrement supérieure à votre fenêtre temporelle suffit.
Les retries portent leur propre signature
Chaque tentative de livraison est signée séparément. L'envoi initial, chaque retry automatique et un renvoi manuel depuis le débogueur de webhooks portent chacun leur propre X-Timestamp, leur propre X-Nonce et une X-Signature correspondante. Un retry qui arrive plusieurs heures après l'événement porte donc toujours un horodatage actuel et un nonce qui n'a jamais été utilisé auparavant.
Vous n'avez donc pas besoin d'élargir votre fenêtre de validation pour les retries : conservez les 30 secondes et la vérification du nonce exactement comme décrit ci-dessus. Le plan de retry est documenté sur la page Webhooks.
N'utilisez pas le champ webhook_timestamp de la charge utile pour cette vérification. Il désigne l'heure de l'événement et diffère volontairement de l'en-tête X-Timestamp lors d'un retry. Validez toujours la valeur de l'en-tête.
Exemples
Script Bash avec curl et openssl
#!/bin/bash
# Vos données API
API_KEY="VOTRE_CLÉ_API"
SIGNING_SECRET="VOTRE_CLE_SIGNATURE"
# Créer un nonce aléatoire
NONCE=$(openssl rand -hex 32)
# Horodatage
TIMESTAMP=$(date +%s)
# Données de la requête
HTTP_VERB="POST"
URL="https://gateway.seven.io/api/sms"
CONTENT_BODY="{ \"to\": \"0170123456789\", \"text\": \"Hello World! :-)\", \"from\": \"seven.io\" }"
# Créer la signature
CONTENT_BODY_MD5=$(printf "${CONTENT_BODY}" | md5sum | awk '{print $1}')
STRING_TO_SIGN=$(printf "${TIMESTAMP}
${NONCE}
${HTTP_VERB}
${URL}
${CONTENT_BODY_MD5}")
SIGNATURE=$(printf "${STRING_TO_SIGN}" | openssl dgst -sha256 -hmac "${SIGNING_SECRET}" | sed 's/^.*= //')
# Envoyer la requête
curl -X $HTTP_VERB $URL \
-H "X-Api key: $API_KEY" \
-H "X-Nonce: $NONCE" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Signature: $SIGNATURE" \
-H "Content-type: application/json" \
-H "Accept: application/json" \
-d "${CONTENT_BODY}"
Script PHP
# Vos données API
$key = 'VOTRE_CLÉ_API';
$secret = 'VOTRE_CLE_SIGNATURE';
# Créer un nonce aléatoire
$nonce = bin2hex(random_bytes(16));
# Horodatage
$timestamp = time();
# Données de la requête
$http_verb = 'POST';
$content_body = json_encode([
'to' => '49170123456789',
'text' => 'Hello World! :-)',
'from' => 'seven.io',
]);
$url = 'https://gateway.seven.io/api/sms';
# Créer la signature
$StringToSign = [$timestamp, $nonce, $http_verb, $url, md5($content_body)];
$StringToSign = implode(PHP_EOL, $StringToSign);
$hash = hash_hmac('sha256', $StringToSign, $secret);
# Définir les en-têtes
$headers = [
'X-Signature: ' . $hash,
'X-Api-Key: ' . $key,
'X-Timestamp:' . $timestamp,
'X-Nonce:' . $nonce,
'Content-type: application/json',
'Accept: application/json'
];
# Envoyer la requête
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POSTFIELDS, $content_body);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
echo $result;