マジかよ……
Node.jsでちょっとしたスクリプト書こうとして絶対ハマる人が出てくると思うので備忘録を書いていく
先に結論
理由は2つ
理由1、Fetch APIはそもそもres.headers.getSetCookie
で値を返さない
クライアントで次のようなコードを実行しても結果は返されません。— Set-Cookie は、ネットワーク経由で取得されたヘッダーからフィルタリングされます。
fetch("https://example.com").then((response) => { console.log(response.headers.getSetCookie()); // No header values returned });
ネットワーク経由で取得されたヘッダー
つまりWebサーバが書き込んでくれたCookie情報をフィルタリングして取り除く仕様になっている
推測だけどクロスサイトスクリプティング等で
悪意のJavaScriptコードからCookieのセッション情報を抜かれて、
不正アクセスからのログイン情報乗っ取りという二次被害が出ない配慮だと思う。
理由2、Node.jsのfetch APIがCookieに対応してない
質問者:
Node.js v20.9.0 fetch API can't get set-cookie header set by cookie-parser
質問者「新しめのNode.jsでFetch APIを使ってる最中、Cookieの値が欲しくてres.headers.getCookieSet()
を実行したのに値が取れないんだけど?」
回答者:
I believe the current behavior of fetch is correct, since undici/fetch does not provide a cookies store. It is impossible to provide the cookies for redirection, unless the user implicitly provide it on the first request.
回答者「undici/fetch は Cookie ストアを提供していないため、fetch の現在の動作は正しいと思います。ユーザーが最初のリクエストで暗黙的に Cookie を提供しない限り、リダイレクト用の Cookie を提供することは不可能です。」
調査ログ
Node.jsでCookieを使ったログインシステムにアクセスする要望が出てきた
Fetch APIのリクエストを飛ばす時にCookieを追加するやり方は腐るほど出てくるが、
Fetch APIを使った時にWebサーバーにセットして貰ったCookieを抜き出す方法がググっても全然出てこない
こっちはログイン情報を残したいんだよ!
「fetch api get cookie」で検索するとMDNの公式サイトがヒット
https://developer.mozilla.org/en-US/docs/Web/API/Headers/getSetCookie
MDNしか勝たん
でもこのHeadersとは何ぞや?
Headers について調べる
Fetch APIのリクエストとレスポンスの両方で使うクラス
ビルトインで提供されているので、Node.js下でもrequireやimportしなくても使える
リクエストとして使う場合
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
const response = await fetch("https://example.org/post", {
method: "POST",
body: JSON.stringify({ username: "example" }),
headers: myHeaders,
});
レスポンスで使う場合はres.headers
で取得することが可能
const res = await fetch("https://example.com");
console.log(response.headers.getSetCookie());
// setされたcookieは取得出来ないが、空配列の[]は受け取れる
参考URL:
- https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#making_post_requests
- https://developer.mozilla.org/en-US/docs/Web/API/Headers/getSetCookie#examples
res.headers.getSetCookie
の落とし穴
最初の落とし穴
Fetch APIは元々ブラウザの中で実行されるAPI
つまり悪意の第三者のフィッシングサイトからリバースプロキシ的にログイン処理を行い
Cookie情報を抜かれるみたいな事をされかれないので、セキュリティが不自然に固くなっているわけだ
なのでHeaders.getSetCookie()
のページをよくよく覗いてみると
それらしき記述がちらほら
冒頭をざっと和訳
Headers インターフェイスの getSetCookie()メソッドは、レスポンスに関連付けられたすべての Set-Cookie ヘッダーの値を含む配列を返します。これにより、Headers オブジェクトは、実装前には不可能だった複数の Set-Cookie ヘッダーを持つことを処理できるようになります。
このメソッドは、サーバー環境(Node。js など)での使用を目的としています。ブラウザは、Fetch 仕様で要求されているように、フロントエンド JavaScript コードが Set-Cookie ヘッダーにアクセスすることをブロックします。Fetch 仕様では、Set-Cookie を、フロントエンド コードに公開されているレスポンスからフィルタリングして除外する必要がある禁止されたレスポンス ヘッダー名として定義しています。
これらは一見Node.jsでもHeaders.getSetCookie()
で抜き出せるよと読み取れるが
このNode.jsというのはExpress等で立ち上げたWebサーバの話であり、
私の要望であるFetch APIで使う事は想像していないと思われる
更にExampleに入ってみる
上でほのめかしたように、クライアントで次のようなコードを実行しても結果は返されません。— Set-Cookie は、ネットワーク経由で取得されたヘッダーからフィルタリングされます。
fetch("https://example.com").then((response) => { console.log(response.headers.getSetCookie()); // No header values returned });
ただし、以下を使用して複数の Set-Cookie 値をクエリできます。これはサーバーでははるかに便利ですが、クライアントでも機能します。
const headers = new Headers({ "Set-Cookie": "name1=value1", }); headers.append("Set-Cookie", "name2=value2"); headers.getSetCookie(); // Returns ["name1=value1", "name2=value2"]
取れないんじゃん!
最近SPAとかでCookieでアクセスするWebサイト少ないと思ったよ
こういう用途には使えないから最初からやらないわけだ
スタフロでトドメ
質問者:
Node.js v20.9.0 fetch API can't get set-cookie header set by cookie-parser
質問者「新しめのNode.jsでFetch APIを使ってる最中、Cookieの値が欲しくてres.headers.getCookieSet()
を実行したのに値が取れないんだけど?」
回答者:
I believe the current behavior of fetch is correct, since undici/fetch does not provide a cookies store. It is impossible to provide the cookies for redirection, unless the user implicitly provide it on the first request.
回答者「undici/fetch は Cookie ストアを提供していないため、fetch の現在の動作は正しいと思います。ユーザーが最初のリクエストで暗黙的に Cookie を提供しない限り、リダイレクト用の Cookie を提供することは不可能です。」
こりゃ対応されないわ
どういうこと?
Node.jsではv19あたりでFetch APIが実装されて
特にrequireやimportしなくても使えるようになった
そのNode.jsの有り難い定義がこちら
A browser-compatible implementation of the fetch() function.
和訳: ブラウザの挙動に従うよ、MDNのリンク張っておくから見てね
薄っぺらすぎて泣いちゃう
そもそもFetch APIはブラウザに搭載してある機能であり
そのブラウザがセキュリティのために、向こう側のWebサーバが準備したHeader情報を削り落として見えなくしたというなら
Node.jsで動くFetch APIも情報取れたらおかしいよねって話
従ってNode.jsの開発者達は「どうせ削り落とされるんだから最初から実装しねえよ」で終わってるわけ
たぶんね
別ライブラリ探してみようかね
最後に
これは情報をつなぎ合わせた推測なので、
本当の所を知ってる人が居たら、こっそりコメント欄で教えてください。