HubSpotからのリクエストを検証する

連携においてHubSpotから受け取るリクエストが実際にHubSpotから送信されたことを確かめるために、いくつかのヘッダーがリクエストに入力されます。これらのヘッダーと一緒に受信リクエストのフィールドを使用して、リクエストの署名を確認することができます。

署名の確認方法は署名のバージョンによって異なります。

  • HubSpot署名の最新バージョンを使用したリクエストを検証するには、X-HubSpot-Signature-V3ヘッダーを使用し、v3バージョンの署名を検証するための関連手順に従います。
  • 後方互換性を維持するために、HubSpotからのリクエストには旧バージョンの署名も含まれています。旧バージョンの署名を検証するには、X-HubSpot-Signature-Versionヘッダーをチェックしてから、バージョンがv1v2のどちらであるかに基づいて、以下の関連手順に従います。

以下の方法で、アプリのクライアントシークレットからのハッシュ値と受信リクエストのフィールドを抽出することができます。ハッシュ値を計算したら、その値を署名と比較します。両者が等しい場合は、リクエストは検証に合格したことになります。そうでない場合は、リクエストが転送中に改ざんされたか、あるいは他の誰かがエンドポイント宛ての偽装のリクエストを送信している可能性があります。

v1リクエスト署名を使用したリクエストの検証

アプリがWebhooks API経由でCRMオブジェクトイベントに登録されている場合は、X-HubSpot-Signature-Versionヘッダーがv1に設定されたリクエストがHubSpotから送信されます。X-HubSpot-Signatureヘッダーは、貴社のアプリのクライアントシークレットとリクエスト詳細の組み合わせに基づくSHA-256ハッシュになっています。

この署名のバージョンを確認するには、以下の手順に従います。

  • クライアントシークレットリクエスト本文(存在する場合)のように連結した文字列を作成します。
  • 連結した文字列のSHA-256ハッシュを生成します。
  • ハッシュ値とX-HubSpot-Signatureヘッダーの値を比較します。
    • 同一の場合、このリクエストが検証に合格したことになります。
    • これらの値が一致しない場合、転送中にリクエストが改ざんされたか、誰かがHubSpotになりすまして貴社のエンドポイントにリクエストを送信していると考えられます。

リクエスト本文の例

//Client secret : yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy // Request body: [ {"eventId":1,"subscriptionId":12345," portalId":62515", occurredAt":1564113600000", subscriptionType":"contact.creation", "attemptNumber":0, "objectId":123, "changeSource":"CRM", "changeFlag":"NEW", "appId":54321} ]

v1のリクエスト署名の例 

NOTE: This is only an example for generating the expected hash. You will need to compare this expected hash with the actual hash in the X-HubSpot-Signature header. >>> import hashlib >>> client_secret = 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' >>> request_body = '[{"eventId":1,"subscriptionId":12345,"portalId":62515,"occurredAt":1564113600000,"subscriptionType":"contact.creation","attemptNumber":0,"objectId":123,"changeSource":"CRM","changeFlag":"NEW","appId":54321}]' >>> source_string = client_secret + request_body >>> source_string 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy[{"eventId":1,"subscriptionId":12345,"portalId":62515,"occurredAt":1564113600000,"subscriptionType":"contact.creation","attemptNumber":0,"objectId":123,"changeSource":"CRM","changeFlag":"NEW","appId":54321}]' >>> hashlib.sha256(source_string).hexdigest() '232db2615f3d666fe21a8ec971ac7b5402d33b9a925784df3ca654d05f4817de' NOTE: This is only an example for generating the expected hash. You will need to compare this expected hash with the actual hash in the X-HubSpot-Signature header. irb(main):003:0> require 'digest' => true irb(main):004:0> client_secret = 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' => "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy" irb(main):005:0> request_body = '[{"eventId":1,"subscriptionId":12345,"portalId":62515,"occurredAt":1564113600000,"subscriptionType":"contact.creation","attemptNumber":0,"objectId":123,"changeSource":"CRM","changeFlag":"NEW","appId":54321}]' => "[{\"eventId\":1,\"subscriptionId\":12345,\"portalId\":62515,\"occurredAt\":1564113600000,\"subscriptionType\":\"contact.creation\",\"attemptNumber\":0,\"objectId\":123,\"changeSource\":\"CRM\",\"changeFlag\":\"NEW\",\"appId\":54321}]" irb(main):006:0> source_string = client_secret + request_body => "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy[{\"eventId\":1,\"subscriptionId\":12345,\"portalId\":62515,\"occurredAt\":1564113600000,\"subscriptionType\":\"contact.creation\",\"attemptNumber\":0,\"objectId\":123,\"changeSource\":\"CRM\",\"changeFlag\":\"NEW\",\"appId\":54321}]" irb(main):007:0> Digest::SHA256.hexdigest source_string => "232db2615f3d666fe21a8ec971ac7b5402d33b9a925784df3ca654d05f4817de" NOTE: This is only an example for generating the expected hash. You will need to compare this expected hash with the actual hash in the X-HubSpot-Signature header. > const crypto = require('crypto') undefined > client_secret = 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' > request_body = '[{"eventId":1,"subscriptionId":12345,"portalId":62515,"occurredAt":1564113600000,"subscriptionType":"contact.creation","attemptNumber":0,"objectId":123,"changeSource":"CRM","changeFlag":"NEW","appId":54321}]' '[{"eventId":1,"subscriptionId":12345,"portalId":62515,"occurredAt":1564113600000,"subscriptionType":"contact.creation","attemptNumber":0,"objectId":123,"changeSource":"CRM","changeFlag":"NEW","appId":54321}]' > source_string = client_secret + request_body 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy[{"eventId":1,"subscriptionId":12345,"portalId":62515,"occurredAt":1564113600000,"subscriptionType":"contact.creation","attemptNumber":0,"objectId":123,"changeSource":"CRM","changeFlag":"NEW","appId":54321}]' > hash = crypto.createHash('sha256').update(source_string).digest('hex') '232db2615f3d666fe21a8ec971ac7b5402d33b9a925784df3ca654d05f4817de'

