Open13

CSRF対策 December 2023

sanematsanemat

結論

  • Sec-Fetch-Site: same-origin or Sec-Fetch-Site: same-siteの確認
  • Cookieのsamesite=lax or samesite=strict

(Safari on iOS v16.4 or later を許容できれば) この2点で十分そう。

Sec-Fetch-Siteに絞ってはダメなら↓が最適解ぽい。

  • CORS + 全POSTリクエストを単純リクエストで無いことを強制する
  • Cookieのsamesite=lax or samesite=strict
sanematsanemat

CSRF

Cross-Site Request Forgery

典型例

  • 罠サイトを踏んだら、SNSに自分で投稿してしまう
  • 罠サイトを踏んだら、匿名掲示板に投稿してしまう
  • 自サイトのXSSから、意図しないリソースを作成してしまう

対象はブラウザユーザー

sanematsanemat

セッションベース認証とトークンベース認証

認証なし: CSRF起きる
セッションベース認証: CSRF起きる
トークンベース認証: CSRF起きない

sanematsanemat

CookieのSameSite Attribute

主眼は 3rd Party Cookieを消していくところ(に見える)
CSRFの緩和にも役立つ

cons

  • 認証なしの場合は意味がない
sanematsanemat

CSRF token (古典的なやつ)

POSTの前の画面のHTMLにCSRF tokenがあって、それを一緒にPOSTする。
サーバー側で受け取ったCSRF tokenを検証する。

sanematsanemat

CSRF token (今使うなら)

はじめのレスポンスにCSRF tokenを含むか、CSRF token用エンドポイントを用意して、クライアント側はそれを叩く。以降のリクエストに取得したCSRF tokenを合わせて送る。
サーバー側で受け取ったCSRF tokenを検証する。

sanematsanemat

Referer

リファラが自分のサイトかサーバー側で検証する。

cons

セキュリティソフトや環境によっては、Referer を残さない環境がある

sanematsanemat

CORS + 全リクエストを単純リクエストで無いことを強制する

request headerに APP-FROM-WEB: 1 をつけるなどで単純リクエストではなくする。
サーバー側でAPP-FROM-WEB: 1を検証して、単純リクエストではないことを確定させる。

pros

クライアント側サーバー側で一番実装が単純

cons

全リクエストでpriflightが走り、2度リクエストしてまあまあ無駄

sanematsanemat

Sec-Fetch-Site: same-origin or Sec-Fetch-Site: same-site

サーバー側で受け取ったSec-Fetch-Siteを検証する。

pros

  • サーバー側だけで良い
  • CSRF用の機能といえる

cons

  • Safari on iOS v16.4 or later
  • Sec-Fetch-Site を使わない or 実装していないブラウザでも、CSRFに脆弱になってもいいから使えてほしい、けどそうできない
sanematsanemat

CORS + 全POSTリクエストを単純リクエストで無いことを強制する

前提として、GET, HEADで副作用があってはいけない。

POSTのrequest headerに APP-FROM-WEB: 1 をつけるなどで単純リクエストではなくする。
サーバー側でPOSTのときはAPP-FROM-WEB: 1を検証して、POSTが単純リクエストではないことを確定させる。

不特定多数ユーザー向けにはこれが最適解にみえる。

pros

  • CORSはほぼすべてのブラウザで使える
  • CORSに対応して無くてもheaderが送れれば使える

cons

  • CORSはCSRF対策に使えるけど、厳密にはそれ用途ではない?
sanematsanemat

CORSハマったところ

preflightなしにリクエストきた場合、ブラウザにはCORSエラーが返るが、処理自体は一通り通ってしまう。(これはサーバー側実装によるかもしれない)

全リクエストがpreflight通る場合は、そこで止まる