質問やフィードバックがありましたら、フォームからお願いします
本文は台湾華語で、ChatGPT で翻訳している記事なので、不確かな部分や間違いがあるかもしれません。ご了承ください
前言
2019年にMediumでCookieとCORSに関する記事(CookieとCORSを扱う)を公開し、好評を得ました。その記事は対岸にも転載されましたが、原作者を忘れないでほしいと思います。当時の記事はCORSとは何か、そしてフロントエンドやバックエンドでCORSを正しく実装する方法について探讨しており、ウェブ開発ではしばしば直面する問題です。
それから1年余りが経ち、ChromeはCookieポリシーの変更(SameSiteをデフォルトでlaxに変更)を発表しました。それを受けて、私はこの問題を再考し、ChromeのCookieポリシー調整と反省という記事に私の考えをまとめました。その中で、SameSite
に関する疑問を述べました。以下の2つの質問についてまだ答えを探しています。
- サーバーが
SameSite
を追加した場合、CSRF対策を実装する必要はなくなるのでしょうか?古いブラウザ(SameSite Attributeをサポートしていない)を使用することによるセキュリティ問題は、企業の責任とブラウザの責任のどちらになるのでしょうか? Cookie
を使用することは本当に完璧な方法なのでしょうか?
最初の質問については、時代の進展と共に、より多くのブラウザが追いつくと考えています。その時点でCSRF対策を完全に廃止できるかもしれません。
2つ目の質問は、いくつかのケースを見たことから生じた疑問で、ここで詳しく説明したいと思います。
その前に小テストをしてみましょう。cookieでdomain=.example.comとdomain=example.comを設定した場合の作用範囲は何ですか?
答えを見る前に、まずはクラシックなケースを見てみましょう - Cookie Bomb
1. Cookie Bombとは?💣
この記事では、GitHub Pageを使ってCookie Bombをデモンストレーションしています。
GitHubはコードを置くだけでなく、個人のウェブページやブログとしてGitHub Pageを作成することもできます。GitHubはその際にuser.github.io
というドメイン名を提供します。(初期はusername.github.com
でしたが、こちらの記事を参照してください)
一般的なブラウザでは、1つのCookie(key, value, expiresなどを含む)に最大4KBのデータを保存でき、最大50個まで保存可能です(ブラウザによって多少の差異があります)。合計で最大4KB * 50 = 200KBです。 Cookie Bombの手法は、クライアントサイドでdocument.cookie
を使って大量のCookieを書き込むことです。Cookieはデフォルトで全てのリクエストに含まれるため、200KBはGETリクエストにとって非常に大きなサイズです。そのため、通常はサーバー側またはnginxレイヤーでリクエストを無視されます。
次に大きな問題はここにあります。**Cookieは自動的に全てのサブドメインに適用される(いくつかの条件を除いて)**ため、Aページで被害を受けた場合(Cookieで満たされた後)、BページやCページでも効果が出ます。その結果、全てのGitHub Pageが壊れているように見えるのです。
厳密に言えば、このCookie Bombはセキュリティ問題を引き起こすわけではなく、ただ少しおかしなだけです。被害者は大量のCookieを削除すればサーバーがリクエストを正しく処理できるようになりますが、こうしたブログサービスのCookieが互いに共有されるのはやはり奇妙な気がします。
この問題を避けるために、特定のドメイン内のサブドメイン同士ではCookieが共有されない仕組みがあり、これをpublic suffix listと呼びます。Mollizaによって管理されており、全てのpublic suffixのドメインリストが含まれています。
先ほどの質問に戻りますが、domain=.example.com
とdomain=example.com
は同じで、全てのサブドメインに適用されます。
domainが設定されていない場合、少なくともデフォルトでは同じドメイン内に制限されますが、設定した場合は全てのサブドメインにCookieが送信されます。
2. あなたは本当にCookieがどのように機能するか知っていますか?
現在のブラウザはRFC6265の標準に従ってCookieを実装しているはずです。例えば、Cookieが同じ値に出会った場合はどう処理されるべきか?Cookieは並べ替える必要があるのでしょうか?
私の経験から言えば、Cookieのさまざまな奇妙な歴史を理解し、仕様の詳細を知っているエンジニアは非常に少ないと思います。これは非常に普通のことだと考えています。一般のエンジニアはCookieの設定方法、httpOnly
やsecure
の動作、pathやdomainのロジックを知っている程度で、これで十分だと思います。製品を開発する際に、すべての仕様書を読み尽くし、Cookieの詳細を理解する必要はないでしょう。
もしこれらの問題がすべて適切に防護されていたとしても、使用しているフレームワークがどのようにCookieを解析し、実装しているかも確認しなければ、完全な安全は得られません。
再考:別の可能性
これらのケースを考慮した結果、私は最適なアプローチは何かを考え始め、むしろJavaScript
に焦点を当てました。次にJavaScript
がどのようにこれらの問題を解決しているかを見てみましょう。
前回の記事でも触れましたが、参考にしていた記事はCookies are bad for youです。ここで復習してみましょう。
JavaScriptから送信されたリクエスト、例えばajaxやfetchはCORSリクエストとして定義されます。CORSリクエストにはいくつかの条件と制限があり、詳細は以前の記事やMDNを参照してください。ここではCORSがどのようにあなたのリクエストを保護するのかを説明します:
- クロスドメインリクエスト時、デフォルトでCookieは自動的に送信/受信されません:これによりCSRFを防止します。CORSリクエストはJavaScriptコードから発生するため、一般的なCSRF攻撃は効果を発揮しません。
- CORSはJavaScriptの実行を必要とします:認証メカニズムを
Authorization
というRequest Headerに配置できます。一般的なフォームやリンク攻撃はリクエストヘッダーに追加できないため、CSRFを効果的に防ぐことができます。 - リクエストにヘッダーを追加できます:fetchはヘッダーを動的に追加して認証メカニズムを実装できます。例えば:
fetch('/api', {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token')
}
})
- 認証がCookieメカニズムを通過しない場合:複雑なCookie仕様を理解する必要がなく、CSRFの実装問題も心配する必要がありません。
さて、皆さんが最も気になる質問が来ました:
token
をlocalStorage
に保存すると、XSSに遭った場合はどうなるのか?- ユーザーが
JavaScript
を有効にしていない場合は、動作しないのでは?
質問1については、私もまだ考えていますが、誰も「では、Cookieを使った場合にXSSが発生したら大丈夫なのか?」と答えたことがないようです。
攻撃者がトークンを取得できるかどうかが違いです。XSS後に何が起こるかを考えてみましょう:
- Cookie:XSS発生 → 攻撃者が悪意のあるコードを挿入 → データを取得
- localStorage:XSS発生 → 攻撃者がトークンを取得 → データを取得
現在の議論は第2ステップで止まっていますが、結果が同じであれば、両者の本質的な違いは何なのかについては議論されていません。これが非常に残念な部分だと思います。
皆がCookieは素晴らしく安全だと考え、localStorageには厳しく批判しますが、実際には本当にそうでしょうか?Cookieは本当に完璧なのでしょうか?仕様の複雑さが潜在的な問題を引き起こしていないでしょうか?この点についての議論はあまり見かけません。
質問2について、localStorageのメカニズムを採用する以上、何らかの代償を払う必要があると思います。実際、大半のユーザーはウェブページを閲覧するためにJavaScriptを有効にしています。JavaScriptの実行を許可しなければ、SSRを実装していないSPAウェブページはほとんど壊れてしまうでしょう。
後記
私がCookieについていくつかの疑問を持っているものの、フレームワークの実装が十分に堅牢かどうかはわかりませんが、Cookieの仕様は進化し、現在はますます安定し安全になっていると感じています。フレームワークについても、オープンソースであれば信頼するに足ると思います。SameSite
の実装はほぼ全てのブラウザに適用できるため、かなり希望の持てる未来だと思いますが、より深い議論と刺激が得られることを期待して、この文章を書きました。
以上が私自身のCookieに関する理解です。何か間違いがあればご指摘いただければ幸いです。
補足
この記事がコミュニティに公開された後、いくつかの提案がありましたので、コメント欄を見てみてください。
この記事が役に立ったと思ったら、下のリンクからコーヒーを奢ってくれると嬉しいです ☕ 私の普通の一日が輝かしいものになります ✨
☕Buy me a coffee