Чтобы подтвердить, что хука отправлен из МТС Линк в заголовок HTTP запроса встроен код для проверки.
При создании нового вебхука можно задать поле "секретная фраза". Если для вебхука был задан секрет, то POST-запрос вебхука будет содержать подпись в заголовке X-Webhook-Signature. Подпись представляет собой код аутентификации (проверки подлинности) сообщения на основе хэша (HMAC) c алгоритмом SHA-256.
Для определения подлинности вебхука необходимо выполнить следующие действия:
PHP
<?php function verifySignature($payload, $headerSignature, $secretKey) { $expectedSignature = hash_hmac('sha256', $payload, $secretKey); return hash_equals($expectedSignature, $headerSignature); } $headers = getallheaders(); $headerSignature = $headers['X-Webhook-Signature'] ?? ''; $payload = file_get_contents("php://input"); $secretKey = 'SuperSecret'; if (verifySignature($payload, $headerSignature, $secretKey)) { echo "Webhook signature is valid"; } else { echo "Invalid webhook signature"; }
Go
package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "io/ioutil" "net/http" ) func verifySignature(payload []byte, headerSignature string, secretKey string) bool { h := hmac.New(sha256.New, []byte(secretKey)) h.Write(payload) expectedSignature := hex.EncodeToString(h.Sum(nil)) return hmac.Equal([]byte(expectedSignature), []byte(headerSignature)) } func handler(w http.ResponseWriter, r *http.Request) { payload, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "Unable to read request body", http.StatusBadRequest) return } headerSignature := r.Header.Get("X-Webhook-Signature") secretKey := "SuperSecret" if verifySignature(payload, headerSignature, secretKey) { fmt.Fprintln(w, "Webhook signature is valid") } else { http.Error(w, "Invalid webhook signature", http.StatusUnauthorized) } } func main() { http.HandleFunc("/webhook", handler) http.ListenAndServe(":8080", nil) }
Ruby
def verify_signature(payload_body) signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body) return halt 500, "Invalid webhook signature" unless Rack::Utils.secure_compare(signature, request.env['X-Webhook-Signature']) end post '/webhook' do request.body.rewind payload_body = request.body.read verify_signature(payload_body) push = JSON.parse(payload_body) "I got some JSON: #{push.inspect}" end
JavaScript
let encoder = new TextEncoder(); async function verifySignature(secret, header, payload) { let parts = header.split("="); let sigHex = parts[1]; let algorithm = { name: "HMAC", hash: { name: 'SHA-256' } }; let keyBytes = encoder.encode(secret); let extractable = false; let key = await crypto.subtle.importKey( "raw", keyBytes, algorithm, extractable, [ "sign", "verify" ], ); let sigBytes = hexToBytes(sigHex); let dataBytes = encoder.encode(payload); let equal = await crypto.subtle.verify( algorithm.name, key, sigBytes, dataBytes, ); return equal; } function hexToBytes(hex) { let len = hex.length / 2; let bytes = new Uint8Array(len); let index = 0; for (let i = 0; i < hex.length; i += 2) { let c = hex.slice(i, i + 2); let b = parseInt(c, 16); bytes[index] = b; index += 1; } return bytes; }
TypeScript
import { Webhooks } from "@octokit/webhooks"; const webhooks = new Webhooks({ secret: process.env.WEBHOOK_SECRET, }); const handleWebhook = async (req, res) => { const signature = req.headers["X-Webhook-Signature"]; const body = await req.text(); if (!(await webhooks.verify(body, signature))) { res.status(403).send("Forbidden"); return; } // Остальная логика здесь };
Python
import hashlib import hmac def verify_signature(payload_body, secret_token, signature_header): """Убедитесь, что вебхук был отправлен с МТС.Линк, выполнив проверку SHA256. В случае неудачной проверки возвращается ошибка 403. Аргументы: payload_body: исходное тело запроса для проверки (request.body()) secret_token: секрет вебхука (SECRET_TOKEN) signature_header: заголовок с подписью (X-Webhook-Signature) """ if not signature_header: raise HTTPException(status_code=403, detail="X-Webhook-Signature header is missing!") hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256) expected_signature = hash_object.hexdigest() if not hmac.compare_digest(expected_signature, signature_header): raise HTTPException(status_code=403, detail="Invalid webhook signature")