Django 설정에서 JWT_AUTH_COOKIE와 JWT_AUTH_REFRESH_COOKIE의 중복 설정이 있었음.
Django REST_AUTH 설정과 Simple JWT 관련 설정이 충돌하여 토큰 발급 문제 발생.
REST_AUTH와 SIMPLE_JWT 설정 중 JWT_AUTH_COOKIE와 JWT_AUTH_REFRESH_COOKIE의 중복 설정을 제거.
# 중복 제거:
JWT_AUTH_COOKIE = "my-app-auth"
JWT_AUTH_REFRESH_COOKIE = "my-refresh-token"
문제 요약: React에서 Django로 JWT 인증을 적용할 때, 쿠키를 통한 인증이 제대로 이루어지지 않음. 특히, withCredentials 옵션을 사용해도 JWT 토큰이 요청에 포함되지 않음.
오류 메시지: 인증 오류(401 Unauthorized)가 발생함.
React에서 JWT 쿠키 전송 여부: withCredentials가 설정되어 있음에도 쿠키가 전송되지 않음.
Django 설정: Django에서 JWT 쿠키 설정 확인 및 CSRF 관련 설정을 점검.
JWTAuthentication와 JWTCookieAuthentication의 차이를 확인하고, JWTAuthentication 방식으로 접근하여 수동으로 Authorization 헤더에 JWT 토큰을 포함.
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
"DEFAULT_AUTHENTICATION_CLASSES": [
# JWT 토큰 기반 인증
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
}
# SIMPLE JWT 설정
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
"AUTH_HEADER_TYPES": ("Bearer",),
# 헤더에 Bearer 타입으로 JWT 토큰을 포함(중요)
}
# CORS 설정 (React에서 Django로 요청할 수 있도록 설정)
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000", # 개발환경에서 React 앱의 주소
"https://poko-dev.com", # 배포 시 도메인 주소
]
CORS_ALLOW_CREDENTIALS = True # 쿠키 자격 증명 허용
# 개발 환경에서의 CSRF 및 쿠키 설정 (로컬 환경)
DEBUG = True
CSRF_COOKIE_SECURE = False # HTTPS가 아닌 HTTP로도 작동하도록 설정
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SAMESITE = "None"
SESSION_COOKIE_SAMESITE = "None"
# 개발 환경에서의 CORS 및 CSRF 설정
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000", # 개발 중인 React 앱의 주소
]
CSRF_TRUSTED_ORIGINS = [
"http://localhost:3000", # React 앱에서의 요청 허용
]
CORS_ALLOW_CREDENTIALS = True # 자격 증명 허용 (withCredentials)
# 배포 환경에서의 CSRF 및 쿠키 설정
DEBUG = False
CSRF_COOKIE_SECURE = True # HTTPS만 허용
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = "Lax" # CSRF 공격을 방지하기 위해 Lax 사용
SESSION_COOKIE_SAMESITE = "Lax"
# 배포 환경에서의 CORS 및 CSRF 설정
CORS_ALLOWED_ORIGINS = [
"https://poko-dev.com", # 실제 배포된 React 앱의 주소
"https://www.poko-dev.com", # www 도메인 포함
]
CSRF_TRUSTED_ORIGINS = [
"https://poko-dev.com", # 배포된 도메인에서의 요청 허용
"https://www.poko-dev.com",
]
CORS_ALLOW_CREDENTIALS = True # 자격 증명 허용 (withCredentials)
import os
# DEBUG 여부에 따라 환경을 설정
DEBUG = os.getenv('DJANGO_DEBUG', 'False') == 'True'
if DEBUG:
# 개발 환경 설정
CSRF_COOKIE_SECURE = False
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SAMESITE = "None"
SESSION_COOKIE_SAMESITE = "None"
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
]
CSRF_TRUSTED_ORIGINS = [
"http://localhost:3000",
]
else:
# 배포 환경 설정
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_SAMESITE = "Lax"
CORS_ALLOWED_ORIGINS = [
"https://poko-dev.com",
"https://www.poko-dev.com",
]
CSRF_TRUSTED_ORIGINS = [
"https://poko-dev.com",
"https://www.poko-dev.com",
]
CORS_ALLOW_CREDENTIALS = True
문제 요약: 로컬 개발 환경에서는 access token과 refresh token이 요청에 포함되지 않아 인증이 실패.
오류 메시지: 인증 오류 (401 Unauthorized)가 발생하거나, 요청에 토큰이 포함되지 않음.
localhost는 도메인 이름이고, 127.0.0.1은 IP 주소. 브라우저는 이 두 가지를 서로 다른 출처로 보기 때문에, 이 둘 간의 요청은 CORS 정책에 의해 제한될 수 있다.
Django API와 React의 도메인/프로토콜 일치 여부를 확인.(매우 중요)
const isProd = process.env.NODE_ENV === "production"; // 배포 환경 감지
const API_URL = isProd ? 'https://www.poko-dev.com/api/accounts/' : 'http://localhost:8000/api/accounts/';
python manage.py runserver localhost:8000
CSRF_COOKIE_DOMAIN = None # 로컬 개발 환경에서는 None으로 설정
SESSION_COOKIE_DOMAIN = None
CSRF_COOKIE_SAMESITE = "None"
SESSION_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SECURE = False
CSRF_COOKIE_HTTPONLY = False
CORS_ALLOW_CREDENTIALS = True
CSRF Token & Django: You explored issues with Django’s CSRF token generation, discussing the need for a CSRF token for secure authentication in React-Django projects. There were difficulties with CSRF cookie generation and validation during login.
React-Django API: Questions focused on setting up a POST request in React to handle attendance data in Django, including serializer issues and correctly formatting data.
CSRF Middleware: You tested custom CSRF middleware to confirm its proper execution and observed issues with token handling in local development.
JWT와 CSRF
JWT는 서명된 토큰을 사용해 요청이 변조되지 않았음을 검증할 수 있다. 또한 서버가 JWT를 서명할 때 비밀키를 사용하므로, 이 토큰을 가지고 있는 클라이언트는 서버가 서명한 요청만을 보낼 수 있다.
CSRF 보호는 주로 세션 기반 인증에서 중요하다. 세션 기반 인증은 사용자의 세션 ID가 쿠키에 저장되기 때문에, CSRF 공격이 발생할 경우 쿠키가 자동으로 전송되어 공격자에게 세션을 탈취당할 위험다. 하지만 JWT 기반 인증에서는 세션이 없으므로 CSRF 공격의 위험이 훨씬 적다.
CSRF_COOKIE_HTTPONLY = True의 의미
이 설정은 JavaScript가 CSRF 토큰에 접근할 수 없도록 설정. HttpOnly 속성은 보안을 강화하기 위해 사용되며, 토큰이 클라이언트 측 JavaScript 코드에서 읽히지 않도록 방지한다. 하지만, React에서 X-CSRFToken 헤더를 추가해야 하는 상황이라면 이 설정은 문제된다.
JWT와 CSRF 보호:
JWT를 쿠키로 전송할 때도 CSRF 공격에 노출될 수 있지만, JWT는 서명되어 있고 변조가 불가능하기 때문에 CSRF 보호가 강제되지 않아도 된다.
만약 CSRF 보호를 비활성화한다면 JWT만으로도 충분한 보안을 확보할 수 있다. 즉, JWT 기반 인증을 사용하는 경우에는 CSRF 검증이 필요하지 않다.
JWTCookieAuthentication, CSRF, Session이 서로 상호보완적인 관계인지 확인하지 못해서 문제 해결 시간이 늘어났다.