-
Notifications
You must be signed in to change notification settings - Fork 631
Open
Description
Механизм безопасности API-запросов.
Этот механизм использует ключи RSA для проверки целостности запросов.
Формируется новый заголовок XX-Signature, который вставляется в запрос.
Что требуется:
- Приватный ключ - хранится в секрете у клиента и используется для создания цифровой подписи каждого отправляемого API-запроса.
- Ключ для проверки - сервер подписывает уведомления своим приватным ключом, а торговец проверяет их публичным ключом.
- Python Scripter (Burp Suite).
- Алгоритм формирования подписи
Решение
Burp Suite работает с Jython. Jython - это реализация языка Python, которая работает на платформе Java Virtual Machine (JVM). Поскольку Jython взаимодействует с Java-кодом и использует Java-библиотеки, нужно использовать системные вызовы через библиотеку subprocess:

Иллюстрация работы плагина и скрипта.
Ниже представлен код Python, реализующий описанный механизм подписи запроса:
На рисунке 1 проиллюстрирована попытка отправки запроса без дополнительного заголовка. Как можно заметить, сервер указал на его отсутствие и не обработал сообщение.
На рисунке 2 изображена еще одна попытка отправить запрос, на этот раз с добавлением требуемого заголовка. Однако изменение в теле сообщения вызвало отсутствие совместимости с подписью, в результате чего сервер также отклонил сообщение.
На рисунке 3 представлен измененный запрос, зарегистрированный модулем Burp Logger, который был отправлен на сервер.
Дополнительный материал:
import base64
import subprocess
try:
import urllib.parse as urlparse # Try python 3 first
except ImportError:
import urllib as urlparse # Fallback for python 2
PRIVATE_KEY = "/path/to/key"
SIGNATURE_HEADER = 'XX-Signature'
if messageIsRequest:
print("Executing signature code")
requestInfo = helpers.analyzeRequest(messageInfo.getRequest())
headers = requestInfo.getHeaders()
requestBody = messageInfo.getRequest()[requestInfo.getBodyOffset():]
url = messageInfo.getUrl()
method = requestInfo.getMethod().upper()
path = url.getPath()
# Удалено URL-кодирование параметров
query_string = url.getQuery()
if query_string:
path += "?" + query_string
msg = helpers.bytesToString(requestBody)
# Construct signature input string with UTF-8 encoding and explicit newline.
signature_input = "{}\n{}\n{}".format(method, path, msg)
print('signature_input', signature_input)
# Use openssl via subprocess. Correctly handle encoding and decoding.
try:
cmd = "openssl dgst -sha256 -sign {}".format(PRIVATE_KEY) # Use .format() instead of f-string
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
output, err = process.communicate(input=signature_input.encode('utf-8')) # Encode input for openssl
if err:
raise Exception(err.decode('utf-8'))
signature_bytes = base64.b64encode(output).decode('utf-8').strip() # Base64 encode the result
signature = signature_bytes
except Exception as e:
print("Error creating signature: {}".format(e)) # Use .format() instead of f-string
signature = "" # or handle the error in another way, e.g., return None
new_sign = '{}: {}'.format(SIGNATURE_HEADER, signature)
print('Adding new', new_sign)
newHeaders = []
print("Original headers:", headers)
# Remove existing XX-Signature headers
for h in headers:
if SIGNATURE_HEADER not in h:
newHeaders.append(h)
else:
print('Header exist, removing: ', h)
print("Headers after removing existing signatures:", newHeaders)
print("New signature header:", new_sign)
# Insert the new XX-Signature header as the *second* header in the list.
if len(newHeaders) > 0:
newHeaders.insert(1, new_sign)
else:
newHeaders.append(new_sign) # if there are no headers, put as first
print("Final headers:", newHeaders)
request = helpers.buildHttpMessage(newHeaders, requestBody)
messageInfo.setRequest(request)
Metadata
Metadata
Assignees
Labels
No labels





