home
home

[JavaScript] CSRF対策でレスポンスヘッダからtokenを取得して、fetchでのリクエストのヘッダに持たせたい

2021/9/22

thumbnail

もくじ

やりたいこと

今回やりたいのは、CSRF対策でレスポンスヘッダからtokenを取得して、fetchでのリクエストのヘッダに持たせるという処理。これは、

  • 1.CSRF対策でレスポンスヘッダからtokenを取得する処理
  • 2.fetchでのリクエストのヘッダに持たせるという処理

というふたつに分解することができる。 で、自分が今回詰まったのが、1.CSRF対策でレスポンスヘッダからtokenを取得する処理 こっちの処理。今回のケースではレスポンスヘッダのX-CSRF-TOKENというキーでtokenが渡されていた。でもresponse.headers.get('X-CSRF-TOKEN')で取得することができなかった。

エラー内容

別にエラーが出たわけではない。ただresponse.headers.get('X-CSRF-TOKEN')の返り値がnullだった。

でもレスポンスヘッダの他のContent-Languageresponse.headers.get('Content-Language')で取得できるし、Content-Typeは, response.headers.get('Content-Type')で取得できる。

なぜX-CSRF-TOKENは取得できないのか。

調査

デベロッパーツールのNetworkでレスポンスヘッダを確認すると、X-CSRF-TOKENがあって、値として token を持っている。ちゃんとレスポンスヘッダはX-CSRF-TOKENを持っているということだ。なのにresponse.headers.get('X-CSRF-TOKEN')で取得できない。困った。

ググった。出てきた。

Introduction to fetch

記事によると、fetch の mode が cors の場合は、レスポンスヘッダのCache-Control, Content-Language, Content-Type, Expires, Last-Modifiedの情報しか取得できないよということらしい。これでなぜ レスポンスのヘッダからtokenを取得できなかったのかはわかった。

じゃあ cors を利用してかつレスポンスヘッダの持っているtokenを参照する方法を調べればいい。ググった。出てきた。

なんとなく CORS がわかる...はもう終わりにする。

記事によればサーバ側で、Access-Control-Allow-Headers(フレームワークによって違う)を設定すればいいらしい。なるほど。

原因

cors のせいでresponse.headers.get() でデフォルトで許可されているヘッダの値以外を取得できないようになっていたので、サーバサイドでAccess-Control-Allow-Headers(フレームワークによって違う)を宣言して貰う必要がある。

解決策

自分はフロントエンドエンジニアなので、サーバサイドの担当者に上記の調査内容を報告して、「レスポンスヘッダのX-CSRF-TOKENの参照を許可するようにしてください」と依頼を投げた。バックエンドは Laravel を使っているらしく、サーバサイドの人から「Access-Control-Expose-Headers: X-CSRF-TOKENっていう記述追加したからためしてみて」と返ってきて、試したらちゃんと取得できた。

token を取得するときのコードはこんなかんじ。このtokenをどこかに保持しておいて、リクエストのときに使用する。

// token取得
await fetch('api/login')
  .then((response) => {
    // トークン取得 (どっかに保持しておく)
    const token = response.headers.get('X-CSRF-TOKEN');
  });
// tokenを持たせてリクエスト
const myHeaders = new Headers();
// このtokenはレスポンスのヘッダから取得してどっかに保持しておいたtoken
myHeaders.set('X-CSRF-TOKEN', token);
await fetch('api/hoge', { method: 'POST', headers: myHeaders });

あんまりセキュリティ周りとか気にしたことなかったので(大問題)今回はcsrfの勉強もできてよかった。

参考

sponsored link