-
Notifications
You must be signed in to change notification settings - Fork 3
긴급입니다. bff route handler 개발 #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,48 @@ | ||
| import type { NextConfig } from 'next'; | ||
|
|
||
| const nextConfig: NextConfig = { | ||
| /* config options here */ | ||
| async headers() { | ||
| return [ | ||
| { | ||
| source: '/(.*)', | ||
| headers: [ | ||
| // HSTS: HTTPS 강제 (배포 환경에서 유효) | ||
| { | ||
| key: 'Strict-Transport-Security', | ||
| value: 'max-age=63072000; includeSubDomains; preload', | ||
| }, | ||
| // CSP: 허용할 리소스 출처 제한 | ||
| { | ||
| key: 'Content-Security-Policy', | ||
| value: [ | ||
| "default-src 'self'", | ||
| "script-src 'self' 'unsafe-inline' 'unsafe-eval'", | ||
| "style-src 'self' 'unsafe-inline'", | ||
| "img-src 'self' blob: data: https:", | ||
| "font-src 'self'", | ||
| "connect-src 'self' https://fe-project-cowokers.vercel.app", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| "frame-ancestors 'none'", | ||
| ].join('; '), | ||
| }, | ||
| // 클릭재킹 방지 | ||
| { | ||
| key: 'X-Frame-Options', | ||
| value: 'DENY', | ||
| }, | ||
| // MIME 스니핑 방지 | ||
| { | ||
| key: 'X-Content-Type-Options', | ||
| value: 'nosniff', | ||
| }, | ||
| // 레퍼러 정보 제한 | ||
| { | ||
| key: 'Referrer-Policy', | ||
| value: 'strict-origin-when-cross-origin', | ||
| }, | ||
| ], | ||
| }, | ||
| ]; | ||
| }, | ||
| }; | ||
|
|
||
| export default nextConfig; | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { cookies } from 'next/headers'; | ||
|
|
||
| // 액세스 토큰: 짧은 만료 (1시간) | ||
| const ACCESS_TOKEN_MAX_AGE = 60 * 60; | ||
| // 리프레시 토큰: 긴 만료 (7일) | ||
| const REFRESH_TOKEN_MAX_AGE = 60 * 60 * 24 * 7; | ||
|
|
||
| export async function setAuthCookies(accessToken: string, refreshToken: string) { | ||
| const cookieStore = await cookies(); | ||
|
|
||
| cookieStore.set('accessToken', accessToken, { | ||
| httpOnly: true, | ||
| secure: process.env.NODE_ENV === 'production', | ||
| sameSite: 'lax', | ||
| path: '/', | ||
| maxAge: ACCESS_TOKEN_MAX_AGE, | ||
| }); | ||
|
|
||
| cookieStore.set('refreshToken', refreshToken, { | ||
| httpOnly: true, | ||
| secure: process.env.NODE_ENV === 'production', | ||
| sameSite: 'lax', | ||
| path: '/', | ||
| maxAge: REFRESH_TOKEN_MAX_AGE, | ||
| }); | ||
| } | ||
|
|
||
| export async function clearAuthCookies() { | ||
| const cookieStore = await cookies(); | ||
| cookieStore.delete('accessToken'); | ||
| cookieStore.delete('refreshToken'); | ||
| } | ||
|
Comment on lines
+1
to
+32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 쿠키 이름인 import { cookies } from 'next/headers';
export const ACCESS_TOKEN_NAME = 'accessToken';
export const REFRESH_TOKEN_NAME = 'refreshToken';
// 액세스 토큰: 짧은 만료 (1시간)
const ACCESS_TOKEN_MAX_AGE = 60 * 60;
// 리프레시 토큰: 긴 만료 (7일)
const REFRESH_TOKEN_MAX_AGE = 60 * 60 * 24 * 7;
export async function setAuthCookies(accessToken: string, refreshToken: string) {
const cookieStore = await cookies();
cookieStore.set(ACCESS_TOKEN_NAME, accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: ACCESS_TOKEN_MAX_AGE,
});
cookieStore.set(REFRESH_TOKEN_NAME, refreshToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: REFRESH_TOKEN_MAX_AGE,
});
}
export async function clearAuthCookies() {
const cookieStore = await cookies();
cookieStore.delete(ACCESS_TOKEN_NAME);
cookieStore.delete(REFRESH_TOKEN_NAME);
} |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Content-Security-Policy의script-src에'unsafe-inline'과'unsafe-eval'을 사용하는 것은 XSS(Cross-Site Scripting) 공격에 매우 취약해질 수 있어 보안상 위험합니다.'unsafe-inline': 인라인<script>태그나onclick같은 인라인 이벤트 핸들러를 허용합니다.'unsafe-eval':eval()함수 사용을 허용하여 임의의 문자열을 코드로 실행할 수 있게 합니다.Next.js App Router 환경에서는 대부분 이 옵션들 없이 구현이 가능합니다. 만약 특정 라이브러리 때문에 반드시 필요하다면, 해당 내용을 문서화하고 점진적으로 제거할 계획을 세우는 것이 좋습니다.
style-src의'unsafe-inline'도 가능하면 제거하는 것을 권장합니다.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AI 리뷰가 정확하지만 현실적인 문제가 있습니다.
Next.js App Router는 내부적으로 인라인 스크립트를 사용하고, Storybook이나 외부 라이브러리들이 unsafe-eval을 요구하는 경우가 있어서 무턱대고 지우면 앱이 깨질 수 있어. AI가 제안한 대로 'self'만 남기면 로컬에서 바로 에러 날 가능성이 높습니다. 지금 수준으로 두되 "unsafe-inline은 Next.js 내부 동작 때문에 임시 허용한 상태이고, nonce 기반으로 개선할 수 있다는 점은 알고 있다면 되는 내용입니다.