はじめに
こんにちは。プロダクト基盤本部 基盤グループで EM をやっている松本 @upinetree です。普段は ID 基盤を作っています。
さて、先日 6月29日(水)にAWS 主催の「インメモリデータベースで実現する超高速データベース活用セミナー」が開催されました。縁がありまして、私と、リテール本部 SRE グループの角田さんの2人で、STORES のセッションストアを Cookie から Amazon MemoryDB for Redis に移行した事例をお話してきました。ちなみに、登壇の準備や必要なやり取りなどはCTO室の小室さんが取りまとめをしてくれました。
こちらの記事で予告したイベントです。
資料はこちらです。
https://speakerdeck.com/upinetree/memorydb-for-stores-sessionstore
私は前半パートを担当しまして、セッションストアの移行の背景と、移行戦略についてお話しました。本稿では前編ということで、私の話した内容について簡単にご紹介します。
角田さんパートの後編もぜひ合わせてご覧ください! tech.hey.jp
セッションストアを MemoryDB へ移行する動機
今まで STORES では、セッションストアに Cookie を使ってきました。
セッションストアとしての Cookie はセキュリティ面などに懸念があるものの、 STORES では(Rails が仕組みとして備えている)暗号化により一定の安全性を確保して、高速でスケールするセッションストアとして活用してきました。
しかし、主に次の3つの動機から、他のセッションストアへと移行することになりました。
セキュリティ強化策が限られる
暗号化して一定の安全性を確保しても、リプレイ攻撃のような Cookie そのものをまるっと利用するような攻撃への対策にはなりません。アプリケーション側の処理を気をつけて実装するのはもちろんのことですが、開発チームが拡大する中で完全に徹底するのは難しく、何らかの仕組みで解決したいところです。
仕組みとしては、1回限りの値として nonce を紐付けてリクエストごとに検証する方法があります。しかしこの方法では別のストレージに検証用の値を保存する必要があり、Cookie の利点である高速さや負荷耐性が損なわれてしまいます。それはつまり、我々が Cookie をセッションストアとして用いている理由も失ってしまうことを意味します。
特定のセッションを失効させることが難しい
Cookie には有効期限がありますが、これはクライアントから送信されるものなので、任意の値に変更可能です。なので STORES では暗号化されたデータの中に独自の日時情報を格納し、それをもって有効性を判断していました。
また、サーバーでセッションを管理しないということは、サーバーが一度発行したセッションはずっと有効です。暗号化に用いる鍵を変えるなどをすれば別ですが、そうするとすべてのセッションが無効になってしまいます。ログアウトしてもクライアントで Cookie を破棄するだけで、セッションデータ自体は有効のままです。このため、いざというときにセッションを即座に失効させることができず、アカウントの乗っ取りなどへの対応には一工夫必要でした。
そのひと工夫はノウハウとして積み上げられてはいましたが、場当たり的で、運用コストが大きくなっていました。自動化はできるのですが、 nonce と同様にストレージに値を保存する必要があり、Cookie の利点を損なってしまいます。
将来複数サービス間でのセッション制御に拡張性を持たせたい
これは将来の話で、まだ何も決まっていないことなのですが、 STORES プラットフォームの各サービスを横断的に連携させるにあたっては、何らかのセッション制御が必要になるだろうという予想があります。そのような未来において、STORES が Cookie をセッションストアに利用しているがためにログアウトの連携が行えない、ということは避けたいのです。
特定のセッションを失効させるには
これまで述べてきたように、特定のセッションを失効させるには、セッションデータがサーバーサイドで管理されている必要があります。本事例では MemoryDB for Redis をセッションストアとするので、キーペア ("セッションキー ", "セッションデータ")
のようなイメージでセッションを格納することになります。
ただ、それだけでは任意のセッションを特定するのはまだ難しく、もうひと工夫必要です。なぜなら、対象のセッションキーを事前に知ることは難しく、 Redis では値による検索もできないからです。
そのため、ユーザーIDのような実際の検索に用いる値に対して、セッションとのマッピングを作成することにしました。マッピングのイメージは ("ユーザーID:セッションID", "セッションキー")
のような形です。セッションIDとセッションキーの違いは、内部用のプレーンな値か、Cookie に格納するための値か、です。これなら、 KEYS
コマンドで正規表現を使うなどして目的のセッションキーを取得できるようになります。
どのように移行したのか
ということで移行することに決まったのですが、既存のセッションを保ったまま移行するために、以下の2点において工夫をしました。
- ダブルライトによる新旧セッションストアの並行稼動
- 比較的アクセス数の少ないダッシュボード(ショップオーナーの管理画面)での実験的な移行
ということで、移行フローは以下のスライドで示す形になります。
まとめ
以上の流れにより、めでたく移行が完了したのでした。といっても実は失敗もあったのですが…その話はまた次回。後編の角田さんパートではいよいよ MemoryDB の話に入り、選定理由や導入過程、そして失敗談についても語られていきます。
なお、今回は MemoryDB の話がメインだったため Rails アプリケーション部分を具体的にどう実装したのかという話はしませんでした。Rack middleware を差し替えてごにょごにょやっているのですが、詳細は今後どこかの機会でご紹介できればと思います。いや〜それは待てない、直接聞きたい、ということがありましたら Meety もやっていますのでお気軽にお声がけください!