-
Notifications
You must be signed in to change notification settings - Fork 1
[BE] Oauth CORS
이승관 edited this page Dec 2, 2024
·
1 revision
-
목적:
- 요청의 출처(origin)를 명시적으로 나타냅니다.
- 주로 CORS(교차 출처 리소스 공유) 요청에서 사용됩니다.
-
포함하는 정보:
- 출처의 스키마(프로토콜), 호스트(도메인), 포트만 포함합니다.
- 예:
https://example.com:443
-
어디서 사용?
- POST, PUT, DELETE 같은 상태를 변경하는 요청이나 CORS와 관련된 모든 요청.
- Referer보다 제한된 정보를 포함하므로 민감한 정보를 노출하지 않음.
-
특징:
- 요청의 정확한 출처만 표시되며, 경로(path) 정보는 포함되지 않음.
- 브라우저에서 설정된 정책에 따라 항상 포함됩니다.
-
예시:
Origin: https://example.com
-
목적:
- 요청이 어디에서 왔는지를 명시합니다.
- 사용자가 현재 요청으로 넘어오기 전에 방문한 직전 페이지의 URL을 제공합니다.
-
포함하는 정보:
- 프로토콜, 호스트, 포트뿐 아니라 **전체 URL(경로, 쿼리 문자열 포함)**을 포함할 수 있음.
- 예:
https://example.com/some/path?query=123
-
어디서 사용?
- 링크 클릭, 리소스 로드, 폼 제출 등 다양한 경우.
- 주로 로그 분석, 트래픽 소스 추적, 보안 검사에 사용.
-
특징:
- 더 많은 정보를 담고 있지만, 개인정보나 민감한 데이터가 노출될 위험이 있음.
- 브라우저 정책(예:
strict-origin-when-cross-origin
)에 따라 수정되거나 생략될 수 있음.
-
예시:
Referer: https://[example.com](http://example.com/)/some/page
- 다른 출처(Origin)의 리소스를 공유하는 방식을 제어하는 보안 메커니즘
- 웹 브라우저가 실행하는 보안 정책의 하나
출처(Origin)의 구성 요소:
- 프로토콜 (http://, https://)
- 호스트 (example.com)
- 포트 번호 (:443, :80)
- 보안상의 이유:
- 악의적인 사이트가 사용자의 데이터에 접근하는 것을 방지
- XSS, CSRF 등의 공격 차단
- Same-Origin Policy:
- 기본적으로 브라우저는 같은 출처의 리소스만 공유 허용
- 다른 출처의 리소스 접근을 제한
// 1. 다른 도메인
fetch('https://api.example.com/data') // 현재: www.mysite.com
// ❌ 도메인이 다름
// 2. 다른 포트
fetch('https://mysite.com:3000/data') // 현재: mysite.com:8080
// ❌ 포트가 다름
// 3. 다른 프로토콜
fetch('http://mysite.com/data') // 현재: https://mysite.com
// ❌ 프로토콜이 다름
- 단순 요청 (Simple Request): 브라우저 → 서버 (직접 요청) 조건: GET, HEAD, POST 메서드만 사용 일반적인 헤더만 사용 Content-Type이 제한적
- 프리플라이트 요청 (Preflight Request): 브라우저 → 서버 (OPTIONS 요청으로 허용 여부 확인) 브라우저 ← 서버 (CORS 헤더로 응답) 브라우저 → 서버 (실제 요청 전송)
- 서버 측 CORS 설정
- 적절한 CORS 헤더 설정
- 허용할 출처 명시
- 프록시 사용
- 개발 환경에서 프록시 서버 설정
- Nginx 등의 리버스 프록시 활용
- 환경별 설정
- 개발/운영 환경에 따른 CORS 설정 분리
- 보안과 편의성의 균형
- 와일드카드(*) 사용 자제
- 특정 도메인만 허용하는 것이 안전
- credentials 처리
- 인증이 필요한 요청은 신중하게 설정
- 필요한 메서드/헤더만 허용
- 최소 권한 원칙 적용
마트 보안 시스템에 비유:
- 마트(서버)에 들어오는 손님(요청)을 체크
- 특정 신분증(Origin)을 가진 사람만 입장 허용
- 사전 확인(Preflight)으로 출입 가능 여부 체크
- 허가된 행동(Methods)만 수행 가능
-
client에서 API요청으로 Oauth를 구현할 경우 CORS오류가 왜 발생할까??
처음에 생각했던 문제점은 Nginx때문인줄 알았습니다.
Nginx를 사용하기 때문에 client가 어떤 곳에서 요청을 보내는지 알 수 없기 때문에 항상 corinee.site로 시작하는 리다이렉트 응답을 보낼 수 밖에 없고 그것때문에 로컬에서는 CORS오류가 발생했습니다.
또한
-
브라우저의 Same-Origin Policy
// Same-Origin Policy란? - 브라우저의 기본 보안 정책 - 다른 출처(origin)로의 요청을 제한 // 예시 // 현재 페이지: http://localhost:5173 const response = await fetch('/api/auth/google'); // 1. 서버가 https://accounts.google.com으로 리다이렉트 시도 // 2. 다른 도메인으로의 리다이렉트는 CORS 정책 위반 // 3. 브라우저가 요청 차단
-
OAuth 리다이렉션 응답을 AJAX로 처리할 수 없음
// AJAX 요청의 한계 const handleGoogleLogin = async () => { try { const response = await axios.get('/api/auth/google'); // 문제점: // 1. AJAX는 JavaScript 내에서 실행되는 비동기 요청 // 2. 브라우저 주소창을 변경할 수 없음 // 3. Google 로그인 페이지로 실제 이동이 불가능 } catch (error) { console.error(error); } } // 리다이렉션이 필요한 이유 - Google OAuth는 실제 Google 로그인 페이지로 이동 필요 - 사용자가 직접 Google 페이지에서 로그인해야 함 - AJAX로는 이러한 페이지 전환을 구현할 수 없음
-
서버의 리다이렉트 응답이 AJAX 요청의 예상된 응답 형식과 맞지 않음
// AJAX가 기대하는 응답 형식 { data: { ... }, status: 200, headers: { ... } } // OAuth 리다이렉션 응답 Status Code: 302 Found Location: https://accounts.google.com/o/oauth2/v2/auth?... // 문제점: 1. AJAX는 JSON이나 데이터 응답을 기대 2. 리다이렉션은 302 상태코드와 Location 헤더 사용 3. 브라우저가 이 불일치를 CORS 오류로 처리
-
실제 동작 비교
// ❌ API 방식 (작동 안 함) const loginWithAPI = async () => { try { // 1. AJAX 요청 발생 const response = await axios.get('/api/auth/google'); // 2. 서버가 302 리다이렉트 응답 // 3. CORS 오류 발생 // 4. Google 페이지로 이동 불가 } catch (error) { console.error('CORS 오류 발생'); } } // ✅ 리다이렉션 방식 (정상 작동) const loginWithRedirect = () => { // 1. 브라우저 주소 변경 window.location.href = '/api/auth/google'; // 2. 서버가 302 리다이렉트 응답 // 3. 브라우저가 Google 페이지로 자동 이동 // 4. 정상적인 OAuth 흐름 시작 }
-
-
현재 Oauth의 작동 순서가 어떻게 되는 것일까??
처음에는 이런 순서로 진행이 되는 줄 알았으나
- 클라이언트 -> 서버 (/api/auth/google)
- 서버 -> Google 인증 서버 (리다이렉트)
- client_id, redirect_uri, scope 등 전달
- 사용자가 Google 로그인 수행
- Google -> 서버 (/api/auth/google/callback)
- authorization code 전달
- 서버 -> Google
- authorization code로 액세스 토큰 요청
- Google -> 서버
- 액세스 토큰, 사용자 정보 전달
- 서버 -> 클라이언트
- JWT 토큰 생성 후 리다이렉트
이런 순서로 진행이 되고 있었습니다.
주소 요청 방식을 사용하여 Google 로그인 페이지를 보여주고 성공한다면 /auth/google/callback 콜백을 통하여 토큰을 전달받는 방식이였습니다.
- API방식을 사용할 수 없는 이유
- 기술적 제약:
- AJAX 요청은 페이지 리다이렉션을 할 수 없음
- Google OAuth는 실제 페이지 전환을 요구
- 팝업이나 iframe 사용시 보안 정책 위반
- 보안 문제:
- 인증 과정이 노출될 수 있음
- CSRF 공격에 취약
- 토큰 탈취 위험
- OAuth 2.0 스펙 위반:
- 표준 인증 흐름을 따르지 않음
- Google의 요구사항 미충족
- 인증 제공자의 정책 위반
- 리다이렉션 방식을 사용하는 이유
- 보안:
- 신뢰할 수 있는 Google 페이지에서 직접 인증
- 인증 정보의 안전한 전달
- state 파라미터를 통한 CSRF 방지
- 사용자 경험:
- 익숙한 Google 로그인 화면
- 투명한 인증 과정
- 2단계 인증 등 Google의 보안 기능 활용
- 유지보수:
- 표준 스펙 준수로 예측 가능한 동작
- 문제 발생시 디버깅 용이
- OAuth 제공자의 업데이트 대응 쉬움
-
client에서 로컬과 배포 환경 모두에서 동일하게 사용할 수 있는 방법을 적용할 수 없을까??
처음 /auth/google에서 쿼리를 통해 받는 방식을 선택한다면 로컬에서도 사용할 수 있습니다.
- [FE] TailwindCSS @apply
- [FE] 캐러셀 구현
- [FE] 사이드 바 상태관리 도전기
- [FE] axios interceptor로 로그인 필요한 api 개선하기
- [FE] Tanstack Query API 최적화 도전기
- [FE] Tanstack Query로 구현하는 무한 스크롤 차트 도전기
- [FE] 차트 무한 스크롤링 최적화 도전기
- [FE] 차트 실시간 등락 구현 도전기
- [FE] 검색 구현 및 검색 API 호출 최적화 도전기
- [FE] 고차 컴포넌트를 활용한 인증 접근 제어
- [FE] 코드 스플릿팅으로 최적화 도전기
- [BE] Server 생성
- [BE] CI/CD
- [BE] GitAction 학습 정리
- [BE] ssh터널링으로 db연결
- [BE] 배포환경에서 DB 연결 및 테스트 완료
- [BE] https 적용
- [BE] upbit api 연결 및 SSE api
- [BE] SSE 구현
- [BE] SSE 에러
- [BE] redis 설치 및 연동
- [BE] 트랜잭션 락 구현과 최적화
- [BE] Oauth CORS
- [BE] QueryRunner 사용 시 발생한 문제점과 해결방안
- [BE] Git Action 학습 정리
- [BE] NestJS 학습 정리
- [BE] 로그인 기능 및 리프레시토큰
- [BE] 비회원 체험 기능
- [BE] Nginx 학습 정리
- [BE] Mixed Content와 HTTPS 보안 구현하기
- [BE] 매수/매도 로직 구현 및 개선 과정
- [BE] Queue, Load Balancing, Redis