この場合は以下のハッシュ値が得られます。
232db2615f3d666fe21a8ec971ac7b5402d33b9a925784df3ca654d05f4817de

v2リクエスト署名を使用したリクエストの検証

アプリがワークフロー内のWebhookアクションからのデータを処理している場合、またはカスタムCRMカード用のデータを返している場合は、X-HubSpot-Signature-Versionヘッダーがv2に設定されたリクエストがHubSpotから送信されます。X-HubSpot-Signatureヘッダーは、貴社のアプリのクライアントシークレットとリクエスト詳細の組み合わせに基づくSHA-256ハッシュになっています。

この署名を検証するには、以下の手順に従います。

  • クライアントシークレットHTTPメソッドURIリクエスト本文(存在する場合)のように連結した文字列を作成します。
  • 連結した文字列のSHA-256ハッシュを生成します。
  • ハッシュ値を署名と比較します。
    • 同一の場合、このリクエストが検証に合格したことになります。
    • これらの値が一致しない場合、転送中にリクエストが改ざんされたか、誰かがHubSpotになりすまして貴社のエンドポイントにリクエストを送信していると考えられます。

注:
  • ソース文字列の作成に使用するURIは、(プロトコルを含めて)元のリクエストと完全に一致する必要があります。署名の検証に問題がある場合は、クエリーパラメーターが元のリクエストに記載した順序と同じになっていることを確かめてください。
  • ソース文字列は、SHA-256ハッシュ値の計算前にUTF-8にエンコードしておく必要があります。

GETリクエストの例


リクエスト本文の例

  • クライアントシークレット:yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
  • HTTPメソッド:POST
  • URIhttps://www.example.com/webhook_uri
  • リクエスト本文{"example_field":"example_value"} 

 

v3のリクエスト署名の検証

X-HubSpot-Signature-v3ヘッダーは、貴社のアプリのクライアントシークレットとリクエスト詳細の組み合わせに基づくHMAC SHA-256ハッシュになっています。また、X-HubSpot-Request-Timestampヘッダーも含まれます。

以下の手順に従って、X-HubSpot-Signature-v3ヘッダーを使用してリクエストを検証します。

  • タイムスタンプが5分前よりも古い場合には、リクエストを拒否します。
  • リクエストURIで、下表に示すURLエンコード文字のいずれかをデコードします。クエリー文字列の先頭を示す疑問符をデコードする必要はありません
エンコード値 デコード値
%3A :
%2F /
%3F ?
%40 @
%21 !
%24 $
%27 '
%28 (
%29 )
%2A *
%2C ,
%3B ;
  • requestMethodrequestUrirequestBody+タイムスタンプのように連結したutf-8エンコード文字列を作成します。タイムスタンプは、X-HubSpot-Request-Timestampヘッダーで提供されます。
  • アプリシークレットをHMAC SHA-256関数のシークレットとして使用し、連結した文字列のHMAC SHA-256ハッシュを作成します。
  • HMAC関数の結果をBase64エンコードします。
  • ハッシュ値を署名と比較します。同一の場合、このリクエストの発信元がHubSpotであることが検証されたことになります。タイミング攻撃を防ぐために、一定時間の文字列比較を使用することをお勧めします。

参考になりましたか?
こちらのフォームではドキュメントに関するご意見をご提供ください。HubSpotがご提供しているヘルプはこちらでご確認ください。