🚀

CORSとServer Actionsの関係を理解する

に公開

Next.jsを学習していて、Server Actionsでfetchを使うコードを見たとき、「あれ、CORSは?」って思ったことありませんか?私は何度かありました笑。

この記事では、CORSの基本から、なぜServer ActionsではCORSを気にしなくていいのか、そしてそれが安全な理由を、できるだけわかりやすく説明します。

CORSって何を守っているの?

まず、CORSが何を守っているのかを理解しましょう。

あなたが銀行のサイト(bank.com)にログインしているとします。ブラウザには、あなたのログイン情報(Cookie)が保存されています。

ここで、悪意のあるサイト(evil.com)を開いてしまったとします。このサイトには、こんなJavaScriptが仕込まれています。

// evil.comのJavaScript
fetch('https://bank.com/api/transfer', {
  method: 'POST',
  body: JSON.stringify({
    to: '攻撃者の口座',
    amount: 100000
  })
});

もしCORSがなかったら、このコードは動いてしまいます。なぜなら、ブラウザはbank.comへのリクエストに、あなたのCookieを自動的に添付してしまうからです。

つまり、あなたのブラウザから、あなたの認証情報を使って、勝手に送金されるわけです。

これを防ぐのがCORSです。

悪意のあるサイト(evil.com)のJavaScript
↓
あなたのブラウザ「bank.comにリクエストを送ろうとしてるな...」
↓
ブラウザ「bank.comはevil.comを許可してない!」
↓
ブラウザ「レスポンスをJavaScriptに渡さない!」

CORSはあなたのブラウザに保存されている認証情報が、悪意のあるサイトに悪用されないように守る仕組みなんです。

Server Actionsの場合は何が違うの?

Next.jsのServer Actionsを使う場合、fetchはあなたのサーバーから実行されます。

"use server";

export async function fetchData() {
  const response = await fetch('https://example.com/api/data');
  const data = await response.json();
  return data;
}

この場合の流れはこうなります。

ユーザーのブラウザ
↓
あなたのNext.jsサーバー
↓
外部サイト(example.com)

ここで重要なのは、ユーザーのブラウザから直接外部サイトにアクセスしていないということです。

fetchを実行しているのはあなたのサーバーなので、ユーザーのブラウザに保存されているCookieは、外部サイトに送られません。

つまり、CORSが守ろうとしている「ユーザーの認証情報が勝手に使われる」という問題が、そもそも起きないんです。

図で理解しよう

ブラウザから直接fetchする場合(CORSの制約あり)

[ユーザーのブラウザ]
  ↓ Cookieが自動的に添付される
[外部サイト]
  ↓ CORSヘッダーで許可されていないと
[ブラウザがレスポンスをブロック]

この場合、ユーザーの認証情報が悪用されるリスクがあるので、CORSで守る必要があります。

Server Actionsを使う場合(CORSの制約なし)

[ユーザーのブラウザ]
  ↓
[あなたのNext.jsサーバー]
  ↓ サーバーからfetch(ユーザーのCookieは送られない)
[外部サイト]
  ↓
[サーバーがレスポンスを受け取る]
  ↓
[ユーザーのブラウザに返す]

この場合、外部サイトにアクセスするのはあなたのサーバーなので、ユーザーの認証情報は関係ありません。だからCORSの制約がないんです。

じゃあServer Actionsは完全に安全なの?

いいえ、Server Actionsには別のセキュリティ考慮点があります。

CORSは「ユーザーのブラウザ」を守る仕組みでしたが、Server Actionsで守るべきは「あなたのサーバーとデータ」です。

例えば、こんなコードは危険です。

"use server";

export async function deleteUser(userId) {
  // 誰がこの関数を呼んでいるかチェックしていない!
  await db.query('DELETE FROM users WHERE id = ?', [userId]);
}

このコードには認証チェックがないので、悪意のあるユーザーが誰でも他人のアカウントを削除できてしまいます。

Server Actionsを使うときは、こんなことに気をつける必要があります。

  • 誰がこの関数を呼び出しているのか認証する
  • その人がこの操作をする権限があるか確認する
  • 機密情報を不用意に返さない
  • レート制限を設ける(連続で大量にリクエストされないようにする)

User-Agentの話

ついでに、最初に見たコードにあったUser-Agentの設定についても説明しておきます。

const response = await fetch(url, {
  headers: {
    "User-Agent": "Chrome/120.0.0.0 Safari/537.36 ..."
  }
});

一部のWebサイトは、User-Agentがないリクエストや、明らかにボットからのリクエストをブロックすることがあります。

なので、ブラウザのふりをすることで、スクレイピングを成功させやすくしているんです。

ただし、これができるのはサーバーサイドだけです。ブラウザ上のJavaScriptからは、User-Agentは変更できません(ブラウザが自動的に設定します)。

まとめ

  • CORSは「ユーザーのブラウザに保存された認証情報が悪用されないように守る」仕組み
  • Server Actionsはサーバーからfetchするので、ユーザーの認証情報は関係ない
  • だからCORSの制約を受けない
  • でもServer Actionsには別のセキュリティ設計が必要(認証・認可・レート制限など)

CORSとServer Actionsの関係、理解できたでしょうか?

Web開発では、「何を守るための仕組みなのか」を理解することが、とても重要です。そうすれば、どんな場面でどんなセキュリティ対策が必要なのか、自然と見えてくるはずです。

頑張ってください!

Discussion