キャッシュのセキュリティについて

川島拓海氏:ここまでキャッシュ戦略の1つの指針となるキャッシュの分類について紹介してきました。ここからは、キャッシュを導入するならば考慮する必要のある、一般的な問題についても紹介します。

まず時間を取って紹介するのが、キャッシュのセキュリティです。セキュリティを考慮することは、他のサービスと同様、キャッシュ導入の際にも非常に重要になることです。Amazonでもベストプラクティスとしても重視されています。

セキュリティで第1に考えるべきはキャッシュデータ保存時の暗号化、および通信時の暗号化の問題です。この問題に対処する方法は、転送中および保管中の暗号化をサポートしたキャッシュを用いることです。

第2に考慮すべきは、キャッシュポイズニング攻撃です。ここではこの攻撃の概要を示します。第1に、キャッシュの中にデータがなく、キャッシュがダウンストリームリソースのデータを取りに行ったと仮定します。

この時、キャッシュポイズニング攻撃では、攻撃者がキャッシュとダウンストリームリソースの通信に介入し、誤ったデータをキャッシュに送ります。これによりキャッシュは誤ったデータをダウンストリームリソースから送られてきたデータと勘違いし、保存することになります。

そしてあたかもそれが正しいデータであるかのように、ユーザーにそれを返すようになってしまいます。この攻撃は特にDNSキャッシュに誤ったサイトのURLを送って、ユーザーに誤ったURLにアクセスさせるといった場合などに用いられます。これを防ぐには、攻撃者がキャッシュとダウンストリームリソースとの通信に介入できないよう、ダウンストリームプロトコルの脆弱性をなくすことが重要です。

(スライドを示して)最後に、サイドチャネルタイミング攻撃についても触れておきます。これはリクエストに対する応答が返ってきた時間を元に、応答が早ければキャッシュ内にデータがあった、遅ければダウンストリームにデータを取りに行ったなどとユーザーが推測するものです。

キャッシュ内のデータの有無を元に、他のユーザーのリクエスト履歴やキャッシュデータの保持ルールを推測できます。他のユーザーのアクセス履歴を推定されたくない場合などは、これに対処する必要があります。これを防ぐ必要がある場合は、内部の計算時間と外部への応答時間との対応をずらし、計算時間が推測できないようにすることが必要です。

セキュリティ以外の重要な考慮事項

それではここから、キャッシュのセキュリティ以外の重要な考慮事項も見ていきます。(スライドを示して)まずはAmazonのベストプラクティスから取り上げます。

キャッシュサイズ、有効期限ポリシー、削除ポリシーといったものを慎重にテスト・検証・調整することです。

キャッシュの導入の際には、キャッシュサイズ、有効期限ポリシー、削除ポリシーを決める必要があります。

キャッシュサイズは、予測されるリクエスト量やキャッシュされたオブジェクトの分布から、理想的な値を推定します。

次に有効期限ポリシーは、キャッシュデータの保持期間(TTL)を決めるものです。これは古いデータの保持に対する許容度やデータの静的性により決定します。古いデータの保持が許されるほど、またデータの静的性が高いほど、新しいデータをダウンストリームリソースに取りに行く必要性が低く、TTLは長くなります。

削除ポリシーは、今あるキャッシュデータの中からどれを優先して保持するか決めるものです。代表的なものにLRU(Least Recently Used)やLFU(Least Frequently Used)があります。これらのポリシーについて、ここでは詳しくは触れません。

これらの適切な対応を知るためには、メトリクスを設定して実際のパフォーマンスを追跡し、逐次改善する必要があります。このようなパラメータは、キャッシュの存在意義にも関わるキャッシュヒット率を大きく左右するものなので、綿密に決める必要があります。

キャッシュヒット率がどうしても低いというなら、当然キャッシュの導入自体に疑問符がつくことになります。なお、このうちの上から2つ目、有効期限ポリシーを決めるTTLについては後でもう少し踏み込んで紹介します。

続いてのベストプラクティスとして、ダウンストリームエラーの際にキャッシュが適切に対応できるようにすることも重要です。

第1に取り上げるのがThundering Herdです。これはダウンストリームリソースへのリクエストが同時に多発した場合に起こる現象です。

このようになる例として、キャッシュを立ち上げたばかりで空の場合、キャッシュミスが頻発した場合、キャッシュがそもそも存在しない場合が考えられます。この時、リクエストの増加がそのままダウンストリームリソースの負荷の上昇につながります。結果として、ダウンストリームリソースの少数のプロセスを複数のリクエストが取り合い、リソースが使用できない状態に陥ります。

これに対する対策の1つが、リクエストの合体です。これは先ほどローカルキャッシュにおけるキャッシュの一貫性の問題や、コールドスタートの問題の解決策としても紹介したものです。複数のリクエストをまとめて送信することで、キャッシュへのリクエスト数の増加にダウンストリームリソースが影響を受けなくなります。この方法は一部のキャッシングライブラリや外部インラインキャッシュで実現できる他、既存のキャッシュの上からの実装でも実現可能です。

ここで、先ほどお話しした、有効期限ポリシーの重要な値となるTTLについてもう1歩深く踏み込んでみます。TTL利用の1つの戦略として、ソフトTTLとハードTTLの併用が挙げられます。

基本的にはソフトTTL以降にアクセスが起こったキャッシュデータは更新しますが、ダウンストリームリソースが利用できない場合にはハードTTLまでその古いデータを利用することを許容するというものです。これによりダウンストリームのエラーの時にハードTTLを利用し、Thundering Herdが起こってもダウンストリームリソース回復を待てます。

(スライドを示して)ダウンストリームのエラー時の挙動として、ソフトTTLとハードTTLの併用を紹介しました。ここでもう1つの対応策として、ネガティブキャッシュの利用を紹介します。ネガティブキャッシュはエラー応答をキャッシュしておくことを指し、これによりエラー応答を何度もダウンストリームリソースに取りに行くことを避けられます。

ソフトTTLとハードTTLの併用とネガティブキャッシュの利用、いずれかの対応策にせよ、ダウンストリームにエラーが発生した場合でもキャッシュに何かが入ってることを保証することで、ダウンストリームリソースにさらなる負荷をかけずにリクエストを処理できます。

Amazonのベストプラクティス

最後となりましたが、Amazonのベストプラクティスを再度紹介してまとめに代えます。今までお話ししてきたのが、Amazonベストプラクティスに沿った話です。

他のサービスと同様の厳密さで管理・モニタリングをすること、キャッシュが使用できない場合にサービスが回復力を持つようにすること、他のサービスのバージョニングと対応できるようにすること、セキュリティを考慮すること、キャッシュサイズ、有効期限ポリシー、削除ポリシーを慎重にテスト・検証・調整すること、ダウンストリームエラーの際にキャッシュが適切に対応できるようにすることです。

そして(スライドの)一番下に書いてあるのが、コスト、レイテンシーおよび可用性の観点から、キャッシュがそもそも必要なのかを確認することです。キャッシュは非常に大きな恩恵をもたらしてくれるものですが、運用の大変さをはじめとして多くの考慮事項があります。

そういったことをキャッシュを用いるメリットが埋まるのか適切に判断した上で導入することが大事になります。もちろんその結果としてキャッシュを導入しているAmazonのサービスチームも多くあります。

以上で私からの話を終わります。今回話したことが、みなさまのキャッシュ戦略を決める参考となれば幸いです。ありがとうございました。