(번역) How to Think About Security in Next.js
원문: https://nextjs.org/blog/security-nextjs-server-components-actions
App router의 React 서버 컴포넌트는 기존 방식과 관련된 많은 중복성과 잠재적 위험을 제거하는 새로운 패러다임입니다. 새로운 방식이기 때문에 개발자와 후속 보안팀은 기존 보안 프로토콜을 이 모델에 맞추는 데에 어려움을 겪을 수 있습니다.
이 문서는 주의해야할 몇 가지 영역과 기본 제공되는 보호 기능을 강조하고 애플리케이션 감사를 위한 가이드를 담고 있습니다. 특히 우발적인 데이터 노출의 위험에 중점을 두고 있습니다.
데이터 처리 모델 선택하기
React 서버 컴포넌트는 서버와 클라이언트 사이의 경계를 허물어줍니다. 데이터 처리는 정보가 처리되고 이후에 제공되는 위치를 이해하는 데 있어 가장 중요합니다.
가장 먼저 해야할 일은 프로젝트에 적합한 데이터 처리 방식을 선택하는 것입니다.
- HTTP API (기존 대규모 프로젝트/조직에 권장)
- 데이터 액세스 계층 (신규 프로젝트에 권장)
- 컴포넌트 수준 데이터 엑세스 (프로토타이핑 및 학습에 권장)
한 가지 접근 방식을 고수하고 너무 많이 섞어 사용하지 않는 것이 좋습니다. 이렇게 하면 코드 베이스에서 작업하는 개발자와 보안 감사자 모두 무엇을 기대해야 하는지 명확하게 알 수 있습니다. 예외는 의심스러운 것으로 나타납니다.
HTTP APIs
기존 프로젝트에서 서버 컴포넌트를 채택하는 경우, 권장되는 접근 방식은 런타임에 서버 컴포넌트를 SSR과 같이 기본적으로 안전하지 않거나 신뢰할 수 없는 것으로 처리하거나 클라이언트 내에서 처리하는 것입니다. 따라서 내부 네트워크나 신뢰 영역에 대한 가정이 없으며 엔지니어는 Zero Trust 개념을 적용할 수 있습니다. 대신 클라이언트에서 실행하는 것처럼 서버 컴포넌트에서 fetch()를 사용하여 REST 또는 GraphQL과 같은 사용자 지정 API 엔드포인트만 호출하며, 이때 모든 쿠키도 전달됩니다.
데이터베이스에 연결되는 기존 getStaticProps
/getServerSideProps
가 있는 경우 모델을 통합하고 이를 API 엔드포인트로 이동하여 한 가지 방법으로 작업을 수행할 수 있습니다.
이 접근 방식을 사용하면 보안을 전문으로 하는 기존 백엔드 팀이 기존 보안 관행을 적용할 수 있는 기존 조직 구조를 유지할 수 있습니다. 해당 팀이 JavaScript 이외의 언어를 사용하는 경우에도 이 접근 방식에 적합합니다.
클라이언트에 보내는 코드의 양이 적고 내재된 데이터 워터폴이 짧은 지연 시간으로 실행될 수 있어 서버 컴포넌트의 많은 이점을 여전히 활용할 수 있습니다.
Data Access Layer (데이터 액세스 계층)
새 프로젝트에 권장하는 접근 방식은 JavaScript 코드베이스 내에 별도의 데이터 액세스 계층을 생성하고 모든 데이터 액세스를 그 안에 통합하는 것입니다. 이 접근 방식은 일관된 데이터 액세스를 보장하고 인증 버그가 발생할 가능성을 줄여줍니다. 또한 단일 라이브러리로 통합하기 때문에 유지 관리가 더 쉽습니다. 단일 프로그래밍 언어로 팀 결속력을 높일 수도 있습니다. 또한 런타임 오버헤드가 줄어들어 성능이 향상되고 요청의 여러 부분에서 인메모리 캐시를 공유할 수 있는 기능도 활용할 수 있습니다.
호출자에게 데이터를 제공하기 전에 사용자 지정 데이터 액세스 검사를 제공하는 내부 JavaScript 라이브러리를 구축할 수 있습니다. HTTP 엔드포인트와 유사하지만 동일한 메모리 모델을 사용합니다. 모든 API는 현재 사용자를 수락하고 데이터를 반환하기 전에 사용자가 이 데이터를 볼 수 있는지 확인해야 합니다. 서버 컴포넌트 함수 본문은 요청을 발행한 현재 사용자에게 접근 권한이 있는 데이터만 볼 수 있어야 한다는 원칙이 있습니다.
이 시점부터는 API 구현에 대한 일반적인 보안 관행이 적용됩니다.
// data/auth.tsx
import { cache } from 'react';
import { cookies } from 'next/headers';
// 캐시된 헬퍼 메서드를 사용하면 수동으로 값을 전달하지 않고도
// 여러 곳에서 동일한 값을 쉽게 얻을 수 있습니다.
// 이렇게 하면 서버 컴포넌트에서 서버 컴포넌트로 전달되는 것을 방지하여
// 클라이언트 컴포넌트로 전달되는 위험을 최소화할 수 있습니다.
export const getCurrentUser = cache(async () => {
const token = cookies().get('AUTH_TOKEN');
const decodedToken = await decryptAndValidate(token);
// 시크릿 토큰이나 개인정보를 공개 필드로 포함하지 마세요.
// 클래스를 사용하면 실수로 전체 객체가 클라이언트로 전달되는 것을 방지할 수 있습니다.
return new User(decodedToken.id);
}
// data/user-dto.tsx
import 'server-only';
import { getCurrentUser } from './auth';
function canSeeUsername(viewer: User) {
// 현재는 공개 정보이지만 변경될 수 있습니다.
return true;
}
function canSeePhoneNumber(viewer: User, team: string) {
// 프라이버시 규칙
return viewer.isAdmin || team === viewer.team;
}
export async function getProfileDTO(slug: string) {
// 값을 전달하지 않고 캐시된 값을 다시 읽고, 컨텍스트를 해결하고, 더 쉽게 게으르게 만들 수 있습니다.
// 쿼리의 안전한 템플릿을 지원하는 데이터베이스 API 사용
const [rows] = await sql`SELECT * FROM user WHERE slug = ${slug}`;
const userData = rows[0];
const currentUser = await getCurrentUser();
// 모든 데이터가 아닌 이 쿼리와 관련된 데이터만 반환합니다.
// <https://www.w3.org/2001/tag/doc/APIMinimization>
return {
username: canSeeUsername(currentUser) ? userData.username : null,
phonenumber: canSeePhoneNumber(currentUser, userData.team) ? userData.phonenumber : null,
};
}
이러한 방법은 클라이언트에 전송해도 안전한 객체를 있는 그대로 노출해야 합니다. 클라이언트가 사용할 준비가 되었음을 명확히 하기 위해 이러한 객체를 데이터 전송 객체(DTO)라고 부릅니다.
실제로는 서버 컴포넌트에서만 사용할 수 있습니다. 이렇게 하면 보안 감사는 주로 데이터 액세스 계층에 집중하고 UI는 빠르게 반복할 수 있는 계층이 만들어집니다. 표면적이 작아지고 커버해야 할 코드가 줄어들어 보안 문제를 더 쉽게 발견할 수 있습니다.
import {getProfile} from '../../data/user'
export async function Page({ params: { slug } }) {
// 이제 이 페이지는 이 프로필을 안전하게 전달할 수 있습니다.
// 민감한 내용이 포함되어서는 안된다는 것을 알 수 있습니다..
const profile = await getProfile(slug);
...
}
Secret Key는 환경 변수에 저장할 수 있지만 이 접근 방식에서는 데이터 액세스 계층만 process.env에 액세스해야 합니다.
Component Level Data Access (컴포넌트 수준 데이터 엑세스 계층)
또 다른 접근 방식은 데이터베이스 쿼리를 서버 컴포넌트에 직접 넣는 것입니다. 이 접근 방식은 빠른 반복 및 프로토타이핑에만 적합합니다. 예를 들어, 소규모 팀으로 구성된 소규모 제품에서 모든 사람이 위험과 위험에 대한 주의 방법을 알고 있는 경우입니다.
이 접근 방식에서는 "use client" 파일을 주의 깊게 감시해야 합니다. PR을 감시하고 검토하는 동안 내보낸 모든 함수를 살펴보고 타입 시그니처가 User와 같이 지나치게 광범위한 객체를 허용하거나 토큰 또는 신용 카드와 같은 props를 포함하는지 확인하세요. 전화 번호와 같이 개인정보에 민감한 필드도 추가 조사가 필요합니다. 클라이언트 컴포넌트는 작업을 수행하는 데 필요한 최소한의 데이터보다 더 많은 데이터를 허용해서는 안 됩니다.
import Profile from "./components/profile.tsx";
export asnyc function Page({ params: { slug } }) {
const [rows] = await sql`SELECT * FROM user WHERE slug = ${slug}`
const userData = rows[0]
// EXPOSED: 이렇게 하면 userData의 모든 필드가 클라이언트에 노출됩니다.
// 서버 컴포넌트에서 클라이언트로 데이터를 전달하기 때문입니다.
// 이는 `getServerSideProps`에서 `userData`를 반환하는 것과 유사합니다.
return <Profile user={userData} />;
}
"use client";
// BAD: 이 인터페이스는 클라이언트 컴포넌트가 필요로 하는 것보다 훨씬 많은 데이터를 받아들이고
// 서버 컴포넌트가 그 모든 데이터를 전달하도록 장려하기 때문에 나쁜 프롭 인터페이스입니다.
// 더 나은 해결책은 프로필 렌더링에 필요한 필드만 있는 제한된 객체를 받아들이는 것입니다.
export default async function Profile({ user }: { user: User }) {
return (
<div>
<h1>{user.name}</h1>
</div>
);
}
SQL Injection 공격을 피하려면 항상 매개변수화된 쿼리 또는 이를 대신 수행하는 DB 라이브러리를 사용하세요.
Server Only
서버에서만 실행되어야 하는 코드는 다음과 같이 표시할 수 있습니다.
import "server-only"
클라이언트 컴포넌트가 이 모듈을 임포트하려고 하면 빌드에 오류가 발생합니다. 이는 독점/민감 코드나 내부 비즈니스 로직이 실수로 클라이언트에 유출되지 않도록 하는 데 사용할 수 있습니다.
데이터를 전송하는 기본 방법은 클라이언트 컴포넌트에 프로퍼티를 전달할 때 자동으로 발생하는 React 서버 컴포넌트 프로토콜을 사용하는 것입니다. 이 직렬화는 JSON의 상위 집합을 지원합니다. 사용자 정의 클래스를 전송하는 것은 지원되지 않으며 오류가 발생합니다.
따라서 너무 큰 객체가 실수로 클라이언트에 노출되는 것을 방지하는 좋은 방법은 데이터 액세스 레코드에 클래스를 사용하는 것입니다.
곧 출시될 Next.js 14 릴리스에서는 next.config.js에서 taint 플래그를 활성화하여 실험적인 React Taint API를 사용해 볼 수도 있습니다.
module.exports = {
experimental: {
taint: true
}
}
이를 통해 클라이언트에 그대로 전달해서는 안 되는 객체를 표시할 수 있습니다.
// app/data.ts
import { experimental_taintObjectReference } from 'react';
export async function getUserData(id) {
const data = ...;
experimental_taintObjectReference( 'Do not pass user data to the client', data );
return data;
}
// app/page.tsx
import { getUserData } from './data';
export async function Page({ searchParams }) {
const userData = getUserData(searchParams.id);
return <ClientComponent user={userData} />; // error
}
이 경우 이 객체에서 데이터 필드를 추출하여 전달하면 오류를 해결할 수 있습니다.
// app/page.tsx
export async function Page({ searchParams }) {
const { name, phone } = getUserData(searchParams.id);
// 의도적인 개인 데이터 노출
return <ClientComponent name={name} phoneNumber={phone} />;
}
토큰과 같은 고유한 문자열의 경우, 원시 값도 taintUniqueValue를 사용하여 차단할 수 있습니다.
import {
experimental_taintObjectReference,
experimental_taintUniqueValue,
} from "react";
export async function getUserData(id) {
const data = ...;
experimental_taintObjectReference(
"Do not pass user data to the client",
data,
);
experimental_taintUniqueValue(
"Do not pass tokens to the client",
data,
data.token,
);
return data;
}
그러나 이 경우에도 파생된 값은 차단되지 않습니다.
데이터 액세스 계층을 사용하여 애초에 데이터가 서버 컴포넌트에 들어가지 않도록 하는 것이 좋습니다. Taint Checking은 값을 지정하여 실수에 대한 추가적인 보호 계층을 제공하지만, 함수와 클래스가 클라이언트 컴포넌트로 전달되는 것을 이미 차단하고 있다는 점을 염두에 두시기 바랍니다. 레이어가 많을수록 무언가 누락될 위험이 최소화됩니다.
기본적으로 환경 변수는 서버에서만 사용할 수 있습니다. 관례에 따라 Next.js는 접두사가 NEXT_PUBLIC_인 모든 환경 변수를 클라이언트에도 노출합니다. 이를 통해 클라이언트가 사용할 수 있어야 하는 특정 명시적 구성을 노출할 수 있습니다.
SSR vs RSC
초기 로드 시 Next.js는 서버에서 서버 컴포넌트와 클라이언트 컴포넌트를 모두 실행하여 HTML을 생성합니다.
서버 컴포넌트(RSC)는 클라이언트 컴포넌트와 별도의 모듈 시스템에서 실행되므로 두 모듈 간에 실수로 정보가 노출되는 것을 방지할 수 있습니다.
서버 측 렌더링(SSR)을 통해 렌더링하는 클라이언트 컴포넌트는 브라우저 클라이언트와 동일한 보안 정책을 고려해야 합니다. 권한이 있는 데이터나 비공개 API에 액세스해서는 안 됩니다. 이 보호 기능을 우회하기 위해 해킹을 사용하는 것(예: 글로벌 객체에 데이터를 숨기는 것)은 권장하지 않습니다. 원칙은 이 코드가 서버와 클라이언트에서 동일하게 실행될 수 있어야 한다는 것입니다. 기본 보안 관행에 따라 클라이언트 컴포넌트에서 서버 전용 모듈을 가져오는 경우 Next.js는 빌드에 실패합니다.
읽기
Next.js 앱 라우터에서 데이터베이스 또는 API에서 데이터를 읽는 것은 서버 컴포넌트 페이지를 렌더링하여 구현됩니다.
페이지에 대한 입력은 URL의 searchParams, URL에서 매핑된 동적 매개변수 및 헤더입니다. 이러한 매개변수는 클라이언트에 의해 다른 값으로 악용될 수 있습니다. 따라서 신뢰할 수 없으며 읽을 때마다 다시 확인해야 합니다. 예를 들어 ?isAdmin=true와 같은 것을 추적하는 데 searchParam을 사용해서는 안 됩니다. 사용자가 /[team]/
에 있다고 해서 해당 팀에 대한 액세스 권한이 있는 것은 아니므로 데이터를 읽을 때 이를 확인해야 합니다. 데이터를 읽을 때마다 항상 액세스 제어 및 cookies()
를 다시 읽는 것이 원칙입니다. props나 매개변수로 전달하지 마세요.
서버 컴포넌트를 렌더링할 때는 절대로 mutations
와 같은 부작용을 수행해서는 안 됩니다. 이것은 서버 컴포넌트에만 국한된 것이 아닙니다. React는 클라이언트 컴포넌트를 렌더링할 때에도 (useEffect 외부에서) 이중 렌더링과 같은 작업을 수행하여 부작용을 자연스럽게 방지합니다.
또한 Next.js에서는 렌더링 중에 쿠키를 설정하거나 캐시 재검증을 트리거할 수 있는 방법이 없습니다. 이는 또한 변형을 위한 렌더링을 사용하지 못하게 합니다.
예를 들어 변경 사항 저장이나 로그아웃과 같은 부수적인 작업을 수행하는 데 searchParams를 사용해서는 안 됩니다. 대신 서버 액션을 사용해야 합니다.
즉, Next.js 모델은 의도한 대로 사용될 때 부작용을 위해 GET 요청을 사용하지 않습니다. 이렇게 하면 대규모 CSRF 문제를 피할 수 있습니다.
Next.js는 GET에 쿠키를 설정할 수 있는 사용자 정의 경로 핸들러(route.tsx)를 지원합니다. 이는 일반적인 모델의 일부가 아닌 탈출구로 간주됩니다. 이 경우 명시적으로 GET 요청을 수락하도록 옵트인해야 합니다. 실수로 GET 요청을 수신할 수 있는 포괄적인 핸들러는 없습니다. 사용자 정의 GET 핸들러를 만들기로 결정한 경우 추가 감사가 필요할 수 있습니다.
쓰기
Next.js 앱 라우터에서 쓰기 또는 변경을 수행하는 관용적인 방법은 Server Action을 사용하는 것입니다.
"use server";
export function logout() {
cookies().delete("AUTH_TOKEN");
}
“use server” 어노테이션은 내보낸 모든 함수를 클라이언트에서 호출할 수 있게 하는 엔드포인트를 노출합니다. 식별자는 현재 소스 코드 위치의 해시입니다. 사용자가 액션의 ID에 대한 핸들을 얻는 한, 어떤 인수를 사용하여도 함수를 호출할 수 있습니다.
따라서 이러한 함수는 항상 현재 사용자가 이 액션을 호출할 수 있는지 확인하는 것으로 시작해야 합니다. 함수는 또한 각 인수의 무결성을 검증해야 합니다. 이 작업은 수동으로 또는 zod와 같은 도구를 사용하여 수행할 수 있습니다.
"use server";
export async function deletePost(id: number) {
if (typeof id !== 'number') {
// 타입스크립트 어노테이션은 강제 적용되지 않으므로
// ID가 우리가 생각하는 것과 같은지 확인해야 할 수도 있습니다.
throw new Error();
}
const user = await getCurrentUser();
if (!canDeletePost(user, id)) {
throw new Error();
}
...
}
Closures
서버 액션은 클로저로 인코딩할 수도 있습니다. 이렇게 하면 액션을 렌더링할 때 사용된 데이터의 스냅샷과 연결하여 액션이 호출될 때 이를 사용할 수 있습니다:
export default function Page() {
const publishVersion = await getLatestVersion();
async function publish() {
"use server";
if (publishVersion !== (await getLatestVersion())) {
throw new Error("The version has changed since pressing publish");
}
// ...
}
return <button action={publish}>Publish</button>;
}
클로저의 스냅샷은 서버가 호출될 때 클라이언트로 전송되고 다시 클라이언트로 전송되어야 합니다.
Next.js 14에서는 클로즈드 오버 변수가 클라이언트로 전송되기 전에 액션 ID로 암호화됩니다. 기본적으로 개인 키는 Next.js 프로젝트를 빌드하는 동안 자동으로 생성됩니다. 다시 빌드할 때마다 새 개인 키가 생성되므로 각 서버 액션은 특정 빌드에 대해서만 호출할 수 있습니다. 스큐 보호를 사용하여 재배포 중에 항상 수정 버전을 호출하도록 할 수 있습니다.
더 자주 회전하거나 여러 빌드에 걸쳐 지속되는 키가 필요한 경우 NEXT_SERVER_ACTIONS_ENCRYPTION_KEY 환경 변수를 사용하여 수동으로 구성할 수 있습니다.
모든 닫힌 변수를 암호화하면 실수로 변수에 있는 비밀이 노출되는 것을 방지할 수 있습니다. 서명을 하면 공격자가 액션에 대한 입력을 조작하기가 더 어려워집니다.
클로저를 사용하는 또 다른 대안은 자바스크립트에서 .bind(...)
함수를 사용하는 것입니다. 이 함수는 암호화되지 않습니다. 이는 성능에 대한 옵트아웃을 제공하며 클라이언트의 .bind()
와도 일관성이 있습니다.
async function deletePost(id: number) {
"use server";
// ID를 확인하고 삭제할 수 있는지 확인합니다.
...
}
export async function Page({ slug }) {
const post = await getPost(slug);
return <button action={deletePost.bind(null, post.id)}>
Delete
</button>;
}
Server Action(“use server”)에 대한 인수 목록은 항상 적대적인 것으로 취급되어야 하며 입력이 확인되어야 한다는 원칙이 있습니다.
CSRF
모든 서버 액션은 일반 <form>
으로 호출할 수 있으며, 이는 CSRF 공격에 노출될 수 있습니다. 이면에서 서버 액션은 항상 POST를 사용하여 구현되며 이 HTTP 메서드만 호출할 수 있습니다. 특히 동일 사이트 쿠키가 기본값으로 사용되기 때문에 이것만으로도 최신 브라우저에서 대부분의 CSRF 취약점을 방지할 수 있습니다.
추가 보호 기능으로 Next.js 14의 서버 액션은 Origin 헤더와 Host 헤더(또는 X-Forwarded-Host)도 비교합니다. 일치하지 않으면 액션이 거부됩니다. 즉, 서버 액션은 해당 액션을 호스팅하는 페이지와 동일한 호스트에서만 호출할 수 있습니다. Origin 헤더를 지원하지 않는 아주 오래되고 오래된 브라우저는 위험할 수 있습니다.
Server Actions는 CSRF 토큰을 사용하지 않으므로, HTML Sanitization이 매우 중요하다.
대신 사용자 지정 경로 핸들러(route.tsx)를 사용하는 경우 CSRF 보호를 수동으로 수행해야 하므로 추가 감사가 필요할 수 있습니다. 여기에는 기존 규칙이 적용됩니다.
Error Handling
버그가 발생합니다. 서버에서 오류가 발생하면 결국 클라이언트 코드에서 다시 발생하여 UI에서 처리됩니다. 오류 메시지와 스택 추적에는 민감한 정보가 포함될 수 있습니다. 예: [신용카드 번호]
는 유효한 전화번호가 아닙니다.
프로덕션 모드에서 React는 클라이언트에 오류나 거부된 프로미스를 전송하지 않습니다. 대신 오류를 나타내는 해시가 전송됩니다. 이 해시는 여러 개의 동일한 오류를 함께 연결하고 오류를 서버 로그와 연결하는 데 사용할 수 있습니다. React는 오류 메시지를 자체적인 일반 메시지로 대체합니다.
개발 모드에서는 디버깅을 돕기 위해 서버 오류가 여전히 일반 텍스트로 클라이언트에 전송됩니다.
프로덕션 워크로드의 경우 항상 프로덕션 모드에서 Next.js를 실행하는 것이 중요합니다. 개발 모드는 보안 및 성능에 최적화되지 않습니다.
사용자 정의 경로 및 미들웨어
사용자 정의 경로 핸들러 및 미들웨어는 다른 기본 제공 기능으로는 구현할 수 없는 기능을 위한 낮은 수준의 탈출구로 간주됩니다. 이는 또한 프레임워크가 보호하지 못하는 잠재적 위험 요소를 열어줍니다. 큰 힘에는 큰 책임이 따릅니다.
위에서 언급했듯이 route.tsx 경로는 사용자 정의 GET 및 POST 핸들러를 구현할 수 있으며, 올바르게 수행하지 않으면 CSRF 문제를 일으킬 수 있습니다.
미들웨어를 사용하여 특정 페이지에 대한 액세스를 제한할 수 있습니다. 일반적으로 거부 목록보다는 허용 목록을 사용하는 것이 가장 좋습니다. 재작성이나 클라이언트 요청 등 데이터에 액세스할 수 있는 다양한 방법을 모두 파악하는 것이 까다로울 수 있기 때문입니다.
예를 들어 HTML 페이지만 생각하는 것이 일반적입니다. Next.js는 RSC/JSON 페이로드를 로드할 수 있는 클라이언트 탐색 기능도 지원합니다. 페이지 라우터에서는 이 기능이 사용자 정의 URL에 있었습니다.
매처를 더 쉽게 작성할 수 있도록 Next.js 앱 라우터는 초기 HTML, 클라이언트 탐색 및 서버 액션 모두에 항상 페이지의 일반 URL을 사용합니다. 클라이언트 탐색은 ?_rsc=... 검색 매개변수를 캐시 브레이커로 사용합니다.
서버 액션은 사용되는 페이지에 상주하므로 동일한 액세스 제어를 상속합니다. 미들웨어에서 페이지 읽기를 허용하는 경우 해당 페이지에서 작업을 호출할 수도 있습니다. 페이지의 서버 액션에 대한 액세스를 제한하려면 해당 페이지에서 POST HTTP 메서드를 금지하면 됩니다.
Audit
Next.js 앱 라우터 프로젝트를 감사하는 경우 추가로 살펴볼 것을 권장하는 몇 가지 사항이 있습니다:
- 데이터 액세스 레이어. 격리된 데이터 액세스 레이어에 대한 확립된 관행이 있나요? 데이터베이스 패키지와 환경 변수가 데이터 액세스 레이어 외부에서 가져오지 않았는지 확인합니다.
- “use client” 파일. 컴포넌트 소품이 비공개 데이터를 예상하고 있나요? 유형 서명이 지나치게 광범위하진 않나요?
- “use server” 파일. 액션 인수가 액션에서 또는 데이터 액세스 레이어 내부에서 유효성이 검사되나요? 사용자가 액션 내에서 재인증되었는가?
/[param]/
.대괄호로 묶인 폴더는 사용자 입력입니다. 매개 변수의 유효성이 검사되나요?- middleware.tsx와 route.tsx에는 많은 권한이 있습니다. 기존 기법을 사용하여 이러한 부분을 감사하는 데 시간을 더 투자하세요. 침투 테스트 또는 취약점 스캔을 정기적으로 또는 팀의 소프트웨어 개발 라이프사이클에 맞춰 수행하세요.