追記
[2017.10.17 追記]
すぐにTwitterで補足いただきました。
https://twitter.com/chibiegg/status/920156559727923200
すなわち
- incoming webhook(サーバ → sakura.io)の場合、JSONの生成は自由
- outgoing webhook(sakura.io → サーバ)の場合、受け取ったペイロード(JSON)をそのまま計算
すればよいとのこと。
(ご丁寧にありがとうございました )
特にoutgoing webhookの場合、大抵はサーバ側の実装言語でデシリアライズされているかと思うので、
元々のペイロードを計算に使うよう気をつければ大丈夫そうです。
以降、outgoing webhookのSecret検証の際に注意する項目として残しておきます。
概要
sakura.ioの連携サービスであるOutgoing WebhookおよびIncoming Webhookには
Secret を設定することができます。
Secretを用いてリクエストを検証するには、Secretと
リクエストボディから生成したハッシュ値を比較する必要がありますが、
ドキュメントから読み取れなかった部分を今回検証したので、メモとして残しておきます。
ハッシュ値の生成
以下、outgoing webhookを例に説明します。
(incoming webhookは未検証だが、おそらく同様の方法)
ドキュメントのサンプル実装より、ハッシュ値はHMACで生成されます。
- ハッシュ関数
- SHA1
- 秘密鍵
- webhookに設定したSecret
- メッセージ
- リクエストボディ(JSON)
生成されたハッシュ値が X-Sakura-Signature
ヘッダとしてリクエストに含まれます。
サーバ側でもハッシュ値の生成を行い、 X-Sakura-Signature
と比較することでリクエストを検証します。
メッセージ
ここで重要なのがメッセージ(以下、JSON)です。
- JSONの要素順
- minifyの有無
が異なると生成されるハッシュ値も異なるため、正常に検証することができません。
これらがドキュメントには記載がありませんので、実際にoutgoing webhookを送信して検証しました。
実際に送ってみる
適当なサーバ(今回はAWS EC2インスタンス)を立てて、ncでリクエストを受けてみました。
[ec2-user@ip-X-X-X-X ~]$ nc -l 8080
POST / HTTP/1.1
Host: Y.Y.Y.Y:8080
User-Agent: SAKURA-IoT-Webhook
Content-Length: 191
Content-Type: application/json
X-Sakura-Signature: 6f83b432c91b398de740b77dd6ee8fc8daeee088
Accept-Encoding: gzip
{"module":"xxxxxxxxxx","type":"channels","datetime":"2017-10-17T02:47:10.113999314Z","payload":{"channels":[{"channel":0,"type":"i","value":1,"datetime":"2017-10-17T02:47:10.114000551Z"}]}}
上記より、以下が判明しました。
- JSONの要素順(
channels
typeの場合)- module
- type
- datetime
- payload
- minifyの有無
- 有
これらを踏まえてドキュメントのサンプルコードを実行したところ、想定通りのハッシュ値を生成することができました。
#!/usr/bin/env python
import hmac
import hashlib
import requests
import json
secret = "Secret"
data = '{"module":"xxxxxxxxxx","type":"channels","datetime":"2017-10-17T02:47:10.113999314Z","payload":{"channels":[{"channel":0,"type":"i","value":1,"datetime":"2017-10-17T02:47:10.114000551Z"}]}}'
x_sakura_signature = hmac.new(secret.encode("utf-8"), data.encode("utf-8"), hashlib.sha1).hexdigest()
まとめ
Secret検証時のメッセージ(JSON)は以下に気をつける。
- 要素順
- minify
余談ですが、ドキュメントのサンプルコードは
- サンプルのJSONがminifyされていない
-
json.dumps()
でシリアライズされる順序が不定
なので、これを元に検証しようとすると苦戦します。