Zenn
🐩

CSRFをおさらいする[セキュリティ攻撃]

2025/03/19に公開

背景

XSSの記事を書いた背景と同様です。

この前急に「XSSって何?」と聞かれた時に100%正しいと思える回答ができませんでした。
そこで主要なセキュリティ攻撃は言語化できるようにしようと思い、アウトプットすることにしました。

今回はCSRFについてアウトプットします。

CSRFとは

ユーザーのログインしたセッション情報を悪用し、当人になりすまして不正リクエストを送信してWebアプリ内で不正な操作をする攻撃です。

例えば、ユーザーがログインした状態で攻撃者が用意した悪意のあるページを開いたり、不正なリンクを踏んだりすることで、ユーザー本人が意図しない操作(例えば、パスワード変更や送金など)を行ってしまう可能性があります

ユーザーが発行した正しいセッションを使うので、ぱっと見正しいユーザーが操作しているように見えるので、とても悪質です。

具体的な攻撃イメージ

  • 例えば、銀行のWebサイトがあるとする。
  • ユーザーはサイトにログインした後、ログアウトせずにサイトを離脱する。
  • その状態で、フィッシングメールなどで悪意のあるサイトのリンクを踏む。
  • リンク先で、ログイン時にユーザーが取得したセッションで以下のようなJavaScriptが動き、不正な送金がされる
html
<form action="https://bank.example.com/transfer" method="POST">
  <input type="hidden" name="amount" value="100000">
  <input type="hidden" name="to_account" value="999999">
  <input type="submit" value="送金">
</form>

<script>
  document.forms[0].submit();  // ページを開いたら自動送信
</script>

その他の攻撃方法としては、セッションを使ってパスワードを変えたり、SNSに悪戯の投稿をしたりなどがあります。

https://www.aeyescan.jp/blog/cross-site_request_forgeries/#toc2

XSSとCSRFの違い

XSSは攻撃対象に対してスクリプトを埋め込み、CSRFは攻撃対象に対してリクエストするという違いです。

CSRFの対策

CSRFの対策は限定的

CSRFは、ユーザーのセッション情報を使ってユーザーの不正な操作を行う攻撃なので、ログイン中のユーザー自身のみができる操作に対して対策すれば良い。

対策1 CSRFトークンを発行する

古来からある方法らしい。
サーバーからCSRFトークンというトークンを発行して、該当のフォーム画面に埋め込む(hiddenのinputとかで仕込む)。フォーム送信時に、トークンも一緒に送信される。

攻撃者のサイトからだと、このトークンは取得されない。

しかし、この方法は、トークンをDBに保持したりしてなかなかめんどくさい。自前実装することは今時あまりなく、例えばLaravelだと@csrfとコードを書けばあとはLaravelが良しなにやってくれるらしい。

対策2 CookieにSamesite属性をつける

セッションを保存しているのがCookieだったら、CookieにSamesite属性をつけるのは良い方法

別ドメインからCookieが送信されなくなる。

Samesiteと判断する基準は以下が参考になる。サブドメインもSamesite扱いとのこと
https://qiita.com/piggydev/items/bc545bb92d63fe39d8b7#samesitesamesite-value-省略可

対策3 Originヘッダーを使う

ブラウザがリクエストする際に、、自動的にリクエスト元のオリジン(スキーム + ホスト + ポート)を含めるヘッダー

意図しているOriginからリクエストが来たかAPI側で確認して、不正だったら403とかを返すイメージ

ただしOriginヘッダーがつかないリクエストもある(同一オリジンのGET/HEAD)ので、注意が必要

Next.jsではどのように対策されている?

馴染みのあるNext.jsでどのように対策されているかざっくり調べてみました。

ちょい古いがこの記事を読んだ
https://nextjs.org/blog/security-nextjs-server-components-actions

Server Actionsの対策

Server Actionsは、常にPOSTメソッドで実装されており、POST以外は受け付けません。
Same-SiteのCookieがデフォルトであるため、ほとんどのCSRFを防ぐことができるとのことです。

また、Originヘッダーを見て、リクエスト元とServer ActionsのOriginが一致しない場合はブロックしてくれるとのことです。

serverOnlyを使ってサーバーで実行するファイルをクライアントに漏らさない

serverOnlyモジュールをimportしているファイルは、クライアントコンポーネントでimport・利用できなくなります。
これでクライアントからサーバー用のファイルが漏れなくなります。

server-logic.ts
import 'server-only';

export function (){
 // serverの処理
}

ルートハンドラーは対策していないみたい

ルートハンドラーは対策していないみたいです。
自前で対策が必要とのこと。
対策方法は、この記事で紹介した対策1〜3の内容などになる。

対策が十分か確認する方法

脆弱性診断サービスで行うのが手軽で良い。
Securifyというサービスを使えば、無料で脆弱性診断の結果を見ることができる。(結果の詳細を見るには有料会員になる必要がある。)

https://www.securify.jp/

まとめ

それなりに自分の中で言語化できたかなと思います。
今時フレームワークがいろいろやってくれてるので自前実装する必要はないですが、知っておくとお客さんに聞かれたときにパッと答えられるので良いですね。

参考

https://speakerdeck.com/hiro_y/update-your-knowledge-of-csrf-protection

株式会社ソニックムーブ

Discussion

ログインするとコメントできます