あ、しんのきです

趣味とか技術系とか

Hasura の Allow List でセキュリティを強化する

しんのきです。

今回は Hasura の Allow List 機能について、業務で必要になったので調査しました。

hasura.io

この内容は先日主催した Disocrd もくもく会で取り組んだものです。

wasd-inc.connpass.com

来週もやる予定ですが、もう少しターゲットが分かりやすいように名前を変えて後ほど公開予定です!

GraphQL とセキュリティ

GaphQL は 1 つのエンドポイントに対してクライアントから送られるオペレーションによって Query で取得できるデータや実行される Mutation が変わるという性質上、仕組み的に何かしらのセキュリティ対策を施さないと悪意あるオペレーションによって大量データを取得されてしまったり意図的に重い処理を走らせてかけてサービスに負荷をかけることが可能です。

下記の記事で詳しく説明されていますが、 GraphQL のセキュリティ対策としてオペレーションの複雑度を計算して重いオペレーションを弾く方法実行可能なオペレーションを予めホワイトリストとして登録しておく方法の 2 つが知られています。

yigarashi.hatenablog.com

Hasura では Allow Llist という機能によってホワイトリスト方式のセキュリティ対策に対応しています。
(余談ですが、これからは BLM 的な意味合いでもホワイトリストよりは Allow List と呼んだ方が良さそうですね)

使い方

Hasura コンソールから Allow List を追加するには、設定画面の Allow List 選択し、 Add operation manually で画面上でオペレーションを入力するか Upload graphql file ファイルをアップロードします。

f:id:konoki_nannoki:20200822151042p:plain

ファイルアップロードを利用する際は、 Allow List のオペレーション名としてファイル名ではなく GraphQL の名前付きオペレーションの名前が使われますので、必ず名前付きオペレーションである必要があります。
オペレーション名が重複していなければ、 1 つのファイルで一括して複数のオペレーションを登録することが可能です。

スクリーンショットの注意文にも書いておりますが、 Allow List を設定しても実際にはデフォルトでは機能が有効になっておらず、有効にするには環境変数HASURA_GRAPHQL_ENABLE_ALLOWLIST=true をセットする必要があります。
公式ドキュメントでは、開発環境では Allow List を無効にして、ステージング環境や本番環境では Allow List を有効にすることが推奨されています。

hasura.io

注意点として、実際に使ってみて初めて分かったのですが、テーブルのマイグレーションパーミッションと違い hasura console コマンドを使っていても Allow List の設定は自動的にメタデータとして保存されませんでした。
これが意図的な挙動なのかどうかはわかりませんが、少なくとも現時点では追加した Allow List をローカルに保存するためには hasura metadata export コマンドを使用する必要がありそうです。

懸念点

SPA やネイティブアプリがクライアントとして API と通信している構成において、 デプロイ戦略は慎重に考えなければなりません。

クライアントと API のデプロイを極力同時に行うようにしても、ユーザーがページを開きっぱなしだったりアプリの更新を行わない場合、最新のクライアントでは使用していない古いオペレーションを使い続けてしまう可能性があるためです。

このような課題があるため GraphQL では一般的に極力後方互換性を保つような API の追加が推奨されているのですが、 Allow List 込みで考えるとこれは更にシビアになります。

つまり使わなくなった古いオペレーションもしばらく世代管理して Allow List に残しておいて、本当に必要がなくなったことが確認できてから消去するというようなデプロイ戦略が求められます。

ここで、同じ名前の名前付きオペレーションを複数登録しておけるのかというのがドキュメントを読んだ上での懸念点でした。
というのも、先ほど使い方で見たように、 Allow List とオペレーション名が密接に関係していそうだったからです。

ファイルアップロードを利用する際は、 Allow List のオペレーション名としてファイル名ではなく GraphQL の名前付きオペレーションの名前が使われますので、必ず名前付きオペレーションである必要があります。

オペレーション名が重複していなければ、 1 つのファイルで一括して複数のオペレーションを登録することが可能です。

もし同じ名前の名前付きオペレーションが登録できないのであれば、実装を進めていくにあたってオペレーションの中身をちょっと変更する度にオペレーション名も変更する必要があり面倒です。

というわけで今回、同じ名前の名前付きオペレーションを複数登録できるかどうか調査しました。

調査結果

できました。めでたし!

f:id:konoki_nannoki:20200822160826p:plain

分かりづらいですが、全く同じ内容の CurrentUser という名前付きオペレーションを別の名前で登録しています。

ということで、名前付きオペレーションの名前と Allow List の名前は別物で、独立して設定可能であることが分かりました。

運用方法の考察

Allow List のオペレーション名は自由に設定できるということがわかったのですが、ここまでニッチな部分のベストプラクティスなど英語を含めてまだどこにも転がっていなく、自分たちで運用方法を考えていく必要がありました。

自社で開発しているサービスでは Web とネイティブアプリで名前が重複した CurrentUser というオペレーションが存在します。

そこでまず <クライアント名>/<ロール名>/<オペレーション名> を基本にするのがわかりやすそうです。

web/user/CurrentUser # Web から呼び出される
native/user/CurrentUser # ネイティブアプリから呼び出される

次に世代管理ですが、 v1 v2 のようなバージョン管理だと崩壊しそうなので日付ベースの名前をつけたほうがよさそうです。

そこで使わなくなったオペレーションは <クライアント名>/<ロール名>/<オペレーション名>~<使わなくなった日時> のように名前を変更するとよさそうです。

web/user/CurrentUser # 最新版
web/user/CurrentUser~20200822 # 2020/08/22に使わなくなったオペレーション

名前に入れる日時としては、作成日時よりは使わなくなった日時をベースにしたほうが「使わなくなって 1 ヶ月以上放置されているぞ」ということに気づきやすいと思い、この命名規則を考えました。

~ という記号を使ったのは GraphQL であまり使われることがないからです。

(補足) Hasura Cloud

Hasura Cloud を使うと Allow List 機能が強化され、オペレーションが最後に実行された時間を確認できたり、ブロックしたオペレーションを確認してその場で Allow List に登録できるようになります。

hasura.io

現在 beta で提供されているというのと、機能のキャッチアップが追いつかないため採用は見送っていますが、この機能を使えば Allow List の管理がぐっと楽になりそうです

まとめ

自社サービスで Hasura を使っているのですが、 Allow List を使うとセキュリティは強化できるものの開発効率は確実に落ちますので、ギリギリまで採用を見送っておりました。

機能追加にあたって徐々にセキュリティを強化する必要が出てきたのですが、今回の調査によって運用できそうなイメージが掴めたのでさっそく使っていこうと思います。