Подпись оповещений (HMAC)
В этом разделе описан алгоритм подписи и проверки оповещений (callback-сообщений), использующий HMAC-ключ.
Назначение подписи
Подпись оповещений с HMAC решает следующие задачи:
-
подтверждает, что сообщение отправлено платформой HighHelp, владеющей секретным ключом;
-
гарантирует, что данные в теле оповещения не были изменены по пути.
| По умолчанию для подписи оповещений используется асимметричный алгоритм RSA-SHA256 (см. раздел Подпись оповещений (RSA)). HMAC-SHA512 доступен в качестве альтернативного метода. |
Для подписи используется симметричный алгоритм HMAC-SHA512 и общий секретный ключ, известный только вашей системе и HighHelp.
|
При создании ключей кассы формируются HMAC-ключ и пара RSA-ключей. Секретный HMAC-ключ доступен для скачивания только в момент генерации или обновления. После скачивания в разделе API → Настройки Callback отображается маскированное значение ключа; повторное скачивание недоступно, доступно только обновление ключа. |
Смена алгоритма подписи с RSA на HMAC
|
Перед обращением к специалисту убедитесь, что интеграция на стороне мерчанта готова к работе с HMAC-подписью. После переключения алгоритма в личном кабинете будет доступен только HMAC-ключ, а все запросы и оповещения должны использовать HMAC-SHA512. |
Для переключения алгоритма подписи с RSA на HMAC обратитесь к специалисту HighHelp. Смена алгоритма выполняется на стороне HighHelp после согласования.
Перед переключением на HMAC убедитесь, что:
-
HMAC-ключ для кассы сгенерирован;
-
интеграция на стороне мерчанта готова к работе с HMAC-подписью;
-
обновлены скрипты для формирования подписи запросов и проверки подписи оповещений.
После смены алгоритма:
-
в личном кабинете доступен только ключ активного алгоритма подписи: Public Key для RSA или HMAC Key для HMAC;
-
в настройках оповещений отображается текущий алгоритм — HMAC.
Получение HMAC-ключа
Для проверки подписи оповещений используйте секретный HMAC-ключ, настроенный для кассы. Тот же ключ используется для формирования подписи запросов к API.
Секретный HMAC-ключ доступен для скачивания только в момент генерации или обновления. После скачивания в разделе API → Настройки Callback отображается маскированное значение ключа; повторное скачивание недоступно, доступно только обновление ключа.
Генерация и обновление HMAC-ключа
При генерации ключей для кассы одновременно создаются HMAC-ключ и пара RSA-ключей. Если требуется сгенерировать HMAC-ключ отдельно или обновить существующий ключ, используйте инструкции ниже.
|
Секретный HMAC-ключ доступен для скачивания только в момент генерации или обновления. После скачивания в разделе API → Настройки Callback отображается маскированное значение ключа; повторное скачивание недоступно, доступно только обновление ключа. |
Генерация HMAC-ключа
Если для кассы не был сгенерирован HMAC-ключ при первоначальной настройке, выполните следующие действия:
-
Откройте личный кабинет мерчанта.
-
Перейдите в раздел API → Настройки Callback.
-
Нажмите на иконку + в блоке HMAC key.
-
Сохраните ключ в защищенном хранилище и передайте команде разработки.
Обновление HMAC-ключа
Если для кассы настроен алгоритм HMAC-SHA512 и требуется обновить только HMAC-ключ без обновления RSA-ключей, выполните следующие действия:
-
Откройте личный кабинет мерчанта.
-
Перейдите в раздел API → Настройки Callback.
-
Убедитесь, что текущий алгоритм подписи — HMAC (отображается внизу модального окна: Алгоритм: HMAC).
-
Нажмите на кнопку Обновить ключ в блоке HMAC key.
-
В открывшемся окне подтвердите операцию.
-
Сохраните новый секретный HMAC-ключ.
-
Обновите конфигурацию интеграции на стороне мерчанта, заменив старый HMAC-ключ на новый.
|
После обновления HMAC-ключа старый ключ перестанет работать немедленно. Убедитесь, что новый ключ корректно настроен на стороне мерчанта до начала использования. Рекомендуется выполнять обновление в период минимальной нагрузки на систему. |
Нормализация тела запроса
Для формирования подписи используется нормализованное представление JSON-тела запроса.
Алгоритм нормализации:
-
Обойдите JSON-структуру рекурсивно.
-
Для каждого значения сформируйте сформируйте путь в виде
ключ1:ключ2:…:значение. -
Для массивов используйте индексы элементов:
:0,:1, … -
Для булевых значений используйте:
true→1,false→0. -
Отсортируйте все строки по алфавиту.
-
Соедините строки через
;.
Пример исходных данных:
{
"amount": 100,
"status": "success",
"is_paid": true,
"data": {
"id": 123,
"is_active": false
}
}
Результат нормализации:
amount:100;data:id:123;data:is_active:0;is_paid:1;status:success
Пример реализации нормализации (Python3)
def parse_json(prefix, obj, result):
"""
Рекурсивный обход JSON-структуры для формирования пар путь:значение.
"""
if isinstance(obj, dict):
for key, value in obj.items():
if isinstance(key, bool):
key = int(key)
new_prefix = f"{prefix}:{key}" if prefix else str(key)
parse_json(new_prefix, value, result)
elif isinstance(obj, list):
for index, item in enumerate(obj):
if isinstance(item, bool):
item = int(item)
new_prefix = f"{prefix}:{index}"
parse_json(new_prefix, item, result)
else:
if isinstance(obj, bool):
obj = int(obj)
result.append(f"{prefix}:{obj}")
def normalize_message(payload: dict) -> str:
"""
Нормализация JSON в детерминированную строку (формат: путь:значение через ;).
"""
items: list[str] = []
parse_json("", payload, items)
items.sort()
return ";".join(items)
Требования к нормализации
При реализации алгоритма нормализации учитывайте следующие требования:
-
Булевы значения: преобразуются в целочисленное представление (
true→1,false→0). -
Значения null: преобразуются в строку
None. Не используйте пустые строки или пробелы. -
Числа: используйте стандартное строковое представление без локализации (разделителей групп разрядов, локальных форматов). Не добавляйте незначащие нули.
-
Массивы: порядок элементов сохраняется в исходной последовательности. Индексы элементов добавляются к пути как
:0,:1,:2, … -
Объекты: после формирования всех пар
путь:значениевыполняется сортировка по алфавиту по полной строке. -
Кодировка символов: используйте UTF-8 для кодирования перед применением Base64Url. Не изменяйте регистр символов.
-
Пробелы и форматирование: не добавляйте и не удаляйте пробелы в значениях. Используйте точные значения из JSON-структуры.
Алгоритм формирования подписи
Подпись формируется на стороне HighHelp по следующему алгоритму:
-
Нормализация JSON-тела оповещения функцией
normalize_message. -
Кодирование нормализованной строки в Base64Url.
-
Конкатенация полученной строки и
timestamp(строка). -
Вычисление HMAC-SHA512 от результирующей строки с использованием секретного ключа.
-
Кодирование значения HMAC в Base64Url.
-
Передача подписи и метки времени в HTTP-заголовках оповещения.
На стороне мерчанта необходимо воспроизвести шаги 1-5 и сравнить вычисленную подпись с полученной.
Проверка подписи на стороне мерчанта
Для проверки подписи:
-
Получите JSON-тело оповещения и значения заголовков с:
-
меткой времени;
-
подписью;
-
идентификатором кассы (для выбора корректного HMAC-ключа).
-
-
Найдите соответствующий секретный ключ для кассы.
-
Нормализуйте тело оповещения в строку
normalizedпо описанному алгоритму. -
Закодируйте
normalizedв Base64Url. -
Сконструируйте строку
message = encoded + str(timestamp). -
Вычислите HMAC-SHA512 от
messageс использованием секретного ключа. -
Закодируйте результат в Base64Url и сравните с подписью из оповещения.
Пример проверки подписи (Python3)
import base64
import hmac
import hashlib
def verify_hmac_callback_signature(
payload: dict,
signature_b64url: str,
secret_key: bytes,
timestamp: int,
) -> bool:
"""
Проверить подпись оповещения по алгоритму HMAC-SHA512.
"""
try:
# Нормализация и подготовка сообщения
normalized = normalize_message(payload)
encoded = base64.urlsafe_b64encode(normalized.encode("utf-8")).decode("utf-8")
message = f"{encoded}{timestamp}".encode("utf-8")
# Вычисление ожидаемой подписи
expected = hmac.new(secret_key, message, hashlib.sha512).digest()
expected_b64url = base64.urlsafe_b64encode(expected).decode("utf-8")
# Сравнение подписи, устойчивое к тайминговым атакам
return hmac.compare_digest(expected_b64url, signature_b64url)
except Exception:
return False
Рекомендации по безопасности
-
Храните секретный HMAC-ключ на стороне сервера.
-
Обновляйте ключи по запросу через специалиста HighHelp.
-
Не передавайте ключи по незащищенным каналам.
-
Не логируйте ключ полностью. Маскируйте значение: первые 3 символа + 7 астерисков (
*) + последние 3 символа.Пример маскирования ключа
def masked_hmac_key(key: str) -> str: return key[:3] + "*******" + key[-3:] -
Проверяйте идемпотентность оповещений по полям
project_id,payment_id,status,sub_status.Проверка индемпотентности гаррантирует, что повторная обработка одного и того же оповещения не изменяет конечный результат. Сохраняйте комбинацию этих полей для предотвращения дублирования операций. -
Проверяйте допустимое окно времени для
timestamp.
Форма для проверки подписи
Используйте форму ниже для проверки корректности формирования подписи HMAC-SHA512 для запросов к API HighHelp.
|
Обработка введенных данных выполняется локально в браузере; данные не передаются на сервер. |
Выполнение проверки
-
Вставьте JSON-тело запроса в первое поле.
-
Введите ваш секретный ключ.
-
Укажите временную метку в формате Unix timestamp.
-
Вставьте подпись, которую необходимо проверить.
-
Нажмите кнопку Проверить подпись.
Форма отобразит пошаговый процесс формирования подписи и результат проверки предоставленной подписи.
Пример тестовых данных
Для проверки подписи можно использовать следующие тестовые данные:
JSON-тело запроса:
{"general":{"project_id":"test-project-123"},"payment":{"amount":100000,"currency":"USD"}}
Секретный ключ: test-secret-key
Timestamp: 1716299720
После нажатия на кнопку Проверить подпись форма отобразит:
-
Нормализованное представление данных.
-
Используемую временную метку.
-
Вычисленную подпись.
-
Результат проверки предоставленной подписи.