Server Action은 Next.js 14+의 React Server Components가 클라이언트에서 호출 가능한 백엔드 함수로, 자동 라우팅·직렬화 덕분에 빠르지만 보안 분기가 명시적으로 강제되지 않는다.
AI 코딩 도구가 만든 코드에서 가장 자주 누락되며, 누락 시 인증 우회·데이터 탈취·서비스 마비로 직결된다.
이 글은 자주 발생하는 실수 패턴, 표준 구현 코드, 30초 자가진단, 24시간 패치 순서를 정리했다.
한눈에 보는 핵심
Q. Server Action은 일반 API와 뭐가 다른가요? A. 별도 라우트 정의 없이 함수 자체가 백엔드 엔드포인트가 되어 미들웨어 인증이 자동 적용되지 않습니다. 함수 첫줄에 인증 가드를 명시해야 합니다. Q. 'use server' 함수에 CSRF가 필요한가요? A. Next.js가 일부 CSRF 보호를 제공하지만 origin 헤더 검증을 추가하는 것이 표준입니다. Q. Server Action에 시크릿 키를 그대로 써도 되나요? A. 서버에서만 실행되지만 함수 시그니처가 클라이언트 번들에 노출되므로 환경변수와 권한 분리가 필수입니다.자주 발생하는 실수 패턴
| # | 패턴 | 위험도 |
|---|---|---|
| 1 | 함수 진입 시 auth() 체크 누락 — 미인증 호출 허용 | 치명 |
| 2 | IDOR — 함수 인자 ID 검증 없이 DB 조회 | 치명 |
| 3 | origin 헤더 검증 누락 — CSRF 위험 | 높음 |
| 4 | process.env 직접 사용 — 함수 시그니처에 노출 | 중간 |
표준 구현 코드
// app/actions.ts — 표준 가드 패턴
'use server';
import { auth } from '@/lib/auth';
import { headers } from 'next/headers';
import { prisma } from '@/lib/prisma';
export async function deleteOrder(orderId: string) {
// 1. 인증 가드 — 함수 진입 시 필수
const session = await auth();
if (!session?.user) throw new Error('UNAUTHORIZED');
// 2. origin 검증 — CSRF 차단
const origin = (await headers()).get('origin');
if (origin !== process.env.NEXT_PUBLIC_APP_URL) {
throw new Error('INVALID_ORIGIN');
}
// 3. IDOR 방어 — user 필터 강제
const order = await prisma.order.findFirst({
where: { id: orderId, userId: session.user.id }
});
if (!order) throw new Error('NOT_FOUND_OR_FORBIDDEN');
// 4. 작업 실행
await prisma.order.delete({ where: { id: orderId } });
}
핵심 4요소를 모두 충족해야 결함이 사라진다.
AI 도구는 동작 코드만 출력하므로 보안 분기는 별도로 강제해야 한다.
미들웨어/데코레이터로 모든 경로에 일괄 적용이 표준이다.
30초 자가진단
// 1. 미인증 상태로 Server Action 호출
fetch('/', { method: 'POST', headers: {'next-action': 'ACTION_ID'}, body: '[arg]' })
// 401이 아니면 결함
// 2. ID 변경 — 남의 자원 접근
deleteOrder('남의_order_id')
// 200이 떨어지면 IDOR
// 3. 다른 origin에서 호출 시도
fetch('https://yourdomain.com', { method:'POST', headers:{'origin':'https://evil.com', 'next-action':'X'} })
위 시그널이 발견되면 결함이다. Next.js Server Actions 공식 참조.
24시간 패치 절차
| 순위 | 조치 |
|---|---|
| 1 | 모든 'use server' 함수 첫줄에 auth() 가드 |
| 2 | 함수 인자 ID는 user 필터와 함께 DB 조회 |
| 3 | headers().get('origin') 검증 추가 |
| 4 | process.env는 함수 본문 안에서만 사용, 시그니처에 노출 금지 |
지금 해야 할 것
실무자: CodeScan 무료 스캔으로 30초 점검.
CISO·임원: 통합 보안 점검은 SENTRIX 30분 1:1 상담.
결함 발견 시 24시간 안에 패치 완료가 표준이다.