Verificarea semnăturii pentru Webhook
Această secțiune oferă exemple de implementare pentru verificarea autenticității cererilor primite, folosind o cheie de semnătură. Acest mecanism contribuie la securitatea integrării (ex. webhook-uri), asigurând că datele nu au fost modificate.
Semnătura este, de obicei, inclusă în header-ul cererii și trebuie validată prin calcularea unui hash HMAC cu cheia secretă partajată.
.NET
using System.Security.Cryptography;
using System.Text;
using System.Text.Json.Nodes;
var jsonMessage = "[CALLBACK MESSAGE]";
var signatureKey = "[YOUR SIGNATURE KEY]";
var jsonNode = JsonNode.Parse(jsonMessage);
var resultElement = jsonNode!["result"]!;
var keys = new Dictionary<string, string>();
foreach (var property in (JsonObject)resultElement)
{
var valueNode = property.Value;
if (valueNode is null)
continue;
string valueStr = property.Key is "amount" or "commission"
? valueNode.GetValue<decimal>().ToString("F2")
: valueNode.ToString();
if (!string.IsNullOrWhiteSpace(valueStr))
keys.Add(property.Key, valueStr);
}
var orderedKeys = keys.OrderBy(kv => kv.Key, StringComparer.OrdinalIgnoreCase).ToArray();
var additionalString =
string.Join(":", orderedKeys.Select(kv => kv.Value));
var hash = SHA256.HashData(Encoding.UTF8.GetBytes($"{additionalString}:{signatureKey}"));
var result = Convert.ToBase64String(hash);
if (result == jsonNode["signature"]!.ToString())
Console.WriteLine("Signature is valid");
else
Console.WriteLine("INVALID SIGNATURE!");
.PHP
<?php
$jsonMessage = '[CALLBACK MESSAGE]';
$signatureKey = '[YOUR SIGNATURE KEY]';
$jsonNode = json_decode($jsonMessage, true);
$resultElement = $jsonNode['result'] ?? [];
$expectedSignature = $jsonNode['signature'] ?? '';
$keys = [];
foreach ($resultElement as $key => $value) {
if (is_null($value)) {
continue;
}
// Format "amount" and "commission" with 2 decimal places
if ($key === 'amount' || $key === 'commission') {
$valueStr = number_format((float)$value, 2, '.', '');
} else {
$valueStr = (string)$value;
}
if (trim($valueStr) !== '') {
$keys[$key] = $valueStr;
}
}
// Sort keys by key name (case-insensitive)
uksort($keys, 'strcasecmp');
// Build the string to hash
$additionalString = implode(':', $keys);
$hashInput = $additionalString . ':' . $signatureKey;
// Generate SHA256 hash and base64-encode it
$hash = hash('sha256', $hashInput, true);
$result = base64_encode($hash);
// Compare the result with the signature
if ($result === $expectedSignature) {
echo "Signature is valid\n";
} else {
echo "INVALID SIGNATURE!\n";
}
node.js
const crypto = require('crypto');
const jsonMessage = '[CALLBACK MESSAGE]';
const signatureKey = '[YOUR SIGNATURE KEY]';
const jsonNode = JSON.parse(jsonMessage);
const resultElement = jsonNode.result || {};
const expectedSignature = jsonNode.signature || '';
const keys = {};
// Collect and format values
for (const [key, value] of Object.entries(resultElement)) {
if (value === null || value === undefined) continue;
let valueStr;
if (key === 'amount' || key === 'commission') {
valueStr = parseFloat(value).toFixed(2); // Always two decimals
} else {
valueStr = String(value);
}
if (valueStr.trim() !== '') {
keys[key] = valueStr;
}
}
// Sort keys case-insensitively
const orderedKeys = Object.keys(keys).sort((a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase())
);
// Join values with colon
const additionalString = orderedKeys.map(k => keys[k]).join(':');
const hashInput = `${additionalString}:${signatureKey}`;
// Hash and base64 encode
const hash = crypto.createHash('sha256').update(hashInput, 'utf8').digest('base64');
Last updated