Hubspotワークフローでウェブフック利用時のリクエスト検証方法
本記事ではC# (.NETCore)でのリクエスト検証サンプルについて記載します。
https://developers.hubspot.com/docs/faq/validating-requests-from-hubspot
実現する検証は上記のものになります。
Hubspotワークフローのウェブフックについて
https://knowledge.hubspot.com/workflows/how-do-i-use-webhooks-with-hubspot-workflows
詳細は上記ドキュメント参照ですが、Hubspotのある操作をトリガにウェブフックリクエストを行うことが出来ます。
本記事執筆時点では、Enterprise版限定の機能ではあります...。
今回はここで「Use Request Signature」にチェックを入れた際に実施可能なバリデーションについて説明します。
事前準備
上記リンクの「Verify request signatures in workflow webhooks」記載の手順を踏む必要があります。
- developerアカウントのサインアップ
- アプリケーションの作成
APP IDの設定
「Use Request Signature」にチェックを入れた際にAPP IDの指定が必要になります。
「アプリケーションの作成」で作成したアプリケーションのIDを指定します。(e.g. 123456)
ウェブフックリクエスト先のアプリケーション
ウェブフックリクエスト先のcontroller
[HttpPost]publicasyncTask<IActionResult>PostAsync(JObjectjObject){// リクエストがHubspotウェブフックによるものか検証if(!_hubSpotService.ValidRequestHeader(jObject.ToString(Formatting.None))){returnBadRequest();}returnJobject.ToString();}HubspotからのウェブフックはPOST限定なので、POSTで受け付けます。
今回はrequest bodyの内容はJObjectで受け取ります。
リクエストボディの文字列がバリデーションに必要ですが、ToString(Formatting.None)で取得します。
フォーマットを指定しないと、余分な空白などが設定されて、SHA256ハッシュ値があわなくなりますのでご注意ください。
Startup.cs
今回はサービスクラスから、HttpContextにアクセスし、ヘッダを取得します。
services.AddHttpContextAccessor();を指定することにより可能になります。
publicvoidConfigureServices(IServiceCollectionservices){// (略)services.AddScoped<IHubSpotService,HubSpotService>();services.AddHttpContextAccessor();}HubSpotService.cs
Hubspotから行われるウェブフックリクエストに格納されているsignatureは全て小文字になります。
そのため、SHA256のハッシュ値をToLower()で小文字にしたものと比較します。
また、ヘッダのバージョンによってハッシュ値の生成が異なるので、実際はバージョンによって処理を分岐させたほうがよいでしょう。
本記事では、V2前提の記述となります。
(記事執筆時点では、V2のリクエストが投げられる)
https://developers.hubspot.com/docs/faq/validating-requests-from-hubspot
publicclassHubSpotService:IHubSpotService{privatereadonlyIHttpContextAccessor_httpContextAccessor;publicHubSpotService(IHttpContextAccessorhttpContextAccessor{_httpContextAccessor=httpContextAccessor;}publicboolValidRequestHeader(stringrequestBody){if(!_httpContextAccessor.HttpContext.Request.Headers.TryGetValue("X-HubSpot-Signature",outStringValuessignature)){thrownewNotImplementedException();}if(!_httpContextAccessor.HttpContext.Request.Headers.ContainsKey("X-HubSpot-Signature-Version")){thrownewNotImplementedException();}// hubspotアプリのclientsecretを指定。(e.g. 00000000-1111-2222-3333-444444444444)stringsecretId="hubspot-client-secret";stringmethod="POST";// ワークフローで指定したウェブフックURLを指定stringurl="https://hogehoge.com/webhook";byte[]bytes=Encoding.UTF8.GetBytes(secretId+method+url+requestBody);varcrypto=newSHA256CryptoServiceProvider();byte[]hashValue=crypto.ComputeHash(bytes);crypto.Clear();varresult=newStringBuilder();foreach(bytebinhashValue){result.Append(b.ToString("X2"));}returnsignature.Equals(result.ToString().ToLower());}今回はハッシュ値を取得する部分をベタ書きしていますが、関数化したりするなど実際は綺麗に書いたほうがよいでしょう。
V2の場合は「クライアントシークレット+"POST"+ウェブフックURL+リクエストボディ」をSHA256でハッシュ化した値と比較します。
ちなみにV1の場合はもう少しシンプルです。
まとめ
- Hubspotからのウェブフックリクエストヘッダ(X-HubSpot-Signature)に入っているSHA256ハッシュ値は小文字
- JObjectで処理する場合は、Formatting.Noneを指定して文字列を取得する
- V2の場合は「クライアントシークレット+"POST"+ウェブフックURL+リクエストボディ」のハッシュ値と比較