Хэширование тела запроса Plaid Webhook

Я пытаюсь проверить веб-перехватчик, отправленный с помощью API Plaid. Каждый запрос веб-перехватчика отправляется с заголовком «plaid-verification», который представляет собой веб-токен JSON.

Шаги, необходимые для проверки:

  1. Извлечь JWT из заголовка запроса

signed_jwt = eyJhbGciOiJFUzI1NiIsImtpZCI6IjZjNTUxNmUxLTkyZGMtNDc5ZS1hOGZmLTVhNTE5OTJlMDAwMSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1OTA4ODcwMDEsInJlcXVlc3RfYm9keV9zaGEyNTYiOiJiNjNhMDdiNTQ3YjAwZjk5MjU0N2Y2YmJjOGQ5YWNjNjFhOGNjZWUxMzhiYzgyZjQ0YTZiYWEwOTY4M2E1ZDBmIn0.OOKvIihgqCj7Qrb2bmz7T3t7uK-0JyjiEqL2s1kWeJBM4MMmjaHKK8GmU_z91QolBWMzvPgs718EElY-rE3cwQ

  1. Извлеките значение заголовка JWT без проверки подписи, которая выглядит следующим образом:
    {
      "alg": "ES256",
      "kid": "6c5516e1-92dc-479e-a8ff-5a51992e0001",
      "typ": "JWT"
    }
  1. Извлеките kid и POST в /webhook_verification_key/get
    POST /webhook_verification_key/get
    {
        "client_id": "MY_CLIENT_ID"
        "secret": "MY_SECRET_ID"
        "key_id": "6c5516e1-92dc-479e-a8ff-5a51992e0001"
    }

Ответ:

{
  "key": {
    "alg": "ES256",
    "created_at": 1560466143,
    "crv": "P-256",
    "expired_at": null,
    "kid": "6c5516e1-92dc-479e-a8ff-5a51992e0001",
    "kty": "EC",
    "use": "sig",
    "x": "35lvC8uz2QrWpQJ3TUH8t9o9DURMp7ydU518RKDl20k",
    "y": "I8BuXB2bvxelzJAd7OKhd-ZwjCst05Fx47Mb_0ugros"
  },
  "request_id": "HvfCtrDLG1ihcp7"
}
  1. Интерпретируйте key как веб-ключ JSON, проверьте правильность подписи веб-ключа JSON и извлеките полезную нагрузку (используя библиотеку jose python).
claims = jwt.decode(signed_jwt, key, algorithms=['ES256'])

claims = {
             "iat": 1590887001, 
             "request_body_sha256": "b63a07b547b00f992547f6bbc8d9acc61a8ccee138bc82f44a6baa09683a5d0f"
         }
  1. Вычислите SHA-256 тела запроса и убедитесь, что он соответствует claims['request_body_sha256']:

Тело находится в файле body.json

{
    "error": null,
    "item_id": "yxQbxDjnD8hr69pKbQpbcKeVn3GL9QuyA7NV3",
    "new_transactions": 390,
    "webhook_code": "HISTORICAL_UPDATE",
    "webhook_type": "TRANSACTIONS"
}

Вычисление SHA-256 для body.json

f = open('body.json')
body = json.load(f)
f.close()

m = hashlib.sha256()
m.update(json.dumps(body).encode())
body_hash = m.hexdigest()
print(body_hash)

body_hash = 'efbb5274864518f7eb3834125d9bcdb95fb03066d3d1bed3ebcc6163d8dc3579'

Хэш тела в приведенном выше примере не равен хешу тела, полученному от Plaid. Здесь возможны 2 проблемы:

  1. Плед не отправляет правильный хэш тела (маловероятно)
  2. Метод хеширования, который я использую для хеширования тела, отличается от метода Пледа.

Есть ли что-то, что мне здесь не хватает? Возможно, тело запроса закодировано по-другому на моей стороне? Я использую Node.js и Express в производстве, но я создал скрипт Python, чтобы следовать методу, описанному Плейдом здесь, но я все еще не получаю правильный хэш. Я честно без идей.


person Jacob Godin    schedule 31.05.2020    source источник


Ответы (2)


Я поделился этим с нашей замечательной службой поддержки, и они нашли проблему. Кажется, проблема с пробелами. Если вы измените body.json, добавив 2 пробела на «вкладку» в каждой новой строке, он сгенерирует правильный хэш.

person Alex    schedule 02.06.2020

Алекс прав. Недавно я столкнулся с той же проблемой, и действительно оказалось, что Plaid хэширует свое тело на основе интервала табуляции, равного 2. Вы можете использовать JSON.stringify(body, null, 2), чтобы все было хорошо:

const happyBody = JSON.stringify(body, null, 2);
const body_hash = crypto
  .createHash("sha256")
  .update(happyBody)
  .digest("hex");

См. также: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

Бонус: вы можете использовать JSON.stringify(object, replacer, space) для украшения JSON. Целое число для последнего аргумента будет определять, сколько пробелов использовать, но вы можете заменить его чем-то вроде "/t", если вместо этого вам нужны вкладки: JSON.stringify(someObject, null, "\t");

person Viktor    schedule 03.09.2020