Webhookの運用
Webhookは、ある特定の操作が行われた時に、予め登録しておいた送信先URLに対して、操作に関連するデータを自動送信する仕組みです。
Webhookを活用することで、SynViz S2と外部システムの連携等を行うことができます。
ここでは、Webhookに関する操作について説明します。
このセクションの内容は以下のとおりです。
システム構成イメージ
SynViz S2が想定する、Webhookを利用する際のシステム構成イメージは以下のとおりです。
SynViz S2と外部システムの間に、「中間サーバ」を設置します。
中間サーバの役割
中間サーバの役割を以下に示します。
SynViz S2から送信されるWebhookデータを外部システムが想定するデータ形式へ変換
一般に、SynViz S2が送信するWebhookデータを基に外部システムにデータを作成する場合、外部システムが提供するWeb APIを実行します。
そのWeb APIとWebhookが送信するデータの形式に乖離がある場合、外部システム側でWeb API実行エラーが発生します。
これを防ぐために、中間サーバを用いて、Web APIが想定するデータ形式に変換する必要があります。
不要なWebhookデータの間引き
Webhookから送信される全てのデータが、外部システムにとって必要なものとは限りません。
中間サーバで不要なデータを間引く処理を行うことで、外部システムへの負荷を下げることができます。
外部システムに対する認証
Webhookはデータの送信処理のみを実施し、認証処理は行いません。
そのため、認証を必要とする外部システムと連携する場合は、中間サーバにおいて認証処理を実施する必要があります。
署名の検証による送信元の確認
送信先URL情報が漏洩している場合、悪意を持つ人から不正なデータ送信が行われる場合があります。
送られてくるデータがSynViz S2からのものであることを保証するために、一般に署名検証を行うことが推奨されています。 署名検証の際に、Webhook作成時に発行されたシークレットトークンを使用します。
外部システムとの連携
外部システムが想定するデータ形式のデータを用いて外部システムのWeb APIを実行し、外部システムとの連携を実現します。
中間サーバ構築手順
「中間サーバの役割」で示した役割が果たせるのであれば、中間サーバに関する制約はありません。 別途サーバを用意し、それを中間サーバとすることも可能であり、クラウドサービス等を利用して中間サーバを用意することも可能です。
ここでは一例として、Amazon Web Services(以降、AWS)が提供するAmazon API GatewayとAWS Lambdaを組み合わせた中間サーバの実装方法を説明します。
他の方法で中間サーバを構築する場合であっても、中間サーバで行う処理の実装例などは参考にできる場合があるため、 適宜本書をご活用ください。
構成イメージは以下のとおりです。
Amazon API Gatewayは、APIの作成や管理に役立つサービスです。 このサービスを利用することにより、外部からリクエストを受け付けるためのURLを得ることができます。
AWS Lambdaは、サーバを管理することなくコードを実行できるサービスです。 プログラムをアップロードし、実行条件を設定するだけで、プログラムが実行できるようになります。
Amazon API GatewayでWebhookデータを受け取りAWS Lambdaを実行、AWS Lambdaでデータ変換等の処理を行い、 外部システムのAPIを実行することで、SynViz S2と外部システムの連携を実現します。
注意
本内容は、2025年1月時点の情報に基づいて記載しています。Amazon API GatewayやAWS Lambdaの仕様変更により、 画面構成や設定項目などに変更が生じている場合があります。
注意
本内容は、AWSを契約してあることを前提として記載しています。
また、発生料金に関する責任は負いかねます。 Webhookに設定した操作が行われるたびにAmazon API Gatewayにリクエストが送信されるため、 SynViz S2の主な利用用途や通信データ量などを評価し、AWSの料金表をご確認の上ご利用ください。
AWS Lambdaの準備
以下では、Webhookデータの処理や、外部システムのAPI実行を行うAWS Lambdaの作成手順概要を説明します。
AWSのLambdaのサービスページにアクセスします。
「関数の作成」ボタンを押下します。
必要事項を入力します。
処理を一から記述する場合、オプションは「一から作成」を選択してください。
ランタイムは、使用するプログラム言語に合わせて選択してください(Webhook機能を利用する上で、特に言語の制約はありません) なお本書は、Node.jsを使用する前提でプログラム例を記載しています。
利用用途に応じてデフォルトの実行ロールを選択し、アクセス権限を設定してください。
「関数の作成」ボタンを押下します。
上記の操作を行うとLambda関数が新規作成され、作成したLambda関数の詳細ページが自動で表示されます。 コードタブに移動し、Lambda関数の処理内容を記述します。
または、ローカル環境でプログラムを実装し、zipファイル形式でアップロードすることも可能です。
プログラムの実装またはzipファイルのアップロード後、「Deploy」ボタンを押下することにより、プログラムが実行可能な状態になります。
注意
Windows標準のzip機能でzipファイルを作成すると、アップロード後、AWS Lambdaの画面でエラーが表示され、ファイルが開けない場合があります。 その場合は、他のツールを使用する等、別な方法でzipファイルを作成してください。
注意
オープンソースソフトウェアを使用する場合、実装したプログラムと併せてアップロードする必要があります。 例として、ランタイムにNode.jsを選択した場合は、node_modulesフォルダもzipファイルに含めてアップロードしてください。
中間サーバ実装例
以下に、Node.jsによるプログラムの実装例を示します。
const crypto = require('crypto');
const axios = require("axios");
const https = require('https');
// API実行URL(※実際の値に変更してください)
const TARGET_URL = 'https://********************************';
// アプリケーションキー(※実際の値に変更してください)
const APP_KEY = '*******************************';
// シグネチャ検証に使用するシークレットトークン(※実際の値に変更してください)
const SECRET_TOKEN = '*************************************************';
exports.handler = async function(event) {
try {
// 署名取得
const signature = event.headers['X-S2-Signature'];
// 署名検証
const result = checkSignature(signature, event.body);
// 署名検証結果エラーなら処理終了
if (!result) {
console.log('Signature is invalid.');
return {
statusCode: 403,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify('Signature is invalid.')
};
}
const inputData = JSON.parse(event.body);
const eventData = inputData.EventData;
// APIの想定するデータ形式にデータ変換
const sendData = {
ID: eventData.ID,
Name: eventData.Name,
Code: eventData.Code,
StartDate: eventData.DerivedProperties.StartDate,
FinishDate: eventData.DerivedProperties.FinishDate
};
// 外部システムへの認証を実施するためのヘッダを付与
const headers = {
'X-S2-Authenticate': APP_KEY
};
// 外部システムとの連携
await axios({
url: TARGET_URL,
method: 'POST',
headers: headers,
responseType: 'json',
data: sendData
});
// 正常終了
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ message: 'Success'})
};
} catch(error) {
console.log(error);
return {
statusCode: 500,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(error)
};
}
}
/**
* シグネチャを検証する。
*
* @param {string} receivedSignature 受信したシグネシャ
* @param {object} requestBody リクエストボディ
* @returns 検証結果
*/
function checkSignature(receivedSignature, requestBody) {
const generateHMACSignature = (secret, payload) => {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload, 'utf8');
return hmac.digest('base64');
};
// 署名の検証
const hmacSignature = generateHMACSignature(SECRET_TOKEN, requestBody);
if (hmacSignature == receivedSignature) {
return true;
} else {
return false;
}
}
注意
上記実装例は、ランタイムにNode.js 22.xを指定した想定のプログラム例です。 異なるバージョンのNode.jsを使用する場合、仕様変更などにより正しく動作しない場合があります。
注意
Lambda関数作成時にNode.js以外のランタイムを選択した場合や、AWSを使わずに中間サーバを実現する場合は、 JavaScript以外の言語で実装することも可能です。
注意
上記実装例では、オープンソースソフトウェアのaxiosを利用しています。 記載内容を参考に中間サーバの処理を実装する場合は、事前にnpm install等によりaxiosをインストールしてください。 また、上記実装例ではv1.7.7のaxiosを使用しています。 異なるバージョンのaxiosを使用する場合、仕様変更などにより正しく動作しない場合があります。
httpsとcryptoはNode.jsの標準モジュールのため、インストール作業は不要です。
なお、オープンソースソフトウェアの利用によって問題が生じた場合の責任は負いかねます。
注意
本記載内容は、Amazon API Gatewayでプロキシ統合を使用することを想定しています。 同様にAmazon API Gatewayでプロキシ統合を使用する場合は、必ずレスポンスが以下の形式になるように実装してください。
{
statusCode: {number},
headers: {Object},
body: {string}
}
データ変換
SynViz S2が送信するWebhookデータを、外部システムが想定するデータ形式に変換する処理を行います。
Webhookデータには、元となる操作に関わらず共通で含まれる属性と、操作によって中身が異なるEventData属性があります。 以下に、EventDataから必要なデータを取り出し、外部システムのWebAPI用にデータを加工する例を示します。
const sendData = {
ID: eventData.ID,
Name: eventData.Name,
Code: eventData.Code,
StartDate: eventData.DerivedProperties.StartDate,
FinishDate: eventData.DerivedProperties.FinishDate
};
上記例では、EventDataの中身のみをWebAPI実行時のリクエストデータとして使用していますが、 共通属性の値を使うことも、中間サーバで独自に計算した値を使用することも可能です。
不要なWebhookデータの間引き
Webhookから送信される全てのデータが、外部システムにとって必要なものとは限りません。 特に、スケジュール変更イベントを対象とするWebhookの場合、関心のないオブジェクトの変更情報が送信される場合があります。
Webhookが実行されるきっかけとなったイベントの種類を表すEventTypeや各種操作に関連する固有データのEventDataの中身を利用し、 必要に応じてデータの間引き処理を実施してください。
以下に、データ間引き処理の実装例を示します。
// ※スケジュール変更イベントを想定
const inputData = JSON.parse(event.body);
const activityEventData = inputData.EventData.ActivityEventData;
activityEventData.forEach((elem) => {
if (elem.EventType == 'Activity_Updated') {
// アクティビティ更新の場合は何もしない
return;
}
// 外部システムとの連携処理(Web API実行など)
// ...
));
外部システムに対する認証
外部システムとの連携には、認証が必要な場合があります。 認証方法は、外部システムによって異なります。
以下に、APIキーをヘッダに付与する認証を行う場合のヘッダ定義例を示します。
// 外部システムへの認証を実施するためのヘッダを付与
const headers = {
// 外部システムがSynViz S2である場合の例。指定するヘッダキーは、連携先のシステムによって異なります
'X-S2-Authenticate': APP_KEY
};
署名の検証
WebhookデータがSynViz S2から送信されたものであることを保証するために、署名検証を行います。
署名の検証は必須の処理ではありませんが、SynViz S2以外から偽装されたリクエストが送信される可能性があるため、 セキュリティの観点から、実施することを推奨します。
SynViz S2が送信するWebhookデータにはヘッダ「X-S2-Signature」が含まれています。 X-S2-Signatureには、送信データとシークレットトークンから計算したハッシュ値が設定されています。 これを取り出し、中間サーバでも同様の計算を行い、値が一致するかどうかを検証することにより、署名検証を行うことができます。
署名が一致した場合は、SynViz S2からの送信データであることが確認できます。 不一致になった場合は、SynViz S2以外からの送信データである可能性があるため、処理を中断することが推奨されます。
署名検証の実装例を以下に示します。
// 署名取得
const signature = event.headers['X-S2-Signature'];
// 署名検証
const result = checkSignature(signature, event.body);
// 署名検証結果エラーなら処理終了
if (!result) {
console.log('Signature is invalid.');
return {
statusCode: 403,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify('Signature is invalid.')
};
}
署名検証を行う関数「checkSignature」の実装例を以下に示します。
/**
* シグネチャを検証。
*
* @param {string} receivedHash 受信したハッシュ値
* @param {object} requestBody リクエストボディ
* @returns 検証結果
*/
function checkSignature(receivedHash, requestBody) {
const generateHMACSignature = (secret, payload) => {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload, 'utf8');
return hmac.digest('base64');
};
// 署名の検証
const hmacSignature = generateHMACSignature(SECRET_TOKEN, requestBody);
if (hmacSignature == receivedHash) {
return true;
} else {
return false;
}
}
SECRET_TOKENには、SynViz S2でWebhook新規作成時 / シークレットトークン再発行時に表示されるシークレットトークンを設定してください。
注意
SynViz S2から送信されるハッシュ値の計算には、HMAC-SHA256を使用しています。 ハッシュ値の計算の際には、必ずHMAC-SHA256をご利用ください。
注意
ハッシュ値の計算に使用する受信データは、必ずプレーンテキスト状態のデータを使用してください。 JSON.parseなどを実施した後のデータでハッシュ値を計算した場合、数値データの精度の不一致、 オブジェクトデータの属性の並び順の不一致などを理由に、ハッシュ値が一致しない場合があります。
外部システムとの連携
外部システムと連携するために、外部システムのWeb APIを実行します。 以下に、axiosを使用して外部システムのWeb APIを実行する例を示します。
// 外部システムとの連携
await axios({
url: TARGET_URL,
method: 'POST',
headers: headers,
responseType: 'json',
data: sendData
});
注意
上記例では、axiosを使用して外部システムとの連携を行っていますが、他のHTTPクライアントを使用しても問題ありません。
Amazon API Gatewayの準備
以下では、Webhookデータを受け取り、AWS Lambdaを実行するためのAmazon API Gatewayの作成手順概要を説明します。
まず、大元となるAPIの作成を実施し、その後、リソースとメソッドの作成を行います。 最後に、APIをデプロイし、実行可能な状態にします。
APIの作成
AWSのAPI Gatewayのサービスページにアクセスします。
「APIを作成」ボタンを押下します。
APIタイプの中から「REST API」を選択します。
「REST APIを作成」画面で必要事項を入力します。
基本的には「新しいAPI」を選択してください。既に流用できるAPIがある場合は、それ以外の項目を選択しても問題ありません。
APIエンドポイントタイプは「プライベート」以外を選択してください。
「APIを作成」ボタンを押下します。これにより、APIが作成されます。
注釈
必要に応じてリソースポリシーを設定し、APIに対するアクセス制限を設定してください。
リソースの作成
本手順により、リクエストを受け付けるためのURLが作成されます。
API Gatewayのサービスページで、「APIの作成」で作成したAPIの名前を押下します。これにより、「リソース」画面が表示されます。
「リソースを作成」ボタンを押下します。
「リソースを作成」画面で必要事項を入力します。
「リソースを作成」ボタンを押下します。これにより、リソースが作成されます。
メソッドの作成
本手順により、Lambda関数との紐づけを行います。
「リソース」画面の項目「メソッド」内にある「メソッドを作成」を押下します。
「メソッドを作成」画面で必要事項を入力します。
メソッドタイプに「POST」を指定します。
統合タイプに「Lambda関数」を指定します。
Lambdaプロキシ統合をオンにします。
Lambda関数に、「AWS Lambdaの準備」で作成したLambda関数を指定します。
「メソッドを作成」ボタンを押下します。これにより、メソッドが作成されます。
APIのデプロイ
本手順により、作成したAPIを実行可能な状態にします。
「リソース」画面の「APIをデプロイ」ボタンを押下します。
「Deploy API」画面で必須事項を入力します。
ステージを指定する必要があるため、「新しいステージ」を選択し、新しいステージを作成してください。ステージ名は任意です。
「デプロイ」ボタンを押下します。これにより、APIがデプロイされ、実行可能な状態になります。
SynViz S2との接続
「リソース」画面で左メニューから「ステージ」を選択し、「ステージ」画面を開きます。
ツリーを展開し、作成したリソースの配下にある「POST」を押下します。これにより、API実行のためのURLが表示されます。
SynViz S2のWebhook設定画面を開きます。
Webhookの送信先URLに手順2で表示されたURLを設定します。