もくじ
やりたいこと
今回やりたいのは、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-Language
はresponse.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')
で取得できない。困った。
ググった。出てきた。
記事によると、fetch の mode が cors の場合は、レスポンスヘッダのCache-Control
, Content-Language
, Content-Type
, Expires
, Last-Modified
の情報しか取得できないよということらしい。これでなぜ レスポンスのヘッダからtokenを取得できなかったのかはわかった。
じゃあ cors を利用してかつレスポンスヘッダの持っているtokenを参照する方法を調べればいい。ググった。出てきた。
記事によればサーバ側で、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の勉強もできてよかった。