Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Чому серверні дії (Server Functions) у Next.js - це публічні ендпойнти і як їх захищати?

Серверна функція з 'use server' виглядає як звичайна функція, яку викликає компонент. Насправді Next.js створює для неї ендпойнт, доступний прямим POST-запитом - не лише з вашого інтерфейсу.

'use server';

export async function deletePost(postId: string) {
  await db.post.delete({ where: { id: postId } });   // будь-хто видалить будь-який пост
}

Що Next.js робить для захисту сам:

  • зашифровані недетерміновані ідентифікатори дій, що змінюються між збірками;
  • видалення невикористаних дій з клієнтського бандла;
  • шифрування змінних із замикань (значень, захоплених функцією, оголошеною всередині компонента) - вони ходять клієнтом і назад, але в зашифрованому вигляді. Ключ генерується на кожну збірку (для кількох серверів - спільний NEXT_SERVER_ACTIONS_ENCRYPTION_KEY);
  • перевірка джерела: заголовок Origin порівнюється з Host - захист від CSRF; для проксі-конфігурацій - serverActions.allowedOrigins.

Але документація прямо застерігає: це зменшує ризики, а автентифікацію й авторизацію треба перевіряти всередині кожної дії.

Правильно:

'use server';

export async function deletePost(postId: string) {
  const session = await auth();
  if (!session) throw new Error('Unauthorized');

  const post = await db.post.findUnique({ where: { id: postId } });
  if (!post || post.authorId !== session.user.id) throw new Error('Forbidden');   // захист від IDOR

  await db.post.delete({ where: { id: postId } });
  revalidatePath('/posts');
}

Типові помилки:

  • перевірка лише на сторінці: сторінка доступна лише адміну, тож «дії на ній безпечні». Ні - перевірка доступу до сторінки не поширюється на дії, оголошені в ній;
  • довіра аргументам: postId, ціна, роль приходять від клієнта й можуть бути будь-якими. Валідація (Zod) обов'язкова, як для будь-якого API;
  • повернення зайвого: повернене значення потрапляє в браузер - лише потрібні поля;
  • покладатися на шифрування замикань для секретів - документація не радить; секрети не повинні опинитися в замиканні взагалі.

Рекомендований підхід - шар доступу до даних: модуль з import 'server-only', де зібрано перевірки прав і запити до бази. Серверні дії лишаються тонкими й викликають його. Так перевірки не залежать від того, звідки викликано код.

Аналогія з Laravel: серверна дія - це маршрут контролера. Ніхто не робить контролер без authorize() лише тому, що на нього немає посилання в інтерфейсі.

Докладніше в документації: Next.js: безпека даних

Схожі питання