こんにちは。@eielhです。 id:cw-hayashiが投稿した記事である 「チャットワークのWebhookの署名検証を各言語で実装してみた」があります。 しかし、自分が利用しているプログラミング言語が網羅されていなかったので、続きがないのか確認してみたところ「自分でやってください」*1と言われてしまいました。 というわけで、早速挑戦してみました。
準備するもの
必要な準備物は「チャットワークのWebhookの署名検証を各言語で実装してみた」を参照ください。 creators-note.chatwork.com
Elixir
弊社ではAkkaを利用していますが、アクタープログラミングに挑戦してみるにはScalaはなかなかヘビーです。 そんな時でも、Elixirはライトに使えてアクタープログラミングの学習にも最適です。
$ elixir --version Erlang/OTP 20 [erts-9.1.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Elixir 1.5.2
token = "A9ne+ygvdV0IZBaPFV2zC1e5Bk+IsI14BPwieRoBQNU=" request_signature = "G7Gtrh5Ee6d8erOVXhWPtUrkNJqqIT5vwLU50KhyLQk=" request_body = ~c[{"webhook_setting_id":"246","webhook_event_type":"message_created","webhook_event_time":1511238729,"webhook_event":{"message_id":"984676321621704704","room_id":36818150,"account_id":1484814,"body":"test","send_time":1511238729,"update_time":0}}] key = Base.decode64! token expected_signature = :crypto.hmac(:sha256, key, request_body) |> Base.encode64 IO.puts expected_signature === request_signature # => true
OTPの関数を利用することになりましたが、簡単に書くことができました。
参考
Rust
弊社でRustは…オープンになっている情報はありませんね。個人的に利用しています。
$ rustc --version rustc 1.22.1 (05e2e1c41 2017-11-22)
// cargo-deps: base64, hmac, sha2 extern crate base64; extern crate hmac; extern crate sha2; fn main() { let token = "A9ne+ygvdV0IZBaPFV2zC1e5Bk+IsI14BPwieRoBQNU="; let request_signature = "G7Gtrh5Ee6d8erOVXhWPtUrkNJqqIT5vwLU50KhyLQk="; let request_body = r#"{"webhook_setting_id":"246","webhook_event_type":"message_created","webhook_event_time":1511238729,"webhook_event":{"message_id":"984676321621704704","room_id":36818150,"account_id":1484814,"body":"test","send_time":1511238729,"update_time":0}}"#; let key = base64::decode(token).unwrap(); use hmac::Mac; let mut mac = hmac::Hmac::<sha2::Sha256>::new(&key).unwrap(); mac.input(request_body.as_bytes()); let digest = mac.result(); let expect_signature = base64::encode(&digest.code().to_vec()); println!("{}", expect_signature == request_signature) // => true }
残念ながら標準ライブラリだけで完結していないです。
他の言語と比べると少し複雑ですね。
また、サンプルコードなので、惜しみなくunwrap
を利用しています。
実行にはcargo-scriptを利用しました。
参考
Haskell
弊社では、Haskellでプログラミングしてる人が…他にいるのかわかりません。 勉強している人は何人かいます。
#!/usr/bin/env stack -- stack --resolver=lts-9.14 runghc --package=base64-bytestring --package=cryptohash-cryptoapi {-# LANGUAGE OverloadedStrings #-} import Crypto.Hash.CryptoAPI (SHA256) import Crypto.HMAC (hmac, MacKey(..)) import qualified Data.ByteString as B import Data.ByteString.Base64 (encode, decodeLenient) import qualified Data.ByteString.Lazy as L import qualified Data.Serialize as S token :: B.ByteString token = "A9ne+ygvdV0IZBaPFV2zC1e5Bk+IsI14BPwieRoBQNU=" requestSignature :: B.ByteString requestSignature = "G7Gtrh5Ee6d8erOVXhWPtUrkNJqqIT5vwLU50KhyLQk=" requestBody :: L.ByteString requestBody = "{\"webhook_setting_id\":\"246\",\"webhook_event_type\":\"message_created\",\"webhook_event_time\":1511238729,\"webhook_event\":{\"message_id\":\"984676321621704704\",\"room_id\":36818150,\"account_id\":1484814,\"body\":\"test\",\"send_time\":1511238729,\"update_time\":0}}" key :: B.ByteString key = decodeLenient token digest :: SHA256 digest = hmac (MacKey key) requestBody expectBody :: B.ByteString expectBody = encode . S.encode $ digest main = print $ expectBody == requestSignature -- => True
lts-9.14を使用しました。
まったく標準ライブラリで完結していません。base64-bytestring
とcryptohash-cryptoapi
を使用しました。
コード自体はとてもシンプルですね。これを見ると、Haskell使ってみたくなりますよね。
hmac
は戻り値の型でアルゴリズムを指定できます。
参考
まとめ
どの言語も標準ライブラリをつかって簡単に検証できるわけではないようです。
みなさんも好きな言語をつかってチャットワークのWebhookをつかったサービスを作ってみてください!
*1:正確には遠回しにしか聞いていません