くろみーの日報テンプレ

日常のつぶやき

SlackのSocketモードに関する覚書

個人的に開発しているtbls-ask-agent-slackという Slackbot があるのだが、先日、退職された P さんという方からこんな質問が来た。

コード内に Signing Secret を使っている箇所がないが、リクエストの検証は正しく行われているのだろうか?という質問だった。

もう少し噛み砕い説明すると、例えばこのブログに書かれているように、Slack アプリはリクエストの検証のためにSlack Signing Secretを使うことが推奨されており、これがないと全世界からのリクエストを受け付ける状態になってしまい、あまりよろしくない。

最初にこの質問を受けたときは、なるほど、と思ったが、その後色々調べていくうちに、Socket モードを使っている場合はSlack Signing Secretが不要であることがわかったのでメモしておく。

Signing Secret の役割

まず最初に Slack Signing Secret の役割について整理しておく。主に前掲のブログの焼き増しになってしまうが、Slack Signing Secret の役割は HTTP リクエストの正当性を検証することである。

Slack からのリクエストには X-Slack-Request-TimestampX-Slack-Signature というパラメータがヘッダーに含まれており、Slack アプリ側は自身が持っているSigning Secretを使って、リクエストボディとタイムスタンプを使って生成されるハッシュがX-Slack-Signatureと一致するかどうかを検証することができる。

これの何が嬉しいかというと、同じハッシュキーを共有している Slack からのリクエストに対しては、同じ内容に対して同じキーを使ってハッシュを生成しているので必然的に一致する。一方で、自身が信頼していない送信元からのリクエストに対しては、同じ内容に対してハッシュ化を行っているが、ハッシュキーが異なるため、一致しない。

非常にシンプルな仕組みでありながら、ハッシュキーそのものは通信経路上に流れることはないため、安全にリクエストの検証を行うことができる。

Socket モードとは

そしてここから Socket モードの話に入ってくるのだが、大事な観点として、Slack の Socket モードは HTTP リクエストではないということを覚えておく必要がある。

先日 WebSocket の実装についての gurasan の資料がはてブで流れてきたが、まさにこれが今回の話ドンピシャの内容だった。

speakerdeck.com

Socket モードには HTTP リクエストと異なり、リクエスト・レスポンスという概念がない。つまり、通信の正当性はリクエストに対して行われるのではなく、接続そのものに対して行われることになる。

Socket モードでのリクエスト検証

前掲の資料にもあるとおり、Socketモードにおけるリクエスト検証は最初の接続確立時(ハンドシェイク)に行われる。ここで使われるのが Signing Secretと対になる概念であるSlack App Tokenである。

Slack アプリ側はこのSlack App Tokenを初回接続時のBearer Tokenに指定することで、Slack 側が正当なアプリであることを検証することができる(ref)。

つまり、HTTP リクエストでは Slack アプリ側がリクエストの検証をしていたのに対して、Socket モードを使ったアプリでは Slack 側がリクエストの検証を行うことで通信の正当性を担保しているということだ。

その後の対応

というわけで、最初にお伝えした通り、私が開発しているtbls-ask-agent-slack は Socket モードを使っているため、Slack Signing Secretは不要である。

とはいえたしかに何もドキュメントに書いていないのは不親切だなと思ったので、レポジトリの README にその旨を追記しておいた。

https://github.com/kromiii/tbls-ask-agent-slack/pull/79/files

というわけで、Socket モードを使っている場合はSlack Signing Secretは不要であることを覚えておこう。

気づきを与えてくれた P さんに感謝。