はじめに
先日、LINEのwebhookをAPI Gatewayで作成し、SQSにシグネチャ、本文を送ろうとした時にかなり躓いたので、その時にやっとことをまとめようと思います。
概要
LINEで用いられているシグネチャであるx-line-signatureは、signatureと本文が一致しているかを検証するために使用されます。
今回はAPI GatewayからSQSにシグネチャを送信し、ポーリング先で検証を行うことが必要であったため、検証可能なメッセージのフォーマットでSQSに送る必要がありました。
しかし、JSON形式のオブジェクトは、キーの順序が保証されていないため、構造体などに変換すると順序が入れ替わってしまい、検証がうまくいかない可能性があります。
色々調査したところ、AWS Lambdaを使用してAPI Gatewayで受けたリクエストのシグネチャを検証する方法についての情報は多く見かけたのですが、SQSからのメッセージをポーリングした後に検証する方法についてはほとんどありませんでした。
話さないこと
・API Gateway、SQSの作成方法
・API GatewayからSQSにメッセージを送る設定方法
・具体的なシグネチャの検証方法
LINEのwebhookで今回想定するデータ
LINEのWebhookから送られてくるデータは、以下のような構造であると仮定しています。
x-line-signature
"x-line-signature" : "signature"
本文
{
"body1" : "body1",
"body2" : "body2"
}
テンプレートマッピングの設定(失敗例1)
まず最初に、次のように本文をそのまま送るテンプレートマッピングを試みました。
Action=SendMessage&MessageBody={
"signature": "$input.params().header.get('x-line-signature')",
"body": $input.body
}
しかし、この方法ではbodyがオブジェクトとして認識され、構造体に変換した際に順序が入れ替わる可能性があります。結果として、SQSに送信されるメッセージの形式は次のようになる可能性があります。
{
"signature" : "signature"
"body" : {
"body2" : "body2",
"body1" : "body1"
}
}
この形式では、x-line-signatureの検証は失敗してしまいます。
テンプレートマッピングの設定(失敗例2)
次に、bodyを文字列として送信しようと試みました。
Action=SendMessage&MessageBody={
"signature": "$input.params().header.get('x-line-signature')",
"body": "$input.body"
}
しかし、この方法ではbodyがオブジェクトとして認識され、文字列の中にダブルクォーテーションが使われることになります。
{
"signature" : "signature"
"body" : "{
"body1" : "body1",
"body2" : "body2"
}"
}
文字列の中にダブルクォーテーションが使われることになるので、この状態では当然エラーとなってしまいます。
テンプレートマッピングの設定(成功例)
最後に、成功例として以下のテンプレートマッピングを試みました。
Action=SendMessage&MessageBody={
"signature": "$input.params().header.get('x-line-signature')",
"body": "$util.escapeJavaScript($input.body)"
}
この設定では、$util.escapeJavaScriptを使用してダブルクォーテーションをエスケープし、bodyを文字列として送信します。
{
"signature" : "signature"
"body" : "{
\"body1\" : \"body1\",
\"body2\" : \"body2\"
}"
}
これにより、構造体に変換する際にbodyが文字列として認識され、検証が可能となります。
最後に
API Gateway→Lambda→SQSの流れでLambdaでwebhookのシグネチャの検証を行う方法もありますが、わざわざLambdaを使うのももったいないですよね。ポーリング先で検証を行う環境があれば本記事の内容を参考にしてみてください。
ご覧いただきありがとうございました!