Next.js 기본 설정만으로는 보안이 부족하다
Vercel에 Next.js를 배포하면 HTTPS는 자동으로 된다. 거기서 만족하고 끝내는 경우가 많다. 하지만 보안 헤더, 환경변수 관리, API 인증 — 이 세 가지는 직접 챙겨야 한다.
next.config.js 보안 헤더 설정
// next.config.js
const securityHeaders = [
{
key: 'X-DNS-Prefetch-Control',
value: 'on'
},
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload'
},
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin'
},
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()'
},
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
}
];
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: securityHeaders,
},
];
},
};
환경변수 NEXT_PUBLIC 분리
Next.js에서 NEXT_PUBLIC_ 접두사가 붙은 환경변수는 클라이언트 번들에 포함된다. 즉, 누구나 소스코드에서 볼 수 있다.
# .env.local
# 클라이언트에 노출됨 (anon key, 공개 URL만 여기에)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
# 서버에서만 사용 (절대 NEXT_PUBLIC_ 붙이지 않기)
SUPABASE_SERVICE_ROLE_KEY=eyJ...
OPENAI_API_KEY=sk-...
DATABASE_URL=postgresql://...
API Route 인증 처리
// pages/api/user/profile.js
import { getServerSession } from "next-auth/next";
import { authOptions } from "./auth/[...nextauth]";
export default async function handler(req, res) {
// 반드시 세션 확인 먼저
const session = await getServerSession(req, res, authOptions);
if (!session) {
return res.status(401).json({ error: 'Unauthorized' });
}
// 인증된 사용자만 아래 코드 실행
const userId = session.user.id;
// ... 로직
}
Middleware로 전역 인증 처리
// middleware.js
import { withAuth } from "next-auth/middleware";
export default withAuth({
callbacks: {
authorized: ({ token }) => !!token,
},
});
export const config = {
matcher: ['/dashboard/:path*', '/api/admin/:path*'],
};
설정 완료 후 배포하고 나서 CodeScan으로 헤더가 제대로 적용됐는지 확인하는 걸 루틴으로 만들어두면 좋다